diff --git a/dbb.php b/dbb.php index 71ed04cfffc1d0c72714eb6a8bf30e091a053430..b2f785d7178f90b310db9a2c384f605bf414e100 100644 --- a/dbb.php +++ b/dbb.php @@ -162,7 +162,7 @@ function dbb_styles() { wp_enqueue_style( 'dbb', - plugins_url( 'css/style.css' , FILE), + plugins_url( 'css/style.css' , __FILE__), array(), NULL ); @@ -170,7 +170,7 @@ function dbb_styles() { else { wp_enqueue_style( 'dbb', - plugins_url( 'css/style.min.css' , FILE), + plugins_url( 'css/style.min.css' , __FILE__), array(), NULL ); diff --git a/fonts/RobotoCondensed-VariableFont_wght.ttf b/fonts/RobotoCondensed-VariableFont_wght.ttf deleted file mode 100644 index a5f645b6e12582b87439eaba68b5da97aa32d62f..0000000000000000000000000000000000000000 Binary files a/fonts/RobotoCondensed-VariableFont_wght.ttf and /dev/null differ diff --git a/fonts/RobotoCondensed-VariableFont_wght.woff b/fonts/RobotoCondensed-VariableFont_wght.woff deleted file mode 100644 index 42bb5e1e8db6f398647b3843ccc381052eb7a208..0000000000000000000000000000000000000000 Binary files a/fonts/RobotoCondensed-VariableFont_wght.woff and /dev/null differ diff --git a/fonts/RobotoCondensed-VariableFont_wght.woff2 b/fonts/RobotoCondensed-VariableFont_wght.woff2 deleted file mode 100644 index 1e274482672003984caadfa51c5de1a9912638a4..0000000000000000000000000000000000000000 Binary files a/fonts/RobotoCondensed-VariableFont_wght.woff2 and /dev/null differ diff --git a/gulpfile.js b/gulpfile.js index 0718f4baa01917dcf847b987f8ae0e974207b302..854293785db43f780b0a8e0302cfb66a50f70f13 100755 --- a/gulpfile.js +++ b/gulpfile.js @@ -96,7 +96,7 @@ gulp.task( 'cssnano', function() { }); gulp.task( 'minifycss', function() { - return gulp.src( `${paths.css}/style.css`,{"allowEmpty": true} ) + return gulp.src( `${paths.css}/style.css`,{"allowEmpty": true} ) .pipe( sourcemaps.init( { loadMaps: true } ) ) .pipe( cleanCSS( { compatibility: '*' } ) ) .pipe( plumber( { @@ -130,12 +130,14 @@ gulp.task( 'browser-sync', function() { // Uglifies and concat all JS files into one gulp.task( 'scripts', function() { var scripts = [ - // vega and vega-lite `${paths.dev}/js/assets/vega/vega.js`, `${paths.dev}/js/assets/vega-lite/vega-lite.js`, `${paths.dev}/js/assets/vega-embed/vega-embed.js`, + // D3 + `${paths.dev}/js/assets/d3/d3.js`, + // Main custom js file `${paths.dev}/js/custom.js`, @@ -193,6 +195,12 @@ gulp.task( 'copy-assets', function() { .pipe( gulp.dest( `${paths.dev}/js/assets/vega-embed` ) ); gulp.src( `${paths.node}vega-embed/build/vega-embed.min.js.map` ) .pipe( gulp.dest( `${paths.dev}/js/assets/vega-embed` ) ); + + // D3.js from node_modules + gulp.src( `${paths.node}d3/dist/d3.js` ) + .pipe( gulp.dest( `${paths.dev}/js/assets/d3` ) ); + gulp.src( `${paths.node}d3/dist/d3.min.js` ) + .pipe( gulp.dest( `${paths.dev}/js/assets/d3` ) ); return stream; }); diff --git a/inc/dbb-template-tags.php b/inc/dbb-template-tags.php index 24ee6e87d2b80655dea25fcb91a3c4b5ef7a4288..72c117aad7ae8f17757365f37bf1f70436dcbb45 100644 --- a/inc/dbb-template-tags.php +++ b/inc/dbb-template-tags.php @@ -102,7 +102,7 @@ function dbb_graph_6() { /** * - * Gráfico 6. + * Gráfico 6b. * Porcentaje de mujeres en plantilla, puestos intermedios y de decisión * */ @@ -114,3 +114,37 @@ function dbb_graph_6b() { vegaEmbed("#vis6b", vis6b);</script> '; } + + +/** + * + * Gráfico 6 con D3 + * Porcentaje de mujeres en plantilla, puestos intermedios y de decisión + * + */ +function dbb_graph_6_d3() { + + return ' + <div style="width: 100%; margin: 0 auto;"> + <div id="vis6_d3" style="width: 100%;"></div> + </div> + '; +} + + + +/** + * + * Gráfico 6 con D3 + * Porcentaje de mujeres en plantilla, puestos intermedios y de decisión + * + */ +function dbb_graph_6b_d3() { + + return ' + <div style="width: 100%; margin: 0 auto;"> + <div id="vis6b_d3" style="width: 100%;"></div> + </div> + <div class="dbb_tooltip_slope"></div> + '; +} diff --git a/inc/shortcodes.php b/inc/shortcodes.php index fb29aa7216d9d4d1ca8749b74966b3d9198cbbd3..a08b33226ac8d04b3f668a8d7e2455dfaa1e868a 100644 --- a/inc/shortcodes.php +++ b/inc/shortcodes.php @@ -68,6 +68,16 @@ add_shortcode('barras_empresa_plantilla_sexo', 'dbb_graph_5'); */ add_shortcode('lineas_mujeres_plantilla_intermedio_decision', 'dbb_graph_6'); +/** + * Render gráfico 6 in D3 + * + * function in inc/dbb-template-tags.php + * + * @return string HTML output + * + */ +add_shortcode('lineas_mujeres_plantilla_intermedio_decision_d3', 'dbb_graph_6_d3'); + /** * Render gráfico 6b * @@ -77,3 +87,15 @@ add_shortcode('lineas_mujeres_plantilla_intermedio_decision', 'dbb_graph_6'); * */ add_shortcode('lineas_mujeres_plantilla_decision', 'dbb_graph_6b'); + +/** + * Render gráfico 6b in D3 + * + * function in inc/dbb-template-tags.php + * + * @return string HTML output + * + */ +add_shortcode('lineas_mujeres_plantilla_decision_d3', 'dbb_graph_6b_d3'); + + diff --git a/package.json b/package.json index 235938137b74bfccf4ab72e321dcad1d01c7027e..977f65836f1e92be9f9b9bbe47a6fee7a528ef2e 100644 --- a/package.json +++ b/package.json @@ -43,5 +43,8 @@ "vega": "^5.27.0", "vega-embed": "^6.24.0", "vega-lite": "^5.17.0" + }, + "dependencies": { + "d3": "^7.9.0" } } diff --git a/sass/assets/d3.scss b/sass/assets/d3.scss new file mode 100644 index 0000000000000000000000000000000000000000..203896d4a2148055c2e148f3ab135d6868c4551c --- /dev/null +++ b/sass/assets/d3.scss @@ -0,0 +1,62 @@ +body { + font-family: Barlow, Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + overflow-x: auto !important; +} +path:hover, circle:hover, text:hover, text:hover, line:hover { + cursor:pointer; +} + +.mandos_intermedios {display:none;} + + +#vis6_d3 { + margin: auto; + max-width: 100%; + max-height:720px; + overflow-x: scroll; + + svg { + display: block; + height: auto; + margin: auto; + width: 100%; + max-height:700px; + overflow:hidden; + } +} + +#vis6b_d3 { + margin: auto; + max-width: 100%; + overflow-x: scroll; + overflow-y: scroll; + max-height:600px; + overflow:hidden; + + svg { + display: block; + height: auto; + margin: auto; + width: 100%; + max-height:600px; + overflow:hidden; + } + +} + +/* This ensures that stroke widths remain the same, no matter how the SVG scales.*/ +line, path, circle { + vector-effect: non-scaling-stroke; +} + +.dbb_tooltip_slope { + position: absolute; + background-color: white; + border: 1px solid black; + padding: 5px; + display: none; + font-size: 1rem; +} \ No newline at end of file diff --git a/sass/assets/fonts.scss b/sass/assets/fonts.scss index 965cf0306d955d08ed1104f79e3bc2bba25b3582..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/sass/assets/fonts.scss +++ b/sass/assets/fonts.scss @@ -1,17 +0,0 @@ -@font-face { - font-family: 'Roboto Condensed'; - src: url('../fonts/RobotoCondensed-VariableFont_wght.woff2') format('woff2'), /* Adjust the path and format based on your font files */ - url('../fonts/RobotoCondensed-VariableFont_wght.woff') format('woff'), - url('../fonts/RobotoCondensed-VariableFont_wght.ttf') format('truetype'); /* Use the appropriate formats */ - font-weight: 400; /* Regular */ - font-style: normal; -} - -@font-face { - font-family: 'Roboto Condensed'; - src: url('../fonts/RobotoCondensed-VariableFont_wght.woff2') format('woff2'), - url('../fonts/RobotoCondensed-VariableFont_wght.woff') format('woff'), - url('../fonts/RobotoCondensed-VariableFont_wght.ttf') format('truetype'); - font-weight: 700; /* Bold */ - font-style: normal; -} diff --git a/sass/style.scss b/sass/style.scss new file mode 100644 index 0000000000000000000000000000000000000000..9e0821875a256da0741e864d92672dde75a5a19d --- /dev/null +++ b/sass/style.scss @@ -0,0 +1 @@ +@import "assets/d3"; // import d3js chart related styles \ No newline at end of file diff --git a/sass/styles.scss b/sass/styles.scss deleted file mode 100644 index ab3aca08d87c4b640e7a8ddc5e735b680f711d82..0000000000000000000000000000000000000000 --- a/sass/styles.scss +++ /dev/null @@ -1 +0,0 @@ -// @import "assets/fonts"; diff --git a/src/css/custom.css b/src/css/custom.css new file mode 100644 index 0000000000000000000000000000000000000000..c858d14955fe4ea79b144327b8f6f7f5e1dadd1f --- /dev/null +++ b/src/css/custom.css @@ -0,0 +1,24 @@ +body { + font-family: Barlow, Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; +} +svg { + display: block; + margin: auto; +} +path:hover, circle:hover, text:hover, text:hover { + cursor:pointer; +} +.highlight { stroke-width: 4 !important; opacity: 1 !important; } +.mandos_intermedios {display:none;} +.dbb_tooltip_slope { + position: absolute; + background-color: white; + border: 1px solid black; + padding: 5px; + display: none; + font-size: 12px; + } diff --git a/src/html/slope-chart-d3.html b/src/html/slope-chart-d3.html index b64084640f24e24d3d4c568aa100b7d8007e4d4f..dc1e37340fa03b92970f0e4aef40177bde626210 100644 --- a/src/html/slope-chart-d3.html +++ b/src/html/slope-chart-d3.html @@ -32,19 +32,15 @@ </head> <body> <div class="dbb_tooltip"></div> + <svg width="800" height="800"></svg> <script> // Set margins and dimensions for the chart - const margin = { top: 50, right: 260, bottom: 50, left: 260 }, - width = 800 - margin.left - margin.right, - height = 800 - margin.top - margin.bottom; + const svg = d3.select("svg"), + margin = { top: 50, right: 260, bottom: 20, left: 260 }, + width = +svg.attr("width") - margin.left - margin.right, + height = +svg.attr("height") - margin.top - margin.bottom; - // Create an SVG container and append it to the body - const svg = d3.select("body") - .append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", `translate(${margin.left},${margin.top})`); + const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`); // Tooltip element for displaying data on hover const tooltip = d3.select(".dbb_tooltip"); @@ -99,7 +95,7 @@ // console.log("Assigning class to line:", sanitizeClassName(d.name)); // Debugging log // Draw lines ------------------ - svg.append("line") + g.append("line") .attr("x1", xScale(positions[0])) .attr("y1", yScale(d.direccion)) .attr("x2", xScale(positions[1])) @@ -147,7 +143,7 @@ }); // Draw circles at each data point ----------------------------- - svg.selectAll("circle") + g.selectAll("circle") .data(dataset) .enter() .append("circle") @@ -200,7 +196,7 @@ .on("click", (event, d) => window.open(d.url, "_blank")); // Open the organization URL when clicked // Simple Labels - svg.selectAll(".dbb_label") + g.selectAll(".dbb_label") .data(dataset) .enter() .append("text") @@ -251,14 +247,14 @@ .on("click", (event, d) => window.open(d.url, "_blank")); // Open the organization URL when clicked // Draw X-axis labels - svg.append("g") + g.append("g") .attr("transform", `translate(0,-20)`) .call(d3.axisTop(xScale)) .select(".domain") // Select the axis line .remove(); // Remove the line // Draw Y-axis with percentage format - svg.append("g") + g.append("g") //.call(d3.axisLeft(yScale).ticks(5).tickFormat(d => `${d}%`)) .select(".domain") // Select the axis line .remove(); // Remove the line diff --git a/src/js/custom.js b/src/js/custom.js index 4f404a8397f0215c54dd54287b7e81f10d911338..aae9f9484ba3457361ce31994b42a2f75a320d1a 100644 --- a/src/js/custom.js +++ b/src/js/custom.js @@ -1,4 +1,4 @@ - // JS code for this plugin +// JS code for this plugin // entidades count const entidadesCount = dbb.entidades; @@ -320,7 +320,12 @@ var vis3 = { data: { url: dbb.endpoint.g5 }, // width: "container", // "width": 400, + "autosize": { + "type": "fit", + "contains": "padding" + }, //"height": {"step": 15}, + //"height": 650, "transform": [ { filter: { field: 'lang', equal: dbb.lang } }, { @@ -353,7 +358,7 @@ var vis3 = { }, "spec": { "width":"container", - "height":600, + //"height":600, "layer": [ { "encoding": { @@ -481,6 +486,7 @@ var vis4 = { ], groupby: ["actividad.0.name","actividad.0.color"] }, + {calculate: "datum.count / 2", as: "count"}, // divide por dos, porque hay uno para cada sexo en la tabla { calculate: "'(' + datum.count + ' " + dbb.strings.companies + ")'", as: "count" // New field with the combined text @@ -1138,7 +1144,7 @@ var vis6b = { "grid": false, "labels": true, "orient": "top", - "labelAngle": -10, + "labelAngle": -11, "labelLimit": 180, "labelFontWeight":"bold" } @@ -1332,3 +1338,669 @@ var vis6b = { } } } + + +// Parallel coordinates chart +// Gráfico 6_d3 +// Porcentaje de mujeres en puestos de mando y resto de plantilla + +// Function to create the D3.js chart +window.create_6_d3= function() { + + // Select the container + const container = d3.select("#vis6_d3"); + + function drawChart() { + + // Remove any existing SVG to prevent duplicates + d3.select("#vis6_d3").select("svg").remove(); + + // Get the width dynamically + let width = container.node().getBoundingClientRect().width; + let height = width > 768 ? width * 1.3 : width * 3.1; // Adjust height for mobile. Changes aspect ratio. Makes it more vertical from small screens + //const height = width * proportionParallelCoord(); // Maintain aspect ratio. Makes it more vertical from small screens + const margin_left = 370; + const margin_top = 100; + const margin_right = 370; + + // Append the SVG with a responsive viewBox + const svg = container.append("svg") + .attr("viewBox", `0 0 ${width + margin_left + margin_right} ${height+margin_top}`) + .attr("preserveAspectRatio", "xMidYMid meet") + .style("width", "100%") // Responsive width + .style("height", "auto"); // Adjusts height proportionally + + const g = svg.append("g").attr("transform", `translate(${margin_left},${margin_top})`); + + // Replace function + const sanitizeClassName = name => name.replace(/[\s.,]+/g, "-").toLowerCase(); // Replace spaces and dots with '-' + + // Load data from API + // const url = "https://denbbora.eus/wp-json/dbb/entidades"; + const url = dbb.endpoint.g5; + + d3.json(url).then(data => { + // Process and filter data to extract necessary values + const processedData = data.filter(d => d.lang === "es") // eu + .map(d => ({ + name: d.name, + direccion: d.medidas._e_women_management_mean.value, + mandos_intermedios: d.medidas._e_mandos_intermedios_women.value, + resto_plantilla: d.medidas._e_resto_plantilla_women.value, + url: `https://denbbora.eus/${d.slug}` + })) + + // remove data if empty or null + .filter(d => d.direccion !== null && d.direccion !== "" && + d.mandos_intermedios !== null && d.mandos_intermedios !== "" && + d.resto_plantilla !== null && d.resto_plantilla !== ""); + + // Define position labels for X-axis + const positions = ["direccion", "mandos_intermedios", "resto_plantilla"]; + + // Create scales for positioning data points + const xScale = d3.scalePoint().domain(positions).range([0, width]); + const yScales = {}; + positions.forEach(dim => { + yScales[dim] = d3.scaleLinear().domain([0, 100]).range([height, 0]); + }); + + // Assign colors to each company + const colorScale = d3.scaleOrdinal(d3.schemeCategory10); + + // Define line generator + const line = d3.line() + .x(d => xScale(d.position)) + .y(d => yScales[d.position](d.value)); + + // Process data into line format + const formattedData = processedData.map(d => { + return { + name: d.name, + values: positions.map(dim => ({ position: dim, value: d[dim] })) + }; + }); + + // Draw Paths ------------------------------------------ + g.selectAll(".line") + .data(formattedData) + .enter().append("path") + .attr("class", "line") + .attr("class", d => `path-${sanitizeClassName(d.name)}`) + .attr("d", d => line(d.values)) + .attr("fill","transparent") + .attr("stroke-width", 2) + .style("stroke", d => colorScale(d.name)) + .on("mouseover", function(event, d) { + d3.selectAll(`path`).attr("opacity", 0.15); + d3.selectAll(`.dbb_label`).attr("opacity", 0.15); + d3.selectAll(`circle`).attr("opacity", 0); + + d3.select(this).attr("stroke-width", 4).attr("opacity", 1); + + d3.selectAll(`.circle-${sanitizeClassName(d.name)}`) + .attr("r", 20) + .attr("opacity", 1); + + d3.selectAll(`.dbb_label-${sanitizeClassName(d.name)}`) + .style("font-weight", "bold") + .attr("opacity", 1); + + d3.selectAll(`.number-group`) + .raise(); + + d3.selectAll(`.number-${sanitizeClassName(d.name)}`) + .attr("opacity", 1) + .attr("font-size", width > 768 ? "1.6rem": "1.8rem") + .raise(); + + // For the 6b_d3 chart + d3.selectAll(`.slopeline`).attr("opacity", 0.2); + d3.selectAll(`.line-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 4) + .attr("opacity", 0.9) + .raise(); + }) + .on("mouseout", function(event, d) { + d3.select(this).attr("stroke-width", 2).attr("opacity", 1).lower(); + + d3.selectAll(`.number-${sanitizeClassName(d.name)}`) + .attr("opacity", 0) + .attr("font-size", "1px"); + + d3.selectAll(`.circle-${sanitizeClassName(d.name)}`) + .attr("r", 5); + + d3.selectAll(`.number-group`).raise(); + + d3.selectAll(`path`).attr("opacity", 1); + d3.selectAll(`.dbb_label`).attr("opacity", 1).style("font-weight", "normal"); + d3.selectAll(`circle`).attr("opacity", 1).attr("r", 5); + + // For the 6b_d3 chart + d3.selectAll(`.line-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 2); + d3.selectAll(`.slopeline`).attr("opacity", 0.9); + }); + + // Draw numbers ----------------------------- + g.selectAll(".number-group") + .data(formattedData) + .enter().append("g") + .attr("class", d => `number-group numb-${sanitizeClassName(d.name)}`) + .each(function(d) { + d3.select(this) + .selectAll(".number") + .data(d.values) + .enter().append("text") + .attr("opacity", 0) + .attr("font-size", "1px") + .attr("class", `number-${sanitizeClassName(d.name)}`) + .attr("x", d => xScale(d.position) ) + .attr("y", d => yScales[d.position](d.value)+8) + .attr("text-anchor", "middle") + .attr("fill", "white") + .text(d => Math.round(d.value)) + .lower(); + }); + + // Draw labels ------------------------- + g.selectAll(".dbb_label-group") + .data(formattedData) + .enter().append("g") + .attr("class", d => `dbb_label dbb_label-${sanitizeClassName(d.name)}`) + .each(function(d) { + d3.select(this) + .selectAll(".dbb_label") + .data(d.values) + .enter().append("text") + .attr("font-size", width > 768 ? "1.7rem": "1.8rem") + .attr("class", d => `${d.position}`) // para ocultar el de enmedio + .attr("x", d => d.position === positions[0] ? xScale(d.position)-21 : xScale(d.position)+21 ) + .attr("y", d => yScales[d.position](d.value)+5) + .attr("text-anchor", d => d.position === positions[0] ? "end" : "start") + .attr("fill", colorScale(d.name)) + .text(d.name); + }) + .on("mouseover", function(event, d) { + d3.selectAll(`path`).attr("opacity", 0.15); + d3.selectAll(`.dbb_label`).attr("opacity", 0.15); + d3.selectAll(`circle`).attr("opacity", 0); + + d3.select(this).attr("opacity", 1).style("font-weight", "bold"); + + d3.selectAll(`.path-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 4) + .attr("opacity", 1); + + d3.selectAll(`.circle-${sanitizeClassName(d.name)}`) + .attr("r", 20) + .attr("opacity", 1) + .lower(); + + d3.selectAll(`.dbb_label-${sanitizeClassName(d.name)}`) + .style("font-weight", "bold") + .attr("opacity", 1); + + d3.selectAll(`.number-group`) + .raise(); + + d3.selectAll(`.number-${sanitizeClassName(d.name)}`) + .attr("opacity", 1) + .attr("font-size", width > 768 ? "1.6rem": "1.8rem") + .raise(); + + // For the 6b_d3 chart + d3.selectAll(`.slopeline`).attr("opacity", 0.2); + d3.selectAll(`.line-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 4) + .attr("opacity", 0.9) + .raise(); + }) + .on("mouseout", function(event, d) { + d3.select(this).attr("opacity", 1).lower(); + + d3.selectAll(`.number-${sanitizeClassName(d.name)}`) + .attr("opacity", 0) + .attr("font-size", "1px"); + + d3.selectAll(`.circle-${sanitizeClassName(d.name)}`) + .attr("r", 5); + + d3.selectAll(`.number-group`).lower(); + d3.selectAll(`path`).attr("opacity", 1).attr("stroke-width", 2); + d3.selectAll(`.dbb_label`).attr("opacity", 1).style("font-weight", "normal"); + d3.selectAll(`circle`).attr("opacity", 1).attr("r", 5); + + // For the 6b_d3 chart + d3.selectAll(`.line-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 2); + d3.selectAll(`.slopeline`).attr("opacity", 0.9); + }); + + // Draw the circles ------------------- + g.selectAll(".circle-group") + .data(formattedData) + .enter() + .append("g") + .attr("class", d => `circle-group`) + .each(function(d) { + d3.select(this) + .selectAll(".circle") + .data(d.values) + .enter().append("circle") + .attr("class", `circle-${sanitizeClassName(d.name)}`) + .attr("cx", d => xScale(d.position)) + .attr("cy", d => yScales[d.position](d.value)) + .style("fill", colorScale(d.name)) + .attr("r", 5) + }) + .on("mouseover", function(event, d) { + d3.selectAll(`path`).attr("opacity", 0.15); + d3.selectAll(`.dbb_label`).attr("opacity", 0.15); + d3.selectAll(`circle`).attr("opacity", 0); + + d3.select(this).attr("r", 10).attr("opacity", 1).attr("fill","black"); + + d3.selectAll(`.circle-${sanitizeClassName(d.name)}`)// TODO: this is noot working + .attr("r", 20) + .attr("fill","black") // for testing + .attr("opacity", 1); + + d3.selectAll(`.path-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 4) + .attr("opacity", 1); + + d3.selectAll(`.dbb_label-${sanitizeClassName(d.name)}`) + .style("font-weight", "bold") + .attr("opacity", 1); + + d3.selectAll(`.number-group`) + .raise(); + + d3.selectAll(`.number-${sanitizeClassName(d.name)}`) + .attr("opacity", 1) + .attr("font-size", width > 768 ? "1.6rem": "1.8rem"); + }) + .on("mouseout", function(event, d) { + d3.selectAll(`.path-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 2) + .attr("opacity", 1); + + d3.selectAll(`.number-${sanitizeClassName(d.name)}`) + .attr("opacity", 0) + .attr("font-size", "1px"); + + d3.selectAll(`.number-group`).lower(); + d3.selectAll(`path`).attr("opacity", 1); + d3.selectAll(`.dbb_label`).attr("opacity", 1).style("font-weight", "normal"); + d3.selectAll(`circle`).attr("opacity", 1).attr("r", 5); + + }); + + // Draw X-axis labels ------------------------------ + const axisLabels = { + direccion: dbb.strings.women_managers_percentage, + mandos_intermedios: dbb.strings.women_middle_management_percentage, + resto_plantilla: dbb.strings.women_workforce_percentage + }; + + // Draw axis labels + g.selectAll(".axis-label") + .data(positions) + .enter().append("text") + .attr("class", "axis-label") + .attr("x", d => xScale(d)) + .attr("y", -30) + .style("font-size", width > 768 ? "1.5rem": "1.5rem") // Change font size + .attr("fill","#222222") + .attr("text-anchor", width < 768 ? "left" : "middle") + //.attr("font-weight","bold") + .text(d => axisLabels[d]) // Use mapping object here + .attr("transform", d => { + return width < 768 + ? `rotate(353, ${xScale(d)}, -35)` // Rotate -5° around (x, y) + : null; // No rotation if width >= 768px + }); // Use mapping object here + + // Remove axis + g.append("g") + //.call(d3.axisLeft(yScale).ticks(5).tickFormat(d => `${d}%`)) + .select(".domain") // Select the axis line + .remove(); // Remove the line + }); + + } + + // Initial Draw + drawChart(); + + // Redraw on Resize + window.addEventListener("resize", drawChart); + +}; + +// Insert chart +document.addEventListener("DOMContentLoaded", function () { + setTimeout(() => { + let chartContainer = document.querySelector("#vis6_d3"); + if (chartContainer) { + if (typeof create_6_d3 === "function") { + create_6_d3(); + } + } + }, 100); // Small delay to ensure the container exists +}); + +function loadChart() { + let chartContainer = document.querySelector("#vis6_d3"); + if (chartContainer && typeof create_6_d3 === "function") { + chartContainer.style.display = "none"; // Hide it first + create_6_d3(); + chartContainer.offsetHeight; // Force a repaint + chartContainer.style.display = "block"; // Show it again + } +} + +document.addEventListener("DOMContentLoaded", loadChart); + +// Parallel slope chart +// Gráfico 6b_d3 +// Porcentaje de mujeres en puestos de mando, intermedios y resto de plantilla + +// Function to create the D3.js chart +window.create_6b_d3= function() { + + // Select the container + const container = d3.select("#vis6b_d3"); + + function drawChart() { + + // Remove any existing SVG to prevent duplicates + d3.select("#vis6b_d3").select("svg").remove(); + + // Get the width dynamically + let width = container.node().getBoundingClientRect().width; + let height = width > 768 ? width * 2.5 : width * 3.3; // Adjust height for mobile. Changes aspect ratio. Makes it more vertical from small screens + //const height = width * proportionParallelCoord(); // Maintain aspect ratio. Makes it more vertical from small screens + const margin_left = 260; + const margin_top = 100; + const margin_right = 260; + + // Append the SVG with a responsive viewBox + const svg = container.append("svg") + .attr("viewBox", `0 0 ${width + margin_left + margin_right} ${height+margin_top}`) + .attr("preserveAspectRatio", "xMidYMid meet") // If your issue is mostly about keeping things proportional, slice instead of meet may help + .style("width", "100%") // Responsive width + .style("height", "auto"); // Adjusts height proportionally + + const g = svg.append("g").attr("transform", `translate(${margin_left},${margin_top})`); + + // Tooltip element for displaying data on hover + const tooltip = d3.select(".dbb_tooltip_slope"); + + // Replace function + const sanitizeClassName = name => name.replace(/[\s.,]+/g, "-").toLowerCase(); // Replace spaces and dots with '-' + + // Load data from API + //const url = "https://denbbora.eus/wp-json/dbb/entidades"; + const url = dbb.endpoint.g5; + + d3.json(url).then(data => { + + // Process and filter data to extract necessary values + const processedData = data.filter(d => d.lang === "es") // eu + .map(d => ({ + name: d.name, + direccion: d.medidas._e_women_management_mean.value, + resto_plantilla: d.medidas._e_resto_plantilla_women.value, + url: `https://denbbora.eus/${d.slug}` + })) + // remove data if empty or null + .filter(d => d.direccion !== null && d.direccion !== "" && d.resto_plantilla !== null && d.resto_plantilla !== "");; + + + // Define position labels for X-axis + const positions = [ + dbb.strings.women_managers_percentage, //"% Mujeres en puestos directivos", + dbb.strings.women_workforce_percentage // "% Mujeres en resto de plantilla" + ]; + + // Convert data into a format suitable for D3 + const dataset = processedData.flatMap(d => [ + { name: d.name, puesto: positions[0], porcentaje: d.direccion, url: d.url }, + { name: d.name, puesto: positions[1], porcentaje: d.resto_plantilla, url: d.url } + ]); + + // Create scales for positioning data points + const xScale = d3.scalePoint().domain(positions).range([0, width]); + const yScale = d3.scaleLinear() + .domain([0, d3.max(dataset, d => d.porcentaje)]) + .nice() + .range([height, 0]); + + // Assign colors to each organization + const colorScale = d3.scaleOrdinal(d3.schemeCategory10) + .domain(processedData.map(d => d.name)); + + // Draw lines connecting two data points for each organization + processedData.forEach(d => { // draws each line + + // Draw lines ------------------ + g.append("line") + .attr("x1", xScale(positions[0])) + .attr("y1", yScale(d.direccion)) + .attr("x2", xScale(positions[1])) + .attr("y2", yScale(d.resto_plantilla)) + .attr("stroke", colorScale(d.name)) + .attr("stroke-width", 2) + .attr("opacity", 0.9) + .attr("class", `slopeline line-${sanitizeClassName(d.name)}`) + .on("mouseover", function () { + // activate tooltip + tooltip.style("display", "block").html(`<strong>${d.name}</strong><br>Direccion: ${d3.format(".1f")(d.direccion)}%<br>Resto plantilla: ${d3.format(".1f")(d.resto_plantilla)}%`); + d3.selectAll(`.slopeline`).attr("opacity", 0.2); + d3.selectAll(`.dbb_label`).attr("opacity", 0.2); + d3.selectAll(`circle`).attr("opacity", 0.2); + + d3.select(this).attr("stroke-width", 4) + .attr("opacity", 0.9); // hightlight line + + // Make the labels bold and bring them to the front + d3.selectAll(`.dbb_label-${sanitizeClassName(d.name)}`) + .style("font-weight", "bold") + .attr("opacity", 1) + .attr("r", 8) // for circles + .raise(); // Bring to front + + // For 6_d3 chart + d3.selectAll(`path`).attr("opacity", 0.15); + d3.selectAll(`.path-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 4) + .attr("opacity", 1); + + + }) + .on("mousemove", function (event) { + tooltip.style("left", event.pageX + "px").style("top", event.pageY + "px"); + }) + .on("mouseout", function () { + d3.select(this).attr("stroke-width", 2); + tooltip.style("display", "none"); + + // Reset labels to normal font weight + d3.selectAll(`.dbb_label-${sanitizeClassName(d.name)}`) + .style("font-weight", "normal") + .attr("r", 5); // for circles + + d3.selectAll(`.slopeline`).attr("opacity", 0.9); + d3.selectAll(`.dbb_label`).attr("opacity", 1); + d3.selectAll(`circle`).attr("opacity", 0.9); + + // For 6_d3 chart + d3.selectAll(`path`).attr("opacity", 1).attr("stroke-width", 2); + }); + }); + + // Draw circles at each data point ----------------------------- + g.selectAll("circle") + .data(dataset) + .enter() + .append("circle") + .attr("class", d => `dbb_label-${sanitizeClassName(d.name)}`) // add class so it can be selected + .attr("cx", d => xScale(d.puesto)) + .attr("cy", d => yScale(d.porcentaje)) + .attr("r", 5) + .attr("fill", d => colorScale(d.name)) + .on("mouseover", function (event, d) { + + // Hide all the elements + d3.selectAll(`.slopeline`).attr("opacity", 0.2); + d3.selectAll(`.dbb_label`).attr("opacity", 0.2); + d3.selectAll(`circle`).attr("opacity", 0.2); + + //tooltip.style("display", "block").html(`<strong>${d.name}</strong><br>${d.puesto}: ${d.porcentaje}%`); + + // Highlight the lines and bring them to the front + d3.selectAll(`.line-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 4) + .attr("opacity", 1) + .raise(); // Bring to front + + // Make the labels bold and bring them to the front + d3.selectAll(`.dbb_label-${sanitizeClassName(d.name)}`) + .style("font-weight", "bold") + .attr("r", 8) + .attr("opacity", 1); + + }) + .on("mousemove", function (event) { + //tooltip.style("left", event.pageX + "px").style("top", event.pageY + "px"); + }) + .on("mouseout", function (event, d) { + d3.select(this).attr("r", 5); + //tooltip.style("display", "none"); + + d3.selectAll(`.line-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 2); + + d3.selectAll(`.dbb_label-${sanitizeClassName(d.name)}`) + .style("font-weight", "normal") + .attr("r", 5); + + // Hide all the elements + d3.selectAll(`.slopeline`).attr("opacity", 0.9); + d3.selectAll(`.dbb_label`).attr("opacity", 0.9).style("font-weight", "normal"); + d3.selectAll(`circle`).attr("opacity", 0.9); + }) + .on("click", (event, d) => window.open(d.url, "_blank")); // Open the organization URL when clicked + + // Simple Labels + g.selectAll(".dbb_label") + .data(dataset) + .enter() + .append("text") + .attr("class", d => `dbb_label dbb_label-${sanitizeClassName(d.name)}`) + .attr("x", d => d.puesto === positions[0] ? xScale(d.puesto)-10 : xScale(d.puesto)+10 ) + .attr("y", d => yScale(d.porcentaje) - 0) + .attr("text-anchor", d => d.puesto === positions[0] ? "end" : "start") + //.attr("font-size", width > 768 ? "3.2rem": "2rem") + .attr("font-size", width > 768 ? "3.2rem": "1.5rem") + .attr("fill", d => colorScale(d.name)) + .text(d => `${d.name} (${d.porcentaje % 1 === 0 ? d.porcentaje : d3.format(".1f")(d.porcentaje)}%)`) + .on("mouseover", function (event, d) { + //tooltip.style("display", "block").html(`<strong>${d.name}</strong><br>${d.puesto}: ${d.porcentaje}%`); + + // Hide all the elements + d3.selectAll(`.slopeline`).attr("opacity", 0.2); + d3.selectAll(`.dbb_label`).attr("opacity", 0.2); + d3.selectAll(`circle`).attr("opacity", 0.2); + + d3.select(this).style("cursor", "pointer"); + + // Highlight the lines and bring them to the front + d3.selectAll(`.line-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 4) + .attr("opacity", 0.9) + .raise(); // Bring to front + + d3.selectAll(`.dbb_label-${sanitizeClassName(d.name)}`) + .style("font-weight", "bold") + .attr("opacity", 0.9) + .raise(); // Bring to front + + // For 6_d3 chart + d3.selectAll(`path`).attr("opacity", 0.15); + d3.selectAll(`.path-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 4) + .attr("opacity", 1); + + }) + .on("mousemove", function (event) { + //tooltip.style("left", event.pageX + "px").style("top", event.pageY + "px"); + }) + .on("mouseout", function (event, d) { + d3.select(this).style("font-weight", "normal").lower(); + //tooltip.style("display", "none"); + + d3.selectAll(`.line-${sanitizeClassName(d.name)}`) + .attr("stroke-width", 2); + + // Hide all the elements + d3.selectAll(`.slopeline`).attr("opacity", 0.9); + d3.selectAll(`.dbb_label`).attr("opacity", 0.9).style("font-weight", "normal"); + d3.selectAll(`circle`).attr("opacity", 0.9); + + // For 6_d3 chart + d3.selectAll(`path`).attr("opacity", 1).attr("stroke-width", 2); + }) + .on("click", (event, d) => window.open(d.url, "_blank")); // Open the organization URL when clicked + + // Draw X-axis labels + g.append("g") + .attr("transform", `translate(0,-60)`) + .call(d3.axisTop(xScale)) + .selectAll("text") // Select all text elements in the axis + .style("font-size", width > 768 ? "2.5rem": "1.4rem") // Change font size + .attr("text-anchor", "middle") + .attr("fill","#444444") + .style("font-weight", "bold") + .attr("transform", d => { + return width < 768 + ? `rotate(357, 0, -35)` // Rotate -5° around (x, y) + : null; // No rotation if width >= 768px + }); // Use mapping object here + + // Remove the axis line + g.select(".domain").remove(); + }); + } + + // Initial Draw + drawChart(); + + // Redraw on Resize + window.addEventListener("resize", drawChart); +} + +// Insert chart +document.addEventListener("DOMContentLoaded", function () { + setTimeout(() => { + let chartContainer = document.querySelector("#vis6b_d3"); + if (chartContainer) { + if (typeof create_6b_d3 === "function") { + create_6b_d3(); + } + } + }, 100); // Small delay to ensure the container exists +}); + +function loadChart() { + let chartContainer = document.querySelector("#vis6b_d3"); + if (chartContainer && typeof create_6b_d3 === "function") { + chartContainer.style.display = "none"; // Hide it first + create_6b_d3(); + chartContainer.offsetHeight; // Force a repaint + chartContainer.style.display = "block"; // Show it again + } +}