diff --git a/src/components/InteractiveDendrogram.vue b/src/components/InteractiveDendrogram.vue index dab70432f706f848b646d2bbec1a86328dde9233..5ef6741076a7d3cbbe1e064adf1b7a736aee0633 100644 --- a/src/components/InteractiveDendrogram.vue +++ b/src/components/InteractiveDendrogram.vue @@ -46,12 +46,12 @@ </template> <script setup> -import { onMounted, ref } from 'vue'; +import { computed, onMounted, ref, watch } from 'vue'; import { isMobile } from 'mobile-device-detect'; import * as d3 from 'd3'; import { useI18n } from 'vue-i18n'; -const { t } = useI18n(); +const { t, locale } = useI18n(); // global variables const publicPath = import.meta.env.BASE_URL; @@ -85,7 +85,6 @@ let linkWidthScale, nodeRadiusScale; onMounted(() => { const userLang = navigator.language || navigator.userLanguage; - const isSpanish = userLang.startsWith('es'); // Check if the language is Spanish loadData().then(() => { if (data.value.length > 0) { @@ -97,10 +96,10 @@ onMounted(() => { .domain([0, d3.max(data.value, d => d.evidence_val)]) .range([2, 9]); - transformData(isSpanish) + transformData() .then((result) => { calculateTextSize(); - createDendrogram(result, isSpanish); + createDendrogram(result); }) .catch((error) => { console.error('Error transforming data:', error); @@ -109,6 +108,8 @@ onMounted(() => { }); }) + + async function loadData() { try { const jsonData = await d3.json(publicPath + 'indicator_uncertainty.json'); @@ -130,8 +131,10 @@ function calculateTextSize() { } } -function transformData(isSpanish) { +async function transformData() { return new Promise((resolve) => { + const isSpanish = locale.value === 'es'; // Determine if the current locale is Spanish + const nestedData = data.value.reduce((acc, item) => { const dimensionName = isSpanish ? item.dimension_es : item.dimension; let dimensionObj = acc.find((d) => d.name === dimensionName); @@ -151,7 +154,7 @@ function transformData(isSpanish) { determinantObj.children.push({ name: indicatorName, evidence_val: item.evidence_val, - level_agreement: item.level_agreement + level_agreement: item.level_agreement, }); return acc; @@ -167,7 +170,7 @@ function transformData(isSpanish) { return node.totalEvidence; }; - nestedData.forEach(dimension => calculateTotalEvidence(dimension)); + nestedData.forEach((dimension) => calculateTotalEvidence(dimension)); const result = { name: ' ', children: nestedData }; resolve(result); @@ -175,8 +178,15 @@ function transformData(isSpanish) { } +watch(() => locale.value, async () => { + // Clear the existing SVG before creating a new dendrogram + d3.select('#dendrogram-container').select('svg').remove(); + + const result = await transformData(); + createDendrogram(result); +}); -function createDendrogram(result, isSpanish) { +function createDendrogram(result) { const width = 1000; const height = 800; const marginTop = 40; @@ -237,7 +247,7 @@ root.descendants().forEach(d => { .attr('cursor', 'pointer') .attr('pointer-events', 'all'); - function update(event, source) { + function update(event, source) { const duration = 1200; const nodes = root.descendants().reverse(); const links = root.links().filter(link => link.source.depth !== 0); @@ -327,8 +337,8 @@ root.descendants().forEach(d => { nodeEnter.append('circle') .filter(d => d.depth !== 1) .attr('r', d => getNodeRadius(d)) - .attr('fill', d => getNodeColor(d, isSpanish)) - .attr('stroke', d => getNodeColor(d, isSpanish)) + .attr('fill', d => getNodeColor(d)) + .attr('stroke', d => getNodeColor(d)) .attr('stroke-width', 2); const wrapWidth = mobileView ? 300 : 300; @@ -344,10 +354,10 @@ root.descendants().forEach(d => { }) .attr('text-anchor', d => d.depth === 1 ? 'end' : 'start') .text(d => d.depth === 3 ? `${d.data.name} (${d.data.evidence_val})` : d.data.name) - .style('fill', d => getNodeColor(d, isSpanish)); + .style('fill', d => getNodeColor(d)); // function to add line break to dimension labels after 2nd word if spanish, after first word if english - function wrapAfterSecondWord(textSelection, isSpanish) { + function wrapAfterSecondWord(textSelection) { textSelection.each(function() { const textElement = d3.select(this); const text = textElement.text(); // Get the original text @@ -356,7 +366,7 @@ root.descendants().forEach(d => { const words = text.split(/\s+/); // For English: Break after 1st word, for Spanish: Break after 2nd word - const breakAfter = isSpanish ? 2 : 1; + const breakAfter = locale.value === 'es' ? 2 : 1; // If there are more than 2 words, wrap after the second word if (words.length > breakAfter) { @@ -374,11 +384,11 @@ root.descendants().forEach(d => { .attr('dy', '1.2em') .text(words.slice(breakAfter).join(' ')); } - }); - } + }); + } textElements.filter(d => d.depth === 1) - .call(wrapAfterSecondWord, isSpanish) + .call(wrapAfterSecondWord) .clone(true).lower() .attr('stroke-linejoin', 'round') .attr('stroke-width', 3) @@ -427,12 +437,12 @@ root.descendants().forEach(d => { return diagonal({ source: o, target: o }); }) .attr('stroke-width', getLinkWidth) - .attr('stroke', d => getLinkColor(d, isSpanish)); + .attr('stroke', d => getLinkColor(d)); link.merge(linkEnter).transition(transition) .attr('d', diagonal) .attr('stroke-width', getLinkWidth) - .attr('stroke', d => getLinkColor(d, isSpanish)); + .attr('stroke', d => getLinkColor(d)); link.exit().transition(transition).remove() .attr('d', () => { @@ -491,42 +501,38 @@ function wrap(text, width) { }); } -function getNodeColor(d, isSpanish) { - // Choose the correct color map based on the language - const colorMap = isSpanish ? dimensionColors_es : dimensionColors; +function getNodeColor(d) { + // Choose the correct color map based on the current locale + const colorMap = locale.value === 'es' ? dimensionColors_es : dimensionColors; + let normalizedDimension; if (d.depth === 1) { - const normalizedDimension = normalizeDimensionName(d.data.name); - return colorMap[normalizedDimension] || 'transaprent'; // Fallback to grey if not found + normalizedDimension = normalizeDimensionName(d.data.name); } else if (d.parent && d.parent.depth === 1) { - const normalizedDimension = normalizeDimensionName(d.parent.data.name); - return colorMap[normalizedDimension] || '#ccc'; + normalizedDimension = normalizeDimensionName(d.parent.data.name); } else if (d.parent && d.parent.parent && d.parent.parent.depth === 1) { - const normalizedDimension = normalizeDimensionName(d.parent.parent.data.name); - return colorMap[normalizedDimension] || '#ccc'; - } else { - return 'transparent'; // Fallback color + normalizedDimension = normalizeDimensionName(d.parent.parent.data.name); } + + // Return the color from the map, defaulting to '#ccc' if not found + return colorMap[normalizedDimension] || '#ccc'; } -function getLinkColor(d, isSpanish) { - const colorMap = isSpanish ? dimensionColors_es : dimensionColors; +function getLinkColor(d) { + const colorMap = locale.value === 'es' ? dimensionColors_es : dimensionColors; + let normalizedDimension; if (d.target.depth === 3) { - const normalizedDimension = normalizeDimensionName(d.target.parent.parent.data.name); - return colorMap[normalizedDimension] || '#ccc'; + normalizedDimension = normalizeDimensionName(d.target.parent.parent.data.name); } else if (d.target.depth === 2) { - const normalizedDimension = normalizeDimensionName(d.target.parent.data.name); - return colorMap[normalizedDimension] || '#ccc'; + normalizedDimension = normalizeDimensionName(d.target.parent.data.name); } else if (d.target.depth === 1) { - const normalizedDimension = normalizeDimensionName(d.target.data.name); - return colorMap[normalizedDimension] || '#ccc'; - } else { - return 'red'; // Fallback color for other cases + normalizedDimension = normalizeDimensionName(d.target.data.name); } -} - + // Return the color from the map, defaulting to '#ccc' if not found + return colorMap[normalizedDimension] || '#ccc'; +} function getLinkWidth(d) { if (d.target.depth === 3) {