thibaud frere commited on
Commit
cb92d91
·
1 Parent(s): 95e6b80
app/src/content/embeds/d3-line.html CHANGED
@@ -119,19 +119,18 @@
119
  display: 'flex',
120
  gap: '16px',
121
  alignItems: 'center',
122
- justifyContent: 'flex-start',
123
  width: '100%'
124
  });
125
 
126
  const labelMetric = document.createElement('label');
127
  Object.assign(labelMetric.style, {
128
- fontSize: '12px', color: 'var(--muted-color)', display: 'flex', alignItems: 'center', gap: '6px', whiteSpace: 'nowrap', padding: '6px 10px'
129
  });
130
  labelMetric.textContent = 'Metric';
131
  const selectMetric = document.createElement('select');
132
  Object.assign(selectMetric.style, { fontSize: '12px' });
133
  labelMetric.appendChild(selectMetric);
134
- controls.appendChild(labelMetric);
135
 
136
  // Inline legend on the right of the select
137
  const legendInline = document.createElement('div');
@@ -141,9 +140,11 @@
141
  gap: '8px',
142
  alignItems: 'center',
143
  flexWrap: 'nowrap',
144
- fontSize: '11px'
 
145
  });
146
  controls.appendChild(legendInline);
 
147
 
148
  // Create SVG
149
  const svg = d3.select(container).append('svg')
@@ -224,11 +225,20 @@
224
  xScale.range([0, innerWidth]);
225
  yScale.range([innerHeight, 0]);
226
 
 
 
 
 
 
 
 
 
 
 
227
  // Grid (horizontal)
228
  gGrid.selectAll('*').remove();
229
- const yTicks = yScale.ticks(6);
230
  gGrid.selectAll('line')
231
- .data(yTicks)
232
  .join('line')
233
  .attr('x1', 0)
234
  .attr('x2', innerWidth)
@@ -241,7 +251,7 @@
241
  // Axes
242
  gAxes.selectAll('*').remove();
243
  const xAxis = d3.axisBottom(xScale).ticks(8).tickSizeOuter(0);
244
- const yAxis = d3.axisLeft(yScale).ticks(6).tickSizeOuter(0).tickFormat(d3.format('.2f'));
245
  gAxes.append('g')
246
  .attr('transform', `translate(0,${innerHeight})`)
247
  .call(xAxis)
@@ -268,7 +278,7 @@
268
  gAxes.append('text')
269
  .attr('class', 'axis-label axis-label--y')
270
  .attr('text-anchor', 'middle')
271
- .attr('transform', `translate(${-52},${innerHeight/2}) rotate(-90)`)
272
  .style('font-size', '12px')
273
  .style('fill', tickColor)
274
  .text('Value');
 
119
  display: 'flex',
120
  gap: '16px',
121
  alignItems: 'center',
122
+ justifyContent: 'space-between',
123
  width: '100%'
124
  });
125
 
126
  const labelMetric = document.createElement('label');
127
  Object.assign(labelMetric.style, {
128
+ fontSize: '12px', color: 'var(--muted-color)', display: 'flex', alignItems: 'center', gap: '6px', whiteSpace: 'nowrap', padding: '6px 10px', marginLeft: 'auto'
129
  });
130
  labelMetric.textContent = 'Metric';
131
  const selectMetric = document.createElement('select');
132
  Object.assign(selectMetric.style, { fontSize: '12px' });
133
  labelMetric.appendChild(selectMetric);
 
134
 
135
  // Inline legend on the right of the select
136
  const legendInline = document.createElement('div');
 
140
  gap: '8px',
141
  alignItems: 'center',
142
  flexWrap: 'nowrap',
143
+ fontSize: '11px',
144
+ marginLeft: '8px'
145
  });
146
  controls.appendChild(legendInline);
147
+ controls.appendChild(labelMetric);
148
 
149
  // Create SVG
150
  const svg = d3.select(container).append('svg')
 
225
  xScale.range([0, innerWidth]);
226
  yScale.range([innerHeight, 0]);
227
 
228
+ // Compute integer ticks for Y
229
+ const yDomain = yScale.domain();
230
+ const yMin = Math.min(yDomain[0], yDomain[1]);
231
+ const yMax = Math.max(yDomain[0], yDomain[1]);
232
+ let yStep = Math.max(1, Math.round((yMax - yMin) / 6));
233
+ if (!isFinite(yStep) || yStep <= 0) yStep = 1;
234
+ let yIntTicks = [];
235
+ for (let v = Math.ceil(yMin); v <= Math.floor(yMax); v += yStep) { yIntTicks.push(v); }
236
+ if (yIntTicks.length === 0) { yIntTicks = [Math.round(yMin), Math.round(yMax)]; }
237
+
238
  // Grid (horizontal)
239
  gGrid.selectAll('*').remove();
 
240
  gGrid.selectAll('line')
241
+ .data(yIntTicks)
242
  .join('line')
243
  .attr('x1', 0)
244
  .attr('x2', innerWidth)
 
251
  // Axes
252
  gAxes.selectAll('*').remove();
253
  const xAxis = d3.axisBottom(xScale).ticks(8).tickSizeOuter(0);
254
+ const yAxis = d3.axisLeft(yScale).tickValues(yIntTicks).tickSizeOuter(0).tickFormat(d3.format('d'));
255
  gAxes.append('g')
256
  .attr('transform', `translate(0,${innerHeight})`)
257
  .call(xAxis)
 
278
  gAxes.append('text')
279
  .attr('class', 'axis-label axis-label--y')
280
  .attr('text-anchor', 'middle')
281
+ .attr('transform', `translate(${-44},${innerHeight/2}) rotate(-90)`)
282
  .style('font-size', '12px')
283
  .style('fill', tickColor)
284
  .text('Value');
app/src/content/embeds/d3-pie.html CHANGED
@@ -77,20 +77,20 @@
77
  }));
78
 
79
  // Layout
80
- let width=800, height=460; const margin = { top: 8, right: 24, bottom: 60, left: 24 };
81
  const CAPTION_GAP = 28; // espace entre titre et donut (augmenté)
82
  const LEGEND_GAP = 8; // espace entre donut et légende (réduit)
83
  const updateSize = () => {
84
  width = container.clientWidth || 800;
85
- height = Math.max(320, Math.round(width/2.5));
86
  svg.attr('width', width).attr('height', height);
87
  gRoot.attr('transform', `translate(${margin.left},${margin.top})`);
88
  return { innerWidth: width - margin.left - margin.right, innerHeight: height - margin.top - margin.bottom };
89
  };
90
 
91
- function renderLegend(categories, colorOf, innerWidth, innerHeight){
92
  const legendHeight = 60;
93
- gLegend.attr('x', 0).attr('y', innerHeight + LEGEND_GAP).attr('width', innerWidth).attr('height', legendHeight);
94
  const root = gLegend.selectAll('div').data([0]).join('xhtml:div');
95
  root.html(`<div class="items">${categories.map(c => `<div class="item"><span class="swatch" style="background:${colorOf(c)}"></span><span style="font-weight:500">${c}</span></div>`).join('')}</div>`);
96
  }
@@ -116,7 +116,7 @@
116
  return pool[idx % pool.length] || primary;
117
  };
118
 
119
- renderLegend(categories, colorOf, innerWidth, innerHeight);
120
 
121
  // Clear plots
122
  gPlots.selectAll('*').remove();
@@ -133,6 +133,11 @@
133
  const arc = d3.arc().innerRadius(innerR).outerRadius(radius).cornerRadius(3);
134
  const arcLabel = d3.arc().innerRadius((innerR + radius) / 2).outerRadius((innerR + radius) / 2);
135
 
 
 
 
 
 
136
  const captions = new Map(METRICS.map(m => [m.key, `${m.title}`]));
137
 
138
  METRICS.forEach((metric, idx) => {
 
77
  }));
78
 
79
  // Layout
80
+ let width=800, height=450; const margin = { top: 0, right: 24, bottom: 32, left: 24 };
81
  const CAPTION_GAP = 28; // espace entre titre et donut (augmenté)
82
  const LEGEND_GAP = 8; // espace entre donut et légende (réduit)
83
  const updateSize = () => {
84
  width = container.clientWidth || 800;
85
+ height = Math.max(260, Math.round(width/3.4));
86
  svg.attr('width', width).attr('height', height);
87
  gRoot.attr('transform', `translate(${margin.left},${margin.top})`);
88
  return { innerWidth: width - margin.left - margin.right, innerHeight: height - margin.top - margin.bottom };
89
  };
90
 
91
+ function renderLegend(categories, colorOf, innerWidth, legendY){
92
  const legendHeight = 60;
93
+ gLegend.attr('x', 0).attr('y', legendY).attr('width', innerWidth).attr('height', legendHeight);
94
  const root = gLegend.selectAll('div').data([0]).join('xhtml:div');
95
  root.html(`<div class="items">${categories.map(c => `<div class="item"><span class="swatch" style="background:${colorOf(c)}"></span><span style="font-weight:500">${c}</span></div>`).join('')}</div>`);
96
  }
 
116
  return pool[idx % pool.length] || primary;
117
  };
118
 
119
+ // Legend will be positioned after radius is known
120
 
121
  // Clear plots
122
  gPlots.selectAll('*').remove();
 
133
  const arc = d3.arc().innerRadius(innerR).outerRadius(radius).cornerRadius(3);
134
  const arcLabel = d3.arc().innerRadius((innerR + radius) / 2).outerRadius((innerR + radius) / 2);
135
 
136
+ // Now that radius is known, position legend just beneath donuts
137
+ const tempCy = innerHeight / 2;
138
+ const legendY = tempCy + radius + (CAPTION_GAP * 2);
139
+ renderLegend(categories, colorOf, innerWidth, legendY);
140
+
141
  const captions = new Map(METRICS.map(m => [m.key, `${m.title}`]));
142
 
143
  METRICS.forEach((metric, idx) => {