diff --git a/app/globals.css b/app/globals.css index 560de628..d6c55847 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1237,14 +1237,6 @@ details.cohortpick .gearmenu-items { min-width: 22rem; max-width: 30rem; gap: 0; /* a plot's axis controls: source, then whatever that source needs */ .axispick { display: inline-flex; align-items: center; gap: var(--s1); } -/* a plot's highlight cohorts: one chip each, in the figure's colour for it */ -.hlpick { display: inline-flex; align-items: center; gap: var(--s1); flex-wrap: wrap; min-width: 0; } -.hlchip { - display: inline-flex; align-items: center; gap: 4px; height: var(--ctl-h); padding: 0 2px 0 var(--s1); - border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--paper); font-size: var(--t-sm); color: var(--ink); -} -.hldot { width: 9px; height: 9px; border-radius: 50%; flex: none; } -.hlname { max-width: 9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* a server-rendered figure inside a view card: it takes the card's width, and scrolls sideways only if it truly cannot fit */ diff --git a/app/lab/explore-charts.tsx b/app/lab/explore-charts.tsx index 921f0094..f851efab 100644 --- a/app/lab/explore-charts.tsx +++ b/app/lab/explore-charts.tsx @@ -47,7 +47,7 @@ export function ExploreCharts({ studies, draft, attrs, endpoints, addClauses, km const cohort: CohortDef = { ...(studies.length > 1 ? { studies } : {}), all: (draft ? draft.all : []) as CohortDef["all"] }; const views: View[] = []; if (ep) views.push({ kind: "survival", endpoint: ep }); - if (hasScatter) views.push({ kind: "plot", unit: "sample", x: { source: "clinical", attribute: X_ATTR, log: true }, y: { source: "clinical", attribute: Y_ATTR }, bins: [48, 32] }); + if (hasScatter) views.push({ kind: "plot", unit: "sample", x: { source: "clinical", attribute: X_ATTR, log: true }, y: { source: "clinical", attribute: Y_ATTR }, bins: [48, 32], allPoints: false, points: [] }); // a superseded request is cancelled, so the server stops its queries instead of finishing them const ac = new AbortController(); api.analysis({ study: studies[0], cohorts: { Selected: cohort }, views }, { signal: ac.signal }) diff --git a/app/lab/plot-controls.tsx b/app/lab/plot-controls.tsx index 6843214f..ec47ec66 100644 --- a/app/lab/plot-controls.tsx +++ b/app/lab/plot-controls.tsx @@ -1,13 +1,19 @@ "use client"; -// The plot view's settings bar, shared by the lab's plot card and the portal pages' Plots tab: -// both axes (with a log scale for a numeric one), a box plot's colour and ring, the highlighted -// cohorts, the unit and the axis swap. Every control is an edit to the view, handed to `update`. -import { ArrowLeftRight } from "lucide-react"; -import { AxisPicker, HighlightPicker } from "./view-chrome.tsx"; +// The plot view laid out as cBioPortal's Plots tab is, shared by the lab's plot card and the portal +// pages' Plots tab: each axis defined in its own block in a sidebar (horizontal above vertical, a swap +// between them, the unit and the point layers below), and beside it a line saying how many cases are shown, +// a box plot's colour and ring, and the figure. Under a narrow card the sidebar sits above the figure. +// Every control is an edit to the view, handed to `update`. +import { useEffect, useState } from "react"; +import { ArrowDown, ArrowUp, X } from "lucide-react"; +import { AxisPicker } from "./view-chrome.tsx"; import { Button } from "../_ui/button"; import { Checkbox } from "../_ui/checkbox"; -import { Choice } from "../_ui/choice.tsx"; -import { TB } from "../_ui/toolbar.ts"; +import { Choice, OptionSelect } from "../_ui/choice.tsx"; +import { Input } from "../_ui/input"; +import { cn } from "../../lib/cn"; +import { FIELD } from "../_ui/toolbar.ts"; +import { DEFAULT_POINT_LAYERS, POINTS_CAP, LAYER_COLORS, POINT_LAYER_CAP, POINT_RULE_N, POINT_RULE_N_MAX, layerLabel, type PointLayerSpec } from "../../lib/plot-layers.ts"; type Update = (fn: (view: any) => any) => void; @@ -15,68 +21,182 @@ function LogToggle({ spec, side, numeric, busy, update }: { spec: any; side: "x" const axis = spec[side]; if (!numeric || !axis || !("source" in axis) || (axis.source !== "clinical" && axis.source !== "molecular")) return null; return ( -