|
8 | 8 |
|
9 | 9 | <!-- String input section --> |
10 | 10 | <div class="mt-4"> |
11 | | - |
12 | 11 | <app-cohort-metadata |
13 | 12 | [cohortData]="cohortData()!" |
14 | 13 | (resetCohortMetadata)="handleReset()" |
15 | 14 | (updateAcronym)="handleAcronym($any($event))" |
16 | 15 | (updateMoi)="handleMoi($any($event))"> |
17 | | -</app-cohort-metadata> |
| 16 | + </app-cohort-metadata> |
18 | 17 |
|
19 | | -<div class="mt-4 flex items-center gap-2"> |
20 | | - <label for="hpoFilter" class="text-sm text-gray-700">Filter HPO columns:</label> |
21 | | - <select |
22 | | - id="hpoFilter" |
23 | | - class="border rounded p-1 text-sm" |
24 | | - [value]="selectedTopLevelHpo()" |
25 | | - (change)="selectedTopLevelHpo.set($any($event.target).value)" |
26 | | -> |
27 | | - <option value="">Show all</option> |
28 | | - @for (groupKey of hpoGroupKeys(); track groupKey) { |
29 | | - <option [value]="groupKey">{{ groupKey }}</option> |
30 | | - } |
31 | | -</select> |
32 | | -<app-help-button |
33 | | - title="Filter HPO columns" |
34 | | - [lines]="[ |
35 | | - 'Filter to the columns shown in the display to the indicated top-level HPO category.', |
36 | | - 'The categories correspond to top-level HPO terms (direct children of <i>Phenotypic abnormality</i>).', |
37 | | - 'Choose <i>Show all</i> to restore the original view.' |
38 | | - ]" /> |
39 | | -</div> |
| 18 | + <div class="mt-4 flex items-center gap-2"> |
| 19 | + <label for="hpoFilter" class="text-sm text-gray-700">Filter HPO columns:</label> |
| 20 | + <select |
| 21 | + id="hpoFilter" |
| 22 | + class="border rounded p-1 text-sm" |
| 23 | + [value]="selectedTopLevelHpo()" |
| 24 | + (change)="selectedTopLevelHpo.set($any($event.target).value)" |
| 25 | + > |
| 26 | + <option value="">Show all</option> |
| 27 | + @for (groupKey of hpoGroupKeys(); track groupKey) { |
| 28 | + <option [value]="groupKey">{{ groupKey }}</option> |
| 29 | + } |
| 30 | + </select> |
| 31 | + <app-help-button |
| 32 | + title="Filter HPO columns" |
| 33 | + [lines]="[ |
| 34 | + 'Filter to the columns shown in the display to the indicated top-level HPO category.', |
| 35 | + 'The categories correspond to top-level HPO terms (direct children of <i>Phenotypic abnormality</i>).', |
| 36 | + 'Choose <i>Show all</i> to restore the original view.' |
| 37 | + ]" /> |
| 38 | + </div> |
40 | 39 |
|
41 | 40 | <!-- Table with one row per phenopacket --> |
42 | 41 | @if (cohortData(); as cohort){ |
43 | 42 | <div class="pt-table-container"> |
44 | 43 | <div class="top-scroll-mirror overflow-x-auto invisible-scrollbar"> |
45 | | - <div class="h-1" [style.width]="tableWidth"></div> |
46 | | - </div> |
47 | | - <div class="overflow-y-auto max-h-[600px] max-w-7xl"> |
48 | | - <table class="pt-table"> |
49 | | - <thead class="sticky top-0 z-10 bg-white shadow-sm border-b border-gray-200"> |
50 | | - <tr> |
51 | | - <th class="narrow-column bottom-align">Individual ID</th> |
52 | | - @if(showAlleleColumn){ |
53 | | - <th class="narrow-column bottom-align"> |
54 | | - Alleles |
55 | | - </th> |
56 | | - } |
57 | | - <!-- The following creates the series of headers for the HPO columns. |
58 | | - The function shouldDisplayHpoColumn allows the user to focus on 1-10 columns at a time--> |
59 | | - @if (cohort) { |
60 | | - @for (header of cohort.hpoHeaders; track header.hpoId; let i = $index) { |
61 | | - @if (visibleColumnMask()[i]) { |
62 | | - <th |
63 | | - class="rotate-header hpo-col hpo-col-th" |
64 | | - (mouseenter)="onHeaderMouseEnter(i)" |
65 | | - (mouseleave)="onHeaderMouseLeave()" |
66 | | - > |
67 | | - <div class="rotated-text" [ngClass]="{ 'hover-underline': true }"> |
68 | | - {{ header.hpoLabel }} |
69 | | - </div> |
70 | | - <!-- Popup shown on hover --> |
71 | | - @if(isHeaderHovered(i)) { |
72 | | - <div class="hpo-popup"> |
73 | | - <a |
74 | | - [href]="'https://hpo.jax.org/browse/term/' + header.hpoId" |
75 | | - target="_blank" |
76 | | - rel="noopener" |
77 | | - > |
78 | | - 🔗 {{ header.hpoId }} |
79 | | - </a> |
| 44 | + <div class="h-1" [style.width]="tableWidth"></div> |
| 45 | + </div> |
| 46 | + <div class="overflow-y-auto max-h-[600px] max-w-7xl"> |
| 47 | + <table class="pt-table"> |
| 48 | + <thead class="sticky top-0 z-10 bg-white shadow-sm border-b border-gray-200"> |
| 49 | + <tr> |
| 50 | + <th class="narrow-column bottom-align">Individual ID</th> |
| 51 | + |
| 52 | + <th class="narrow-column bottom-align"> |
| 53 | + Alleles |
| 54 | + </th> |
| 55 | + <!-- The following creates the series of headers for the HPO columns. |
| 56 | + The function shouldDisplayHpoColumn allows the user to focus on 1-10 columns at a time--> |
| 57 | + @if (cohort) { |
| 58 | + @for (header of cohort.hpoHeaders; track header.hpoId; let i = $index) { |
| 59 | + @if (visibleColumnMask()[i]) { |
| 60 | + <th |
| 61 | + class="rotate-header hpo-col hpo-col-th" |
| 62 | + (mouseenter)="onHeaderMouseEnter(i)" |
| 63 | + (mouseleave)="onHeaderMouseLeave()" |
| 64 | + > |
| 65 | + <div class="rotated-text" [ngClass]="{ 'hover-underline': true }"> |
| 66 | + {{ header.hpoLabel }} |
80 | 67 | </div> |
81 | | - } |
82 | | - </th> |
| 68 | + <!-- Popup shown on hover --> |
| 69 | + @if(isHeaderHovered(i)) { |
| 70 | + <div class="hpo-popup"> |
| 71 | + <a |
| 72 | + [href]="'https://hpo.jax.org/browse/term/' + header.hpoId" |
| 73 | + target="_blank" |
| 74 | + rel="noopener" |
| 75 | + > |
| 76 | + 🔗 {{ header.hpoId }} |
| 77 | + </a> |
| 78 | + </div> |
| 79 | + } |
| 80 | + </th> |
| 81 | + } |
83 | 82 | } |
84 | 83 | } |
85 | | - } |
86 | | - </tr> |
87 | | - </thead> |
88 | | - <tbody> |
| 84 | + </tr> |
| 85 | + </thead> |
| 86 | + <tbody> |
89 | 87 | @for (row of tableVM(); track row.id; let rowIndex = $index) { |
90 | 88 | <tr [class.bg-red-50]="!row.hasObservedHpo"> |
91 | 89 | <!-- First column: PMID/title/individual_id/comment --> |
|
136 | 134 | </td> |
137 | 135 |
|
138 | 136 | <!-- Second column for gene/allele --> |
139 | | - @if(showAlleleColumn){ |
140 | | - <td class="relative narrow-column"> |
141 | | - @if (row.alleles.length === 0) { |
142 | | - <button |
143 | | - class="allele-btn allele-btn-add" |
144 | | - title="Add allele" |
145 | | - (click)="toggleAddAllelePopover(row.id, $event)"> |
146 | | - + |
147 | | - </button> |
148 | | - } |
149 | | - @for (allele of row.alleles; track allele.key) { |
150 | | - <div class="allele-row flex items-center gap-x-3"> |
151 | | - <div class="flex items-center gap-x-1 allele-content"> |
| 137 | + |
| 138 | + |
| 139 | +<td class="relative narrow-column"> |
| 140 | + @if (row.alleles.length === 0) { |
| 141 | + <button cdkOverlayOrigin #originEmpty="cdkOverlayOrigin" |
| 142 | + class="allele-btn allele-btn-add" (click)="openAddAlleleRowId = row.id">+</button> |
| 143 | + |
| 144 | + <app-popover [trigger]="originEmpty" [isOpen]="openAddAlleleRowId === row.id"> |
| 145 | + <ng-container *ngTemplateOutlet="alleleMenuContent"></ng-container> |
| 146 | + </app-popover> |
| 147 | + } |
| 148 | + @for (allele of row.alleles; track allele.key) { |
| 149 | + <div class="allele-row flex items-center gap-x-3"> |
| 150 | + <div class="flex items-center gap-x-1 allele-content"> |
152 | 151 | <span |
153 | 152 | class="inline-block w-3 h-3 rounded-full" |
154 | 153 | [ngClass]="allele.validated ? 'bg-green-500' : 'bg-red-500'" |
|
158 | 157 | {{ allele.shortDisplay }} |
159 | 158 | </span> |
160 | 159 | </div> |
161 | | - |
162 | | - <!-- Right side: actions --> |
163 | | - <div class="flex items-center gap-x-2"> |
164 | | - <div class="allele-count-grid"> |
165 | | - <button |
| 160 | + <div class="allele-count-grid"> |
| 161 | + <button |
166 | 162 | class="allele-btn allele-btn-count" |
167 | 163 | [ngClass]="{ 'selected-count': allele.count === 1 }" |
168 | 164 | (click)="onAlleleCountChange(allele.key, row.id, 1)">1</button> |
|
172 | 168 | <button |
173 | 169 | class="allele-btn allele-btn-delete" |
174 | 170 | (click)="onAlleleCountChange(allele.key, row.id, 0)">🗑️</button> |
175 | | - <button |
176 | | - class="allele-btn allele-btn-add" |
177 | | - title="Add allele" |
178 | | - (click)="toggleAddAllelePopover(row.id, $event)"> |
179 | | - + |
180 | | - </button> |
181 | | - </div> |
182 | | - </div> |
183 | | - </div> |
184 | | - } <!-- for gene --> |
185 | | - <!-- Popover --> |
186 | | - @if(openAddAlleleRowId === row.id) { |
187 | | - @if (openAddAlleleRowId === row.id) { |
188 | | - <div |
189 | | - class="absolute bg-white border border-gray-300 shadow-md rounded-lg p-2 z-50" |
190 | | - style="top: 100%; right: 0; width: 220px;" |
191 | | - > |
192 | | - <div class="grid grid-cols-2 gap-2"> |
193 | | - <button |
194 | | - type="button" |
195 | | - class="allele-tile" |
196 | | - (click)="addAllele(row.id, VariantKind.HGVS); closeAddAllelePopover()" |
197 | | - > |
198 | | - HGVS |
199 | | - </button> |
200 | | - |
201 | | - <button |
202 | | - type="button" |
203 | | - class="allele-tile" |
204 | | - (click)="addAllele(row.id, VariantKind.SV); closeAddAllelePopover()" |
205 | | - > |
206 | | - SV |
207 | | - </button> |
208 | | - |
209 | | - <button |
210 | | - type="button" |
211 | | - class="allele-tile" |
212 | | - (click)="addAllele(row.id, VariantKind.INTERGENIC); closeAddAllelePopover()" |
213 | | - > |
214 | | - Intergenic |
215 | | - </button> |
216 | | - |
217 | | - <button |
218 | | - type="button" |
219 | | - class="allele-tile" |
220 | | - (click)="addAllele(row.id, VariantKind.INTERGENIC); closeAddAllelePopover()" |
221 | | - > |
222 | | - Mitochondrial |
223 | | - </button> |
224 | | - </div> |
| 171 | + <button cdkOverlayOrigin #originList="cdkOverlayOrigin" |
| 172 | + class="allele-btn allele-btn-add" (click)="openAddAlleleRowId = row.id">+</button> |
| 173 | + <app-popover [trigger]="originList" [isOpen]="openAddAlleleRowId === row.id"> |
| 174 | + <ng-container *ngTemplateOutlet="alleleMenuContent"></ng-container> |
| 175 | + </app-popover> |
225 | 176 | </div> |
226 | | - } |
| 177 | + </div> |
227 | 178 | } |
228 | | - </td> |
229 | | - }<!-- isShowAllele --> |
| 179 | + |
| 180 | + <ng-template #alleleMenuContent> |
| 181 | + <div class="grid grid-cols-2 gap-2 w-[220px]"> |
| 182 | + <button type="button" class="allele-tile" (click)="addAllele(row.id, VariantKind.HGVS); openAddAlleleRowId = null">HGVS</button> |
| 183 | + <button type="button" class="allele-tile" (click)="addAllele(row.id, VariantKind.SV); openAddAlleleRowId = null">SV</button> |
| 184 | + <button type="button" class="allele-tile" (click)="addAllele(row.id, VariantKind.INTERGENIC); openAddAlleleRowId = null">Intergenic</button> |
| 185 | + <button type="button" class="allele-tile" (click)="addAllele(row.id, VariantKind.SV); openAddAlleleRowId = null">Mito</button> |
| 186 | + </div> |
| 187 | + </ng-template> |
| 188 | +</td> |
230 | 189 |
|
231 | 190 | <!-- The values of the HPO columns--> |
232 | 191 | @for (cell of row.hpoCells; track $index; let hpoIndex = $index) { |
|
0 commit comments