Add colored SCA-style flavor wheel to cupping; colorize the radar chart
Test and deploy / test-and-deploy (push) Successful in 55s
Test and deploy / test-and-deploy (push) Successful in 55s
- New flavor-wheel.js: two-ring sunburst (families inner, descriptors outer) in SCA hue families; tapping a wedge toggles the same flavor tag as the checkbox list; selections render bold with a dark outline; respects the tag limit; labels drawn above all wedges so long names never clip (screenshot-verified in headless Chromium) - Cupping radar: one hue per attribute on labels + vertex dots Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5
parent
2e1b8d24f9
commit
ec078116b4
+34
-2
@@ -2,6 +2,7 @@ import { api, protectedFetch } from "./api.js?v=__ASSET_VERSION__";
|
||||
import { initSideNav, loadNavUser } from "./nav.js?v=__ASSET_VERSION__";
|
||||
import { showToast } from "./toast.js?v=__ASSET_VERSION__";
|
||||
import { wireWhyPanels } from "./why-panels.js?v=__ASSET_VERSION__";
|
||||
import { renderFlavorWheel } from "./flavor-wheel.js?v=__ASSET_VERSION__";
|
||||
import {
|
||||
SCORE_ATTRS,
|
||||
SCORE_LABELS,
|
||||
@@ -392,11 +393,24 @@ function toggleFlavorTag(tag, checked) {
|
||||
} else {
|
||||
data.flavor_tags = data.flavor_tags.filter((t) => t !== tag);
|
||||
}
|
||||
renderWheel();
|
||||
renderFlavorFamilies();
|
||||
renderFlavorChips();
|
||||
scheduleSave();
|
||||
}
|
||||
|
||||
function renderWheel() {
|
||||
const svg = document.getElementById("flavor-wheel");
|
||||
if (!svg) return;
|
||||
renderFlavorWheel(
|
||||
svg,
|
||||
FLAVOR_TAXONOMY,
|
||||
data.flavor_tags,
|
||||
(tag, on) => toggleFlavorTag(tag, on),
|
||||
data.flavor_tags.length >= MAX_FLAVOR_TAGS,
|
||||
);
|
||||
}
|
||||
|
||||
function renderFlavorFamilies() {
|
||||
const wrap = document.getElementById("flavor-families");
|
||||
const atLimit = data.flavor_tags.length >= MAX_FLAVOR_TAGS;
|
||||
@@ -438,6 +452,8 @@ function renderFlavorFamilies() {
|
||||
// ── Radar chart (pure SVG, no dependency) ──
|
||||
const RADAR_CENTER = 120;
|
||||
const RADAR_RADIUS = 90;
|
||||
// One hue per attribute so the wheel reads as ten distinct spokes, not a grey web.
|
||||
const AXIS_COLOR = (i) => `hsl(${(i * 36 + 8) % 360} 46% 40%)`;
|
||||
|
||||
function radarPoints() {
|
||||
const n = SCORE_ATTRS.length;
|
||||
@@ -498,7 +514,8 @@ function initRadarStatic() {
|
||||
"text-anchor": "middle",
|
||||
"dominant-baseline": "middle",
|
||||
"font-size": "9",
|
||||
fill: "var(--ink-2)",
|
||||
"font-weight": "600",
|
||||
fill: AXIS_COLOR(i),
|
||||
});
|
||||
label.textContent = SCORE_LABELS[attr].split("/")[0];
|
||||
svg.append(label);
|
||||
@@ -516,7 +533,21 @@ function initRadarStatic() {
|
||||
function renderRadar() {
|
||||
const shape = document.getElementById("radar-shape");
|
||||
if (!shape) return;
|
||||
shape.setAttribute("points", radarPoints().map((p) => `${p.x},${p.y}`).join(" "));
|
||||
const points = radarPoints();
|
||||
shape.setAttribute("points", points.map((p) => `${p.x},${p.y}`).join(" "));
|
||||
// Colored vertex dots matching each axis label.
|
||||
const svg = document.getElementById("cup-radar");
|
||||
for (const old of svg.querySelectorAll("[data-vertex]")) old.remove();
|
||||
points.forEach((p, i) => {
|
||||
const dot = svgEl("circle", {
|
||||
cx: p.x,
|
||||
cy: p.y,
|
||||
r: 3.2,
|
||||
fill: AXIS_COLOR(i),
|
||||
"data-vertex": "1",
|
||||
});
|
||||
svg.append(dot);
|
||||
});
|
||||
}
|
||||
|
||||
function wireNotes() {
|
||||
@@ -553,6 +584,7 @@ async function initSessionView(user) {
|
||||
renderScoreRows();
|
||||
renderTickRows();
|
||||
renderDefectRows();
|
||||
renderWheel();
|
||||
renderFlavorFamilies();
|
||||
renderFlavorChips();
|
||||
wireNotes();
|
||||
|
||||
Reference in New Issue
Block a user