first viable version
This commit is contained in:
parent
20c85dc6fc
commit
c9cc2c2cb6
|
|
@ -1,8 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
import {computed, onMounted, reactive, ref} from "vue";
|
import {computed, onMounted} from "vue";
|
||||||
import * as _ from 'underscore'
|
import * as _ from 'underscore'
|
||||||
import {useCircleStore} from "@/stores/circle";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
circleStore: {},
|
circleStore: {},
|
||||||
|
|
@ -21,15 +20,15 @@ const props = defineProps<{
|
||||||
|
|
||||||
|
|
||||||
function someFinished(learningSequence) {
|
function someFinished(learningSequence) {
|
||||||
return props.circleStore.flatChildren.filter((lc) => {
|
return props.circleStore.flatChildren.filter((lc) => {
|
||||||
return lc.completed && lc.parentLearningSequence?.translation_key === learningSequence.translation_key;
|
return lc.completed && lc.parentLearningSequence?.translation_key === learningSequence.translation_key;
|
||||||
}).length > 0;
|
}).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const pieData = computed(() => {
|
const pieData = computed(() => {
|
||||||
const circle = props.circleStore.circleData, completionData = props.circleStore.completionData
|
const circle = props.circleStore.circleData, completionData = props.circleStore.completionData
|
||||||
console.log('initial of compute pie data ', circle, completionData)
|
console.log('initial of compute pie data ', circle, completionData)
|
||||||
|
|
||||||
if (circle && completionData) {
|
if (circle && completionData) {
|
||||||
console.log('initial of compute pie data ', circle, completionData)
|
console.log('initial of compute pie data ', circle, completionData)
|
||||||
|
|
@ -38,7 +37,6 @@ const pieData = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const completionDataByPageId = _.object(_.map(completionData, function (item) {
|
const completionDataByPageId = _.object(_.map(completionData, function (item) {
|
||||||
console.log(item)
|
|
||||||
return [item.page_key, item]
|
return [item.page_key, item]
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
@ -53,7 +51,6 @@ const pieData = computed(() => {
|
||||||
pie.endAngle = pie.endAngle + Math.PI
|
pie.endAngle = pie.endAngle + Math.PI
|
||||||
pie.arrowStartAngle = pie.endAngle + (pie.startAngle - pie.endAngle) / 2
|
pie.arrowStartAngle = pie.endAngle + (pie.startAngle - pie.endAngle) / 2
|
||||||
pie.arrowEndAngle = pie.startAngle + (pie.startAngle - pie.endAngle) / 2
|
pie.arrowEndAngle = pie.startAngle + (pie.startAngle - pie.endAngle) / 2
|
||||||
console.log(someFinished(thisLearningSequence))
|
|
||||||
pie.done = someFinished(thisLearningSequence)
|
pie.done = someFinished(thisLearningSequence)
|
||||||
})
|
})
|
||||||
angles = angles.reverse()
|
angles = angles.reverse()
|
||||||
|
|
@ -64,169 +61,158 @@ const pieData = computed(() => {
|
||||||
|
|
||||||
|
|
||||||
const blue900 = '#00224D',
|
const blue900 = '#00224D',
|
||||||
blue700 = '#1A5197',
|
blue700 = '#1A5197',
|
||||||
gray100 = '#EDF2F6',
|
gray100 = '#EDF2F6',
|
||||||
gray300 = '#E0E5EC',
|
gray300 = '#E0E5EC',
|
||||||
gray500 = '#B1C1CA',
|
gray500 = '#B1C1CA',
|
||||||
sky400 = '#72CAFF',
|
sky400 = '#72CAFF',
|
||||||
sky500 = '#41B5FA'
|
sky500 = '#41B5FA'
|
||||||
|
|
||||||
|
|
||||||
const svg = computed(() => {
|
const render = computed(() => {
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
const render = computed( () => {
|
|
||||||
const width = 450, //props.width,
|
const width = 450, //props.width,
|
||||||
height = 450, //props.height,
|
height = 450, //props.height,
|
||||||
radius: number = Math.min(width, height) / 2.4,
|
radius: number = Math.min(width, height) / 2.4,
|
||||||
arrowStrokeWidth = 2
|
arrowStrokeWidth = 2
|
||||||
|
|
||||||
|
|
||||||
|
const svg = d3.select('.circle-visualization')
|
||||||
|
.attr('width', width)
|
||||||
|
.attr('height', height)
|
||||||
|
|
||||||
|
// Append markter as definition to the svg
|
||||||
|
svg.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', gray500)
|
||||||
|
|
||||||
|
const g = svg.append('g').attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
|
||||||
|
|
||||||
|
|
||||||
const svg = d3.select('.circle-visualization')
|
// Generate the pie diagram wede
|
||||||
.attr('width', width)
|
const wedgeGenerator = d3
|
||||||
.attr('height', height)
|
.arc()
|
||||||
|
.innerRadius(radius / 2.5)
|
||||||
// Append markter as definition to the svg
|
.padAngle(12 / 360)
|
||||||
svg.append("svg:defs").append("svg:marker")
|
.outerRadius(radius)
|
||||||
.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', gray500)
|
|
||||||
|
|
||||||
const g = svg.append('g').attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
|
|
||||||
|
|
||||||
|
|
||||||
|
// Generate the arrows
|
||||||
|
const arrowRadius = radius * 1.1
|
||||||
|
|
||||||
|
|
||||||
// Generate the pie diagram wede
|
const learningSequences = g.selectAll('.learningSegmentArc').data(pieData.value).enter().append('g')
|
||||||
const wedgeGenerator = d3
|
.attr('class', 'learningSegmentArc')
|
||||||
.arc()
|
.attr('role', 'button')
|
||||||
.innerRadius(radius / 2.5)
|
.attr('fill', gray300)
|
||||||
.padAngle(12 / 360)
|
|
||||||
.outerRadius(radius)
|
|
||||||
|
|
||||||
|
|
||||||
// Generate the arrows
|
learningSequences
|
||||||
const arrowRadius = radius * 1.1
|
.on('mouseover', function (d, i) {
|
||||||
|
d3.select(this)
|
||||||
|
.transition()
|
||||||
|
.duration('200')
|
||||||
|
.attr('fill', (d) => {
|
||||||
|
return d.done ? sky400 : gray100
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
.on('mouseout', function (d, i) {
|
||||||
|
d3.select(this)
|
||||||
|
.transition()
|
||||||
|
.duration('200')
|
||||||
|
.attr('fill', (d) => {
|
||||||
|
return d.done ? sky500 : gray300
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
learningSequences
|
||||||
|
.transition()
|
||||||
|
.duration('1')
|
||||||
|
.attr('fill', (d) => {
|
||||||
|
return d.done ? sky500 : gray300
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
const learningSequences = g.selectAll('.learningSegmentArc').data(pieData.value).enter().append('g')
|
learningSequences.append('path').attr('d', wedgeGenerator)
|
||||||
.attr('class', 'learningSegmentArc')
|
|
||||||
.attr('role', 'button')
|
|
||||||
.attr('fill', gray300)
|
|
||||||
|
|
||||||
|
|
||||||
learningSequences
|
const learningSequenceText = learningSequences
|
||||||
.on('mouseover', function (d, i) {
|
.append('text')
|
||||||
d3.select(this)
|
.attr('fill', blue900)
|
||||||
.transition()
|
.style('font-size', 15)
|
||||||
.duration('200')
|
.text((d) => {
|
||||||
.attr('fill', (d) => {
|
return d.title
|
||||||
return d.done ? sky400 : gray100
|
})
|
||||||
})
|
.attr("transform", function (d) {
|
||||||
|
let translate = wedgeGenerator.centroid(d)
|
||||||
|
translate = [translate[0], translate[1] + 20]
|
||||||
|
return "translate(" + translate + ")";
|
||||||
|
})
|
||||||
|
.attr('class', 'circlesText text-xl font-bold')
|
||||||
|
.style('text-anchor', 'middle')
|
||||||
|
|
||||||
})
|
const iconWidth = 25
|
||||||
.on('mouseout', function (d, i) {
|
|
||||||
d3.select(this)
|
|
||||||
.transition()
|
|
||||||
.duration('200')
|
|
||||||
.attr('fill', (d) => {
|
|
||||||
return d.done ? sky500 : gray300
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
learningSequences
|
const learningSequenceIcon = learningSequences.append("svg:image")
|
||||||
.transition()
|
.attr("xlink:href", (d) => {
|
||||||
.duration('1000')
|
return "/static/icons/" + d.icon.replace("it-", "") + ".svg"
|
||||||
.attr('fill', (d) => {
|
})
|
||||||
return d.done ? sky500 : gray300
|
.attr("width", iconWidth)
|
||||||
})
|
.attr("height", iconWidth)
|
||||||
|
.attr("transform", function (d) {
|
||||||
|
let translate = wedgeGenerator.centroid(d)
|
||||||
|
translate = [translate[0] - iconWidth / 2, translate[1] - iconWidth]
|
||||||
|
return "translate(" + translate + ")";
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
learningSequences.append('path').attr('d', wedgeGenerator)
|
// Create Arrows
|
||||||
|
const arrow = d3
|
||||||
|
.arc()
|
||||||
|
.innerRadius(arrowRadius)
|
||||||
|
.outerRadius(arrowRadius + arrowStrokeWidth)
|
||||||
|
.padAngle(20 / 360)
|
||||||
|
.startAngle(d => {
|
||||||
|
return d.arrowStartAngle
|
||||||
|
})
|
||||||
|
.endAngle(d => {
|
||||||
|
return d.arrowEndAngle
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
const learningSequenceText = learningSequences
|
const arrows = g
|
||||||
.append('text')
|
.selectAll('.arrow')
|
||||||
.attr('fill', blue900)
|
.data(pieData.value)
|
||||||
.style('font-size', 15)
|
.join('g')
|
||||||
.text((d) => {
|
.attr('class', 'arrow')
|
||||||
return d.title
|
.attr('marker-end', 'url(#triangle)')
|
||||||
})
|
|
||||||
.attr("transform", function (d) {
|
|
||||||
let translate = wedgeGenerator.centroid(d)
|
|
||||||
translate = [translate[0], translate[1] + 20]
|
|
||||||
return "translate(" + translate + ")";
|
|
||||||
})
|
|
||||||
.attr('class', 'circlesText text-xl font-bold')
|
|
||||||
.style('text-anchor', 'middle')
|
|
||||||
|
|
||||||
const iconWidth = 25
|
// remove last arrow
|
||||||
|
d3.selection.prototype.last = function () {
|
||||||
const learningSequenceIcon = learningSequences.append("svg:image")
|
let last = this.size() - 1;
|
||||||
.attr("xlink:href", (d) => {
|
return d3.select(this.nodes()[last]);
|
||||||
return "/static/icons/" + d.icon.replace("it-", "") + ".svg"
|
};
|
||||||
})
|
|
||||||
.attr("width", iconWidth)
|
|
||||||
.attr("height", iconWidth)
|
|
||||||
.attr("transform", function (d) {
|
|
||||||
let translate = wedgeGenerator.centroid(d)
|
|
||||||
translate = [translate[0] - iconWidth / 2, translate[1] - iconWidth]
|
|
||||||
return "translate(" + translate + ")";
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
// Create Arrows
|
const all_arows = g.selectAll('.arrow')
|
||||||
const arrow = d3
|
all_arows.last().remove()
|
||||||
.arc()
|
|
||||||
.innerRadius(arrowRadius)
|
|
||||||
.outerRadius(arrowRadius + arrowStrokeWidth)
|
|
||||||
.padAngle(20 / 360)
|
|
||||||
.startAngle(d => {
|
|
||||||
return d.arrowStartAngle
|
|
||||||
})
|
|
||||||
.endAngle(d => {
|
|
||||||
return d.arrowEndAngle
|
|
||||||
})
|
|
||||||
|
|
||||||
|
//Draw arrow paths
|
||||||
const arrows = g
|
arrows.append('path').attr('fill', gray500).attr('d', arrow)
|
||||||
.selectAll('.arrow')
|
|
||||||
.data(pieData.value)
|
|
||||||
.join('g')
|
|
||||||
.attr('class', 'arrow')
|
|
||||||
.attr('marker-end', 'url(#triangle)')
|
|
||||||
|
|
||||||
// remove last arrow
|
|
||||||
d3.selection.prototype.last = function () {
|
|
||||||
let last = this.size() - 1;
|
|
||||||
return d3.select(this.nodes()[last]);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const all_arows = g.selectAll('.arrow')
|
|
||||||
all_arows.last().remove()
|
|
||||||
|
|
||||||
//Draw arrow paths
|
|
||||||
arrows.append('path').attr('fill', gray500).attr('d', arrow)
|
|
||||||
return svg
|
return svg
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
function viewBox() {
|
function viewBox() {
|
||||||
|
|
@ -239,10 +225,12 @@ function viewBox() {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="svg-container h-full content-center">
|
<div class="svg-container h-full content-center">
|
||||||
<pre hidden>{{pieData}}</pre>
|
<pre hidden>{{ pieData }}</pre>
|
||||||
<pre hidden> {{render}}</pre>
|
<pre hidden> {{ render }}</pre>
|
||||||
<pre> </pre>
|
<pre> </pre>
|
||||||
<svg class="circle-visualization h-full" :viewBox="viewBox">
|
<svg class="circle-visualization h-full" :viewBox="viewBox">
|
||||||
|
|
||||||
|
<circle v-if="! pieData" cx="50" cy="50" r="50"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ onMounted(async () => {
|
||||||
{{ circleStore.circleData.title }}
|
{{ circleStore.circleData.title }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div v-if="circleStore.circleData.learningSequences && circleStore.completionData" class="w-full mt-8">
|
<div v-if="circleStore.circleData.learningSequences && circleStore.flatChildren" class="w-full mt-8">
|
||||||
<CircleDiagram :circle-store="circleStore"></CircleDiagram>
|
<CircleDiagram :circle-store="circleStore"></CircleDiagram>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue