Spaces:
Running
Running
make x and y columns separately selectable
Browse files- index.html +84 -53
index.html
CHANGED
|
@@ -53,9 +53,15 @@
|
|
| 53 |
<div id="visualizationSection" class="visualization-section" style="display: none;">
|
| 54 |
<h2>Visualization</h2>
|
| 55 |
<div class="form-group">
|
| 56 |
-
<label for="
|
| 57 |
-
<select id="
|
| 58 |
-
<option value="">--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
</select>
|
| 60 |
</div>
|
| 61 |
<div id="vizContainer" class="viz-container"></div>
|
|
@@ -167,22 +173,43 @@
|
|
| 167 |
}
|
| 168 |
}
|
| 169 |
|
| 170 |
-
//
|
| 171 |
function buildVisualizationDropdown() {
|
| 172 |
-
const
|
|
|
|
| 173 |
const vizSection = document.getElementById('visualizationSection');
|
| 174 |
|
| 175 |
// Clear existing options except first
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
-
//
|
| 181 |
-
|
|
|
|
| 182 |
const xCol = columnInfo[i];
|
| 183 |
const xIsNumeric = isNumericType(xCol.type);
|
| 184 |
const xIsText = isTextType(xCol.type);
|
| 185 |
|
|
|
|
|
|
|
| 186 |
for (let j = 0; j < columnInfo.length; j++) {
|
| 187 |
if (i === j) continue; // Skip same column
|
| 188 |
|
|
@@ -190,50 +217,25 @@
|
|
| 190 |
const yIsNumeric = isNumericType(yCol.type);
|
| 191 |
const yIsText = isTextType(yCol.type);
|
| 192 |
|
| 193 |
-
// Valid combinations:
|
| 194 |
-
// 1. Numeric x Numeric (scatter plot)
|
| 195 |
-
// 2. Numeric x Text (scatter plot with text on one axis)
|
| 196 |
-
// 3. Text x Numeric (scatter plot with text on one axis)
|
| 197 |
-
// Skip: Text x Text
|
| 198 |
-
|
| 199 |
if ((xIsNumeric && yIsNumeric) ||
|
| 200 |
(xIsNumeric && yIsText) ||
|
| 201 |
(xIsText && yIsNumeric)) {
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
let chartType = 'scatter';
|
| 205 |
-
|
| 206 |
-
validCombinations.push({
|
| 207 |
-
xCol: xCol.name,
|
| 208 |
-
xType: xCol.type,
|
| 209 |
-
yCol: yCol.name,
|
| 210 |
-
yType: yCol.type,
|
| 211 |
-
chartType: chartType
|
| 212 |
-
});
|
| 213 |
-
|
| 214 |
-
const option = document.createElement('option');
|
| 215 |
-
option.value = JSON.stringify({ xCol: xCol.name, yCol: yCol.name, chartType });
|
| 216 |
-
option.textContent = `${xCol.name} (${xCol.type}) × ${yCol.name} (${yCol.type}) - ${chartType}`;
|
| 217 |
-
vizSelect.appendChild(option);
|
| 218 |
-
} else if (!xIsNumeric && !xIsText && !yIsNumeric && !yIsText) {
|
| 219 |
-
// Other unsupported type combinations - we'll handle error when selected
|
| 220 |
-
validCombinations.push({
|
| 221 |
-
xCol: xCol.name,
|
| 222 |
-
xType: xCol.type,
|
| 223 |
-
yCol: yCol.name,
|
| 224 |
-
yType: yCol.type,
|
| 225 |
-
chartType: 'unsupported'
|
| 226 |
-
});
|
| 227 |
-
|
| 228 |
-
const option = document.createElement('option');
|
| 229 |
-
option.value = JSON.stringify({ xCol: xCol.name, yCol: yCol.name, chartType: 'unsupported' });
|
| 230 |
-
option.textContent = `${xCol.name} (${xCol.type}) × ${yCol.name} (${yCol.type}) - unsupported`;
|
| 231 |
-
vizSelect.appendChild(option);
|
| 232 |
}
|
| 233 |
}
|
| 234 |
}
|
| 235 |
|
| 236 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
vizSection.style.display = 'block';
|
| 238 |
} else {
|
| 239 |
vizSection.style.display = 'none';
|
|
@@ -343,13 +345,42 @@
|
|
| 343 |
}
|
| 344 |
});
|
| 345 |
|
| 346 |
-
//
|
| 347 |
-
|
| 348 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 349 |
|
| 350 |
-
if (
|
| 351 |
-
|
| 352 |
-
await renderVisualization(xCol, yCol, chartType);
|
| 353 |
}
|
| 354 |
});
|
| 355 |
|
|
|
|
| 53 |
<div id="visualizationSection" class="visualization-section" style="display: none;">
|
| 54 |
<h2>Visualization</h2>
|
| 55 |
<div class="form-group">
|
| 56 |
+
<label for="xColSelect">X Axis</label>
|
| 57 |
+
<select id="xColSelect">
|
| 58 |
+
<option value="">-- Select X column --</option>
|
| 59 |
+
</select>
|
| 60 |
+
</div>
|
| 61 |
+
<div class="form-group">
|
| 62 |
+
<label for="yColSelect">Y Axis</label>
|
| 63 |
+
<select id="yColSelect">
|
| 64 |
+
<option value="">-- Select Y column --</option>
|
| 65 |
</select>
|
| 66 |
</div>
|
| 67 |
<div id="vizContainer" class="viz-container"></div>
|
|
|
|
| 173 |
}
|
| 174 |
}
|
| 175 |
|
| 176 |
+
// Populate X and Y axis dropdowns with columns
|
| 177 |
function buildVisualizationDropdown() {
|
| 178 |
+
const xColSelect = document.getElementById('xColSelect');
|
| 179 |
+
const yColSelect = document.getElementById('yColSelect');
|
| 180 |
const vizSection = document.getElementById('visualizationSection');
|
| 181 |
|
| 182 |
// Clear existing options except first
|
| 183 |
+
xColSelect.innerHTML = '<option value="">-- Select X column --</option>';
|
| 184 |
+
yColSelect.innerHTML = '<option value="">-- Select Y column --</option>';
|
| 185 |
+
|
| 186 |
+
// Populate both dropdowns with all visualizable columns (numeric or text)
|
| 187 |
+
columnInfo.forEach(col => {
|
| 188 |
+
const isNumeric = isNumericType(col.type);
|
| 189 |
+
const isText = isTextType(col.type);
|
| 190 |
+
|
| 191 |
+
if (isNumeric || isText) {
|
| 192 |
+
const xOption = document.createElement('option');
|
| 193 |
+
xOption.value = col.name;
|
| 194 |
+
xOption.textContent = `${col.name} (${col.type})`;
|
| 195 |
+
xColSelect.appendChild(xOption);
|
| 196 |
+
|
| 197 |
+
const yOption = document.createElement('option');
|
| 198 |
+
yOption.value = col.name;
|
| 199 |
+
yOption.textContent = `${col.name} (${col.type})`;
|
| 200 |
+
yColSelect.appendChild(yOption);
|
| 201 |
+
}
|
| 202 |
+
});
|
| 203 |
|
| 204 |
+
// Find first valid pair and pre-select it
|
| 205 |
+
let firstValidPair = null;
|
| 206 |
+
for (let i = 0; i < columnInfo.length && !firstValidPair; i++) {
|
| 207 |
const xCol = columnInfo[i];
|
| 208 |
const xIsNumeric = isNumericType(xCol.type);
|
| 209 |
const xIsText = isTextType(xCol.type);
|
| 210 |
|
| 211 |
+
if (!xIsNumeric && !xIsText) continue;
|
| 212 |
+
|
| 213 |
for (let j = 0; j < columnInfo.length; j++) {
|
| 214 |
if (i === j) continue; // Skip same column
|
| 215 |
|
|
|
|
| 217 |
const yIsNumeric = isNumericType(yCol.type);
|
| 218 |
const yIsText = isTextType(yCol.type);
|
| 219 |
|
| 220 |
+
// Valid combinations: at least one must be numeric
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
if ((xIsNumeric && yIsNumeric) ||
|
| 222 |
(xIsNumeric && yIsText) ||
|
| 223 |
(xIsText && yIsNumeric)) {
|
| 224 |
+
firstValidPair = { x: xCol.name, y: yCol.name };
|
| 225 |
+
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
}
|
| 227 |
}
|
| 228 |
}
|
| 229 |
|
| 230 |
+
if (firstValidPair) {
|
| 231 |
+
// Pre-select the first valid pair
|
| 232 |
+
xColSelect.value = firstValidPair.x;
|
| 233 |
+
yColSelect.value = firstValidPair.y;
|
| 234 |
+
vizSection.style.display = 'block';
|
| 235 |
+
|
| 236 |
+
// Render the visualization immediately
|
| 237 |
+
renderVisualization(firstValidPair.x, firstValidPair.y, 'scatter');
|
| 238 |
+
} else if (columnInfo.length > 0) {
|
| 239 |
vizSection.style.display = 'block';
|
| 240 |
} else {
|
| 241 |
vizSection.style.display = 'none';
|
|
|
|
| 345 |
}
|
| 346 |
});
|
| 347 |
|
| 348 |
+
// Check if selected columns form a valid combination and render
|
| 349 |
+
function isValidCombination(xCol, yCol) {
|
| 350 |
+
if (!xCol || !yCol || xCol === yCol) return false;
|
| 351 |
+
|
| 352 |
+
const xColInfo = columnInfo.find(c => c.name === xCol);
|
| 353 |
+
const yColInfo = columnInfo.find(c => c.name === yCol);
|
| 354 |
+
|
| 355 |
+
if (!xColInfo || !yColInfo) return false;
|
| 356 |
+
|
| 357 |
+
const xIsNumeric = isNumericType(xColInfo.type);
|
| 358 |
+
const xIsText = isTextType(xColInfo.type);
|
| 359 |
+
const yIsNumeric = isNumericType(yColInfo.type);
|
| 360 |
+
const yIsText = isTextType(yColInfo.type);
|
| 361 |
+
|
| 362 |
+
// At least one must be numeric, and both must be numeric or text
|
| 363 |
+
return ((xIsNumeric || xIsText) && (yIsNumeric || yIsText) &&
|
| 364 |
+
(xIsNumeric || yIsNumeric));
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
+
// Handle X axis selection
|
| 368 |
+
document.getElementById('xColSelect').addEventListener('change', async function(e) {
|
| 369 |
+
const xCol = e.target.value;
|
| 370 |
+
const yCol = document.getElementById('yColSelect').value;
|
| 371 |
+
|
| 372 |
+
if (isValidCombination(xCol, yCol)) {
|
| 373 |
+
await renderVisualization(xCol, yCol, 'scatter');
|
| 374 |
+
}
|
| 375 |
+
});
|
| 376 |
+
|
| 377 |
+
// Handle Y axis selection
|
| 378 |
+
document.getElementById('yColSelect').addEventListener('change', async function(e) {
|
| 379 |
+
const yCol = e.target.value;
|
| 380 |
+
const xCol = document.getElementById('xColSelect').value;
|
| 381 |
|
| 382 |
+
if (isValidCombination(xCol, yCol)) {
|
| 383 |
+
await renderVisualization(xCol, yCol, 'scatter');
|
|
|
|
| 384 |
}
|
| 385 |
});
|
| 386 |
|