From 0637616c359748455a43637528df561b81527f20 Mon Sep 17 00:00:00 2001 From: el-rabies Date: Wed, 1 Apr 2026 16:17:29 -0400 Subject: [PATCH 1/2] feat / fix: Added missing fields to search results --- .../community/icon-lib/icon-lib.component.ts | 2 +- .../src/app/search/search.component.html | 35 +++++++++++++++---- .../src/app/search/search.component.scss | 12 +++++++ .../src/app/search/search.component.ts | 12 +++++++ 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/projects/website-angular/src/app/community/icon-lib/icon-lib.component.ts b/projects/website-angular/src/app/community/icon-lib/icon-lib.component.ts index d5d75d0..32b6dda 100644 --- a/projects/website-angular/src/app/community/icon-lib/icon-lib.component.ts +++ b/projects/website-angular/src/app/community/icon-lib/icon-lib.component.ts @@ -49,7 +49,7 @@ interface ParsedReference { @Component({ selector: 'app-icon-lib', - imports: [PageLayoutComponent, RouterLink], + imports: [PageLayoutComponent], templateUrl: './icon-lib.component.html', styleUrl: './icon-lib.component.scss' }) diff --git a/projects/website-angular/src/app/search/search.component.html b/projects/website-angular/src/app/search/search.component.html index 512355f..f1ecd67 100644 --- a/projects/website-angular/src/app/search/search.component.html +++ b/projects/website-angular/src/app/search/search.component.html @@ -280,7 +280,11 @@

{{ group.typeName }} ({{ group.entriesCount }})

@for (entry of group.entries; track entry.dbId) {
-
+
+ TODO: Add type icons +
+ } + @if (entry.iconCuratorName) { +
Curator: + {{ entry.iconCuratorName }} +
+ } + @if (entry.iconDesignerName) { +
Designer: + {{ entry.iconDesignerName }} +
+ } @if (entry.summation) {

} - @if (entry.referenceName) { + @if (entry.referenceName || entry.databaseName) {
- Reference: ({{ entry.referenceIdentifier }}) + Reference: ({{ entry.referenceIdentifier }})
} + +
+ @if (entry.exactType == "Icon") { +
+ +
+ }
- } + }
} @@ -343,12 +364,12 @@

{{ group.typeName }} ({{ group.entriesCount }})

- {{ entry.stId }} +
@if (entry.species?.length) {
@for (sp of entry.species; track sp; let last = $last) { - {{ sp }} + }
} diff --git a/projects/website-angular/src/app/search/search.component.scss b/projects/website-angular/src/app/search/search.component.scss index b512da3..863f264 100644 --- a/projects/website-angular/src/app/search/search.component.scss +++ b/projects/website-angular/src/app/search/search.component.scss @@ -243,7 +243,15 @@ $border-radius: 8px; margin-top: 16px; } +.type-icon { + width: 10px; +} + .search-entry { + display: flex; + flex-direction: row; + align-items: center; + padding: 12px 0; border-top: 1px solid rgba(0, 0, 0, 0.08); @@ -252,6 +260,10 @@ $border-radius: 8px; } } +.entry-info { + flex: 1; +} + .entry-header { display: flex; align-items: baseline; diff --git a/projects/website-angular/src/app/search/search.component.ts b/projects/website-angular/src/app/search/search.component.ts index 6d10fc0..025d116 100644 --- a/projects/website-angular/src/app/search/search.component.ts +++ b/projects/website-angular/src/app/search/search.component.ts @@ -17,6 +17,7 @@ import { } from '../../services/search.service'; import { DatePipe } from '@angular/common'; import { MatIcon } from "@angular/material/icon"; +import { IconEntry } from '../../services/icon.service'; @Component({ selector: 'app-search', @@ -257,6 +258,14 @@ export class SearchComponent implements OnInit, OnDestroy, AfterViewInit { return of(null); } + printEntry(entry: SearchEntry): void { + console.log('Entry details:', entry); + } + + iconSvgUrl(entry: SearchEntry): string { + return `https://dev.reactome.org/icon/${entry.stId}.svg`; + } + get allEntries(): SearchEntry[] { if (!this.results?.results) return []; return this.results.results.flatMap(g => this.filterDeletedEntries(g.entries)); @@ -341,6 +350,9 @@ export class SearchComponent implements OnInit, OnDestroy, AfterViewInit { } getDetailLink(entry: SearchEntry): string { + //Remove HTML tags from entry.stId if present + entry.stId = entry.stId?.replace(/<[^>]*>/g, ''); + if (entry.exactType === 'Interactor') return '/content/detail/interactor/' + entry.stId; if (entry.exactType === 'Icon') return '/content/detail/icon/' + entry.stId; return '/content/detail/' + entry.stId; From 9ba61cf1dfe07b368ea088c4b95b1b935684de6b Mon Sep 17 00:00:00 2001 From: el-rabies Date: Tue, 7 Apr 2026 15:13:02 -0400 Subject: [PATCH 2/2] feat: Added Search Result Type Icons --- .../src/app/search/search.component.html | 3 +- .../src/app/search/search.component.scss | 269 +++++++++++++++++- .../src/app/search/search.component.ts | 17 +- .../src/assets/spritesheet.png | Bin 0 -> 8018 bytes 4 files changed, 284 insertions(+), 5 deletions(-) create mode 100644 projects/website-angular/src/assets/spritesheet.png diff --git a/projects/website-angular/src/app/search/search.component.html b/projects/website-angular/src/app/search/search.component.html index f1ecd67..8e11300 100644 --- a/projects/website-angular/src/app/search/search.component.html +++ b/projects/website-angular/src/app/search/search.component.html @@ -281,7 +281,7 @@

{{ group.typeName }} ({{ group.entriesCount }})

@for (entry of group.entries; track entry.dbId) {
- TODO: Add type icons +
@if (entry.exactType == "Icon") {
diff --git a/projects/website-angular/src/app/search/search.component.scss b/projects/website-angular/src/app/search/search.component.scss index 863f264..d86b058 100644 --- a/projects/website-angular/src/app/search/search.component.scss +++ b/projects/website-angular/src/app/search/search.component.scss @@ -244,9 +244,276 @@ $border-radius: 8px; } .type-icon { - width: 10px; + align-self: flex-start; + padding: 4px; } +/** START - Sprite base configuration **/ +.sprite { + background: url('/assets/spritesheet.png') no-repeat; + display: inline-block; +} + +.sprite-resize { + zoom: .75; + transform: scale(.75); + -moz-transform: scale(.75); +} + +.sprite-resize-small { + zoom: .6; + transform: scale(.6); + -moz-transform: scale(.6); +} + +.sprite-position { + vertical-align: middle; + padding: 2px; +} +/** END - Sprite base configuration **/ + +/** PASTE NEW CSS HERE **/ +.sprite-BlackBoxEvent { + width: 16px; + height: 16px; + background-position: -5px -5px; +} + +.sprite-CandidateSet { + width: 16px; + height: 16px; + background-position: -31px -5px; +} + +.sprite-Cell { + width: 16px; + height: 16px; + background-position: -57px -5px; +} + +.sprite-CellDevelopmentStep { + width: 16px; + height: 16px; + background-position: -83px -5px; +} + +.sprite-CellLineagePath { + width: 16px; + height: 16px; + background-position: -109px -5px; +} + +.sprite-ChemicalDrug { + width: 16px; + height: 16px; + background-position: -135px -5px; +} + +.sprite-Complex { + width: 16px; + height: 16px; + background-position: -161px -5px; +} + +.sprite-ConceptualEvent { + width: 16px; + height: 16px; + background-position: -187px -5px; +} + +.sprite-DefinedSet { + width: 16px; + height: 16px; + background-position: -213px -5px; +} + +.sprite-Depolymerization { + width: 16px; + height: 16px; + background-position: -239px -5px; +} + +.sprite-EntitySet { + width: 16px; + height: 16px; + background-position: -265px -5px; +} + +.sprite-EntityWithAccessionedSequence { + width: 16px; + height: 16px; + background-position: -291px -5px; +} + +.sprite-EquivalentEventSet { + width: 16px; + height: 16px; + background-position: -317px -5px; +} + +.sprite-FailedReaction { + width: 16px; + height: 16px; + background-position: -343px -5px; +} + +.sprite-GenomeEncodedEntity { + width: 16px; + height: 16px; + background-position: -369px -5px; +} + +.sprite-Icon { + width: 16px; + height: 16px; + background-position: -395px -5px; +} + +.sprite-Interactor { + width: 16px; + height: 16px; + background-position: -421px -5px; +} + +.sprite-NegativeRegulation { + width: 16px; + height: 16px; + background-position: -447px -5px; +} + +.sprite-OpenSet { + width: 16px; + height: 16px; + background-position: -473px -5px; +} + +.sprite-OtherEntity { + width: 16px; + height: 16px; + background-position: -499px -5px; +} + +.sprite-Pathway { + width: 16px; + height: 16px; + background-position: -525px -5px; +} + +.sprite-Person { + width: 16px; + height: 16px; + background-position: -551px -5px; +} + +.sprite-Polymer { + width: 16px; + height: 16px; + background-position: -577px -5px; +} + +.sprite-Polymerisation { + width: 16px; + height: 16px; + background-position: -603px -5px; +} + +.sprite-PositiveRegulation { + width: 16px; + height: 16px; + background-position: -629px -5px; +} + +.sprite-ProteinDrug { + width: 16px; + height: 16px; + background-position: -655px -5px; +} + +.sprite-Reaction { + width: 16px; + height: 16px; + background-position: -681px -5px; +} + +.sprite-ReferenceDNASequence { + width: 16px; + height: 16px; + background-position: -707px -5px; +} + +.sprite-ReferenceGeneProduct { + width: 16px; + height: 16px; + background-position: -733px -5px; +} + +.sprite-ReferenceGroup { + width: 16px; + height: 16px; + background-position: -759px -5px; +} + +.sprite-ReferenceIsoform { + width: 16px; + height: 16px; + background-position: -785px -5px; +} + +.sprite-ReferenceMolecule { + width: 16px; + height: 16px; + background-position: -811px -5px; +} + +.sprite-ReferenceRNASequence { + width: 16px; + height: 16px; + background-position: -837px -5px; +} + +.sprite-Requirement { + width: 16px; + height: 16px; + background-position: -863px -5px; +} + +.sprite-SimpleEntity { + width: 16px; + height: 16px; + background-position: -889px -5px; +} + +.sprite-TopLevelPathway { + width: 16px; + height: 16px; + background-position: -915px -5px; +} + +.sprite-deleted { + width: 20px; + height: 20px; + background-position: -941px -5px; +} + +.sprite-isDisease { + width: 10px; + height: 16px; + background-position: -971px -5px; +} + +.sprite-minus { + width: 21px; + height: 20px; + background-position: -991px -5px; +} + +.sprite-plus { + width: 20px; + height: 20px; + background-position: -1022px -5px; +} + + .search-entry { display: flex; flex-direction: row; diff --git a/projects/website-angular/src/app/search/search.component.ts b/projects/website-angular/src/app/search/search.component.ts index 025d116..3ebcc39 100644 --- a/projects/website-angular/src/app/search/search.component.ts +++ b/projects/website-angular/src/app/search/search.component.ts @@ -258,8 +258,21 @@ export class SearchComponent implements OnInit, OnDestroy, AfterViewInit { return of(null); } - printEntry(entry: SearchEntry): void { - console.log('Entry details:', entry); + getSpriteClass(entry: SearchEntry): string { + const reactionSubtypes = new Set([ + 'association', + 'binding', + 'dissociation', + 'omitted', + 'transition', + 'uncertain', + 'depolymerisation', + 'polymerisation' + ]); + + const rawType = (entry.exactType || entry.type || '').trim(); + const spriteType = reactionSubtypes.has(rawType.toLowerCase()) ? 'Reaction' : (rawType || 'Pathway'); + return `sprite sprite-resize sprite-${spriteType}`; } iconSvgUrl(entry: SearchEntry): string { diff --git a/projects/website-angular/src/assets/spritesheet.png b/projects/website-angular/src/assets/spritesheet.png new file mode 100644 index 0000000000000000000000000000000000000000..f3a58383f385dadb778bdf6cce8deed8f61194b5 GIT binary patch literal 8018 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