Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,29 +72,43 @@ <h3 class="entity-popup__group">
<button type="button" (click)="open(row)">
<!-- With an analysis running, every molecule says whether it
was in it: otherwise a complex whose components are
half hit looks the same as one nothing touched. -->
@if (row.found !== undefined) {
<span
class="row-hit"
[class.row-hit--found]="row.found"
[matTooltip]="row.found ? 'In your analysis' : 'Not in your analysis'"
></span>
}
<span class="row-label" [class.row-label--missed]="row.found === false">{{ row.label }}</span>
half hit looks the same as one nothing touched.
The marker sits on the label's own line: stacked above
it, curators read straight past it and reported the
indication as missing. -->
<span class="row-head">
@if (row.found !== undefined) {
<span
class="row-hit"
[class.row-hit--found]="row.found"
[matTooltip]="row.found ? 'In your analysis' : 'Not in your analysis'"
></span>
}
<span class="row-label" [class.row-label--missed]="row.found === false">{{ row.label }}</span>
</span>
@if (row.expression?.length) {
<span class="row-expression">
@for (value of row.expression; track $index) {
<!-- One value per sample, as the GWT panel shows one
column per sample. The one the rest of the
browser is displaying is the emphasised one. -->
column per sample. The sample's name is here
rather than in a tooltip only: three bare
numbers say nothing about which tissue is
which. The one the rest of the browser is
displaying is the emphasised one. -->
<span
class="row-expression__value"
[class.row-expression__value--current]="$index === selectedSample()"
[matTooltip]="sampleNames()[$index] ?? 'Sample ' + ($index + 1)"
>{{ value | number: '1.0-2' }}</span
>
<span class="row-expression__name">{{
sampleNames()[$index] ?? 'Sample ' + ($index + 1)
}}</span>
{{ value | number: '1.0-2' }}
</span>
}
</span>
} @else if (row.found === false) {
<!-- Said in words, not only by an unfilled dot. -->
<span class="row-missed">not in your data</span>
}
@if (row.detail) {
<span class="row-detail">{{ row.detail }}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,43 +209,80 @@
opacity: 0.7;
}

// The marker and the name it belongs to, on one line. The button is a column,
// so without this the dot sits on a line of its own above the label -- which is
// how a marker that is present gets reported as missing.
.row-head {
display: flex;
align-items: center;
gap: 0.4rem;
}

// Analysis markers. A dot rather than a colour on the label: the rows already
// carry type icons and colour, and this has to read at a glance next to them.
.row-hit {
flex: none;
width: 7px;
height: 7px;
width: 9px;
height: 9px;
border-radius: 50%;
border: 1px solid var(--outline, rgb(0 0 0 / 35%));
border: 1px solid var(--outline, rgb(0 0 0 / 45%));
background: transparent;
}

.row-hit--found {
// The same green the diagram uses for a hit, so the popup and the diagram
// agree about what "found" looks like.
// agree about what "found" looks like. The ring is what separates it from an
// empty dot at a glance rather than on inspection.
background: var(--analysis-hit, #2e7d32);
border-color: var(--analysis-hit, #2e7d32);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--analysis-hit, #2e7d32) 28%, transparent);
}

.row-label--missed {
opacity: 0.55;
}

// Said in words. An unfilled dot is the whole signal otherwise, and on a row
// with no values beside it there is nothing else to read.
.row-missed {
margin-left: 1.3rem;
font-size: 0.68rem;
font-style: italic;
opacity: 0.6;
}

.row-expression {
margin-left: auto;
display: inline-flex;
gap: 0.35rem;
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
margin: 0.15rem 0 0 1.3rem;
font-family: 'Roboto Mono', ui-monospace, monospace;
font-size: 0.72rem;
font-size: 0.7rem;
}

// One chip per sample, carrying its own name: three bare numbers cannot say
// which tissue is which, and the name was in a tooltip nobody hovers.
.row-expression__value {
opacity: 0.5;
display: inline-flex;
align-items: baseline;
gap: 0.25rem;
padding: 0.05rem 0.3rem;
border-radius: 999px;
background: color-mix(in srgb, currentColor 8%, transparent);
opacity: 0.75;
}

.row-expression__name {
font-family: inherit;
font-size: 0.62rem;
letter-spacing: 0.02em;
opacity: 0.75;
}

// The sample the diagram is currently coloured by, so the number you are
// looking at on the canvas is the one that stands out here.
.row-expression__value--current {
opacity: 1;
font-weight: 600;
background: color-mix(in srgb, currentColor 16%, transparent);
}
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,22 @@ export class EntityPopupComponent {
readonly sampleNames = computed(() => this.analysis.samples());

/** The sample the rest of the browser is currently showing. */
readonly selectedSample = computed(() => Number(this.state.sample() ?? 0));
/**
* Which sample the diagram is currently coloured by, as an index into the
* values on a row.
*
* `state.sample` holds the column's *name* -- `samples()[index]` is what sets
* it -- so reading it as a number gave NaN for every real analysis, and the
* value a reader was actually looking at on the canvas was never the one
* emphasised here. It only ever appeared to work when no sample was set,
* where the fallback happened to be the first column anyway.
*/
readonly selectedSample = computed(() => {
const name = this.state.sample();
if (!name) return 0;
const at = this.sampleNames().indexOf(name);
return at === -1 ? 0 : at;
});

/** Group components by molecule type, in a stable order, as production does. */
private group(components: any[], refs: Map<string, ComponentRef>): PopupGroup[] {
Expand Down
Loading