From b6caaeb0875d8e7cf01fb019c2de90a81c6c66dc Mon Sep 17 00:00:00 2001 From: akhuoa Date: Mon, 30 Mar 2026 10:48:41 +1300 Subject: [PATCH 001/100] Add cell cards explorer component in sidebar tab --- src/components.d.ts | 1 + src/components/CellCardsExplorer.vue | 56 ++++++++++++++++++++++++++++ src/components/SideBar.vue | 15 +++++++- 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/components/CellCardsExplorer.vue diff --git a/src/components.d.ts b/src/components.d.ts index 6b02393b..4a435b42 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -9,6 +9,7 @@ declare module 'vue' { export interface GlobalComponents { AnnotationTool: typeof import('./components/AnnotationTool.vue')['default'] BadgesGroup: typeof import('./components/BadgesGroup.vue')['default'] + CellCardsExplorer: typeof import('./components/CellCardsExplorer.vue')['default'] ConnectivityCard: typeof import('./components/ConnectivityCard.vue')['default'] ConnectivityExplorer: typeof import('./components/ConnectivityExplorer.vue')['default'] ConnectivityInfo: typeof import('./components/ConnectivityInfo.vue')['default'] diff --git a/src/components/CellCardsExplorer.vue b/src/components/CellCardsExplorer.vue new file mode 100644 index 00000000..7631b0a3 --- /dev/null +++ b/src/components/CellCardsExplorer.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/src/components/SideBar.vue b/src/components/SideBar.vue index ff630c17..e1650c1e 100644 --- a/src/components/SideBar.vue +++ b/src/components/SideBar.vue @@ -65,6 +65,15 @@ @connectivity-item-close="onConnectivityItemClose" /> + - @@ -110,6 +34,7 @@ import SearchFilters from './SearchFilters.vue' import SearchHistory from './SearchHistory.vue' import DatasetCard from './DatasetCard.vue' import EventBus from './EventBus.js' +import CellCard from './CellCard.vue' import { generateUUID } from '../utils/common.js'; export default { @@ -118,6 +43,7 @@ export default { SearchHistory, DatasetCard, Card, + CellCard, }, name: 'CellCardExplorer', props: { @@ -168,11 +94,6 @@ export default { activateCard: function(cardId) { this.activeCardId = this.activeCardId === cardId ? null : cardId; }, - transformString: function(str) { - if (!str) return ''; - // replace the string with ^ with and the next word with - return str.replace(/\^(\w+)/g, '$1'); - }, }, } @@ -231,96 +152,5 @@ export default { background-color: #979797; } -.card { - position: relative; - font-family: Asap; - font-size: 14px; - - + .card::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 1px; - background-color: #e4e7ed; - } - - .card-details { - display: none; - } - - &.active { - .card-details { - display: block; - } - } -} - -.card-header { - cursor: pointer; -} - -.card-header, -.card-details-inner { - padding: 1rem; -} - -.card-title { - margin-bottom: 0.75rem; - font-weight: bold; - line-height: 1.5; - letter-spacing: 1.05px; - display: -webkit-box; - -webkit-line-clamp: 5; - line-clamp: 5; - -webkit-box-orient: vertical; - overflow: hidden; -} -.card-details-inner { - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -.card-keywords, -.card-chips { - display: flex; - align-items: center; - gap: 0.5rem; - - span { - color: #606266; - } -} - -.card-chip { - display: inline-block; - padding: 2px 8px; - background-color: #f0f0f0; - border-radius: 12px; -} - -.card-section { - label { - font-weight: 600; - display: block; - margin-bottom: 0.25rem; - } - - p { - margin: 0; - color: #606266; - } - - ul { - margin: 0; - padding-left: 1rem; - - li { - color: #606266; - } - } -} From 0a6c85765af5b564cddea467cb0b6ca69425b802 Mon Sep 17 00:00:00 2001 From: akhuoa Date: Sun, 12 Apr 2026 12:22:59 +1200 Subject: [PATCH 011/100] Update cell card style to be the same as other cards in sidebar --- src/components/CellCard.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/CellCard.vue b/src/components/CellCard.vue index 0a4ef60b..ba827825 100644 --- a/src/components/CellCard.vue +++ b/src/components/CellCard.vue @@ -111,14 +111,15 @@ export default { position: relative; font-family: Asap; font-size: 14px; + max-height: 215px; // Same as other cards in the sidebar &:not(:first-child)::before { content: ''; position: absolute; top: 0; - left: 0; - width: 100%; - height: 1px; + left: 10px; + width: 455px; // Same as other cards in the sidebar + height: 2px; // Same as other cards in the sidebar background-color: #e4e7ed; } From 4903936d4e7f4c327a777e7e52980d94f1e8bbd9 Mon Sep 17 00:00:00 2001 From: akhuoa Date: Sun, 12 Apr 2026 12:41:18 +1200 Subject: [PATCH 012/100] Update cell caard styles to be the saame as others --- src/components/CellCard.vue | 40 ++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/components/CellCard.vue b/src/components/CellCard.vue index ba827825..33512e4f 100644 --- a/src/components/CellCard.vue +++ b/src/components/CellCard.vue @@ -111,16 +111,36 @@ export default { position: relative; font-family: Asap; font-size: 14px; - max-height: 215px; // Same as other cards in the sidebar - &:not(:first-child)::before { + &::before { content: ''; position: absolute; - top: 0; - left: 10px; - width: 455px; // Same as other cards in the sidebar - height: 2px; // Same as other cards in the sidebar - background-color: #e4e7ed; + top: 5px; + left: 5px; + width: calc(100% - 15px); + height: calc(100% - 10px); + border: 3px solid transparent; + border-radius: 5px; + z-index: 1; + } + + &:hover::before { + border-color: $app-primary-color; + } + + &:not(:first-child) { + margin-top: 5px; + + &::after { + content: ''; + position: absolute; + top: 0px; + left: 10px; + width: 455px; // Same as other cards in the sidebar + height: 2px; // Same as other cards in the sidebar + background-color: #e4e7ed; + z-index: 0; + } } .card-details { @@ -138,6 +158,12 @@ export default { cursor: pointer; } +.card-header, +.card-details { + position: relative; + z-index: 2; +} + .card-header, .card-details-inner { padding: 1rem; From 2f6672b19b54e3bc1e7701897fdeac44b5413a3d Mon Sep 17 00:00:00 2001 From: akhuoa Date: Sun, 12 Apr 2026 15:32:24 +1200 Subject: [PATCH 013/100] Add close button --- src/components/CellCard.vue | 158 +++++++++++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 12 deletions(-) diff --git a/src/components/CellCard.vue b/src/components/CellCard.vue index 33512e4f..5123c6df 100644 --- a/src/components/CellCard.vue +++ b/src/components/CellCard.vue @@ -7,20 +7,40 @@ class="card-header" @click="$emit('toggle')" > -
- {{ cellType.preferredLabel }} -
-
- {{ cellType.species }} -
- - {{ somaLocation }} - +
+
+ {{ cellType.preferredLabel }} +
+
+ {{ cellType.species }} +
+ + {{ somaLocation }} + +
+
+ + + + Close + +
@@ -83,8 +103,22 @@ @@ -124,6 +191,7 @@ export default { z-index: 1; } + &.active::before, &:hover::before { border-color: $app-primary-color; } @@ -148,6 +216,8 @@ export default { } &.active { + background-color: #f7faff; + .card-details { display: block; } @@ -156,6 +226,9 @@ export default { .card-header { cursor: pointer; + display: flex; + justify-content: space-between; + align-items: flex-start; } .card-header, @@ -226,4 +299,65 @@ export default { } } } + +:deep(.el-popper.popover-map-pin) { + padding: 4px 10px; + min-width: max-content; + font-family: Asap; + font-size: 12px; + line-height: inherit; + color: inherit; + background: #f3ecf6 !important; + border: 1px solid $app-primary-color; + + & .el-popper__arrow::before { + border: 1px solid; + border-color: $app-primary-color; + background: #f3ecf6; + } +} + +.title-buttons { + display: flex; + flex: 1 0 0%; + max-width: 20%; + flex-direction: row; + justify-content: end; + gap: 0.5rem; + + :deep(.copy-clipboard-button) { + &, + &:hover, + &:focus { + border-color: $app-primary-color !important; + border-radius: 50%; + } + &.is-disabled { + border-color: #dab3ec !important; + } + } + + .el-button + .el-button { + margin-left: 0 !important; + } +} + +.button-circle { + margin: 0; + width: 24px !important; + height: 24px !important; + border: 1px solid $app-primary-color !important; + + &, + &:hover, + &:focus, + &:active { + background-color: $app-primary-color; + border-color: $app-primary-color !important; + } + + &.secondary { + background-color: white !important; + } +} From ea4bd1459a67ceddc696f4284feecf5fd1590eaf Mon Sep 17 00:00:00 2001 From: akhuoa Date: Sun, 12 Apr 2026 15:39:03 +1200 Subject: [PATCH 014/100] Hide button in cell card active --- src/components/CellCard.vue | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/CellCard.vue b/src/components/CellCard.vue index 5123c6df..5c56d9d0 100644 --- a/src/components/CellCard.vue +++ b/src/components/CellCard.vue @@ -218,6 +218,12 @@ export default { &.active { background-color: #f7faff; + .card-header { + .card-chips { + display: none; + } + } + .card-details { display: block; } @@ -318,12 +324,16 @@ export default { } .title-buttons { - display: flex; flex: 1 0 0%; max-width: 20%; flex-direction: row; justify-content: end; gap: 0.5rem; + display: none; + + .card.active & { + display: flex; + } :deep(.copy-clipboard-button) { &, From b1d07d0a8384134d44bf1af22e5705a465c7096a Mon Sep 17 00:00:00 2001 From: akhuoa Date: Tue, 21 Apr 2026 10:43:06 +1200 Subject: [PATCH 015/100] Add filters to cell cards explorer --- src/components/CellCardExplorer.vue | 162 ++++++++++++++++++++++++++-- 1 file changed, 152 insertions(+), 10 deletions(-) diff --git a/src/components/CellCardExplorer.vue b/src/components/CellCardExplorer.vue index 0a6b4866..3884110e 100644 --- a/src/components/CellCardExplorer.vue +++ b/src/components/CellCardExplorer.vue @@ -6,13 +6,23 @@ Cell Card Explorer
+ + + -
+
{ + const loadedCellTypes = data.DEFAULT_CELL_TYPES.map((cellType) => { return { ...cellType, id: generateUUID(), } }); + this.allCellTypes = loadedCellTypes; + this.filterOptions = this.buildFilterOptions(loadedCellTypes); + this.applyFilters(this.activeFilters); } } catch (error) { console.error('Error fetching cell types:', error); @@ -94,6 +126,116 @@ export default { activateCard: function(cardId) { this.activeCardId = this.activeCardId === cardId ? null : cardId; }, + filterUpdate: function(filters) { + this.activeFilters = [...filters]; + this.applyFilters(this.activeFilters); + this.loadingCards = false; + }, + numberPerPageUpdate: function(value) { + this.numberPerPage = parseInt(value, 10) || 10; + this.applyFilters(this.activeFilters); + }, + filtersLoading: function() { + // SearchFilters only emits loading:true and never emits false. + // CellCardExplorer filters synchronously, so loading is reset in filterUpdate. + }, + cascaderReady: function() { + this.cascaderIsReady = true; + }, + normalizeFacetValue: function(value) { + return String(value || '').trim().toLowerCase(); + }, + buildFacetChildren: function(cellTypes, key) { + const values = new Set(); + + cellTypes.forEach((cellType) => { + const fieldValue = cellType[key]; + if (Array.isArray(fieldValue)) { + fieldValue.forEach((item) => { + const normalized = String(item || '').trim(); + if (normalized) values.add(normalized); + }); + return; + } + + const normalized = String(fieldValue || '').trim(); + if (normalized) values.add(normalized); + }); + + return [...values] + .sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' })) + .map((value) => ({ label: value })); + }, + buildFilterOptions: function(cellTypes) { + const options = [ + { + key: 'species', + label: 'Species', + children: this.buildFacetChildren(cellTypes, 'species'), + }, + { + key: 'somaLocations', + label: 'Soma Location', + children: this.buildFacetChildren(cellTypes, 'somaLocations'), + }, + { + key: 'circuitRole', + label: 'Circuit Role', + children: this.buildFacetChildren(cellTypes, 'circuitRole'), + }, + { + key: 'sourceNomenclatureLabel', + label: 'Source', + children: this.buildFacetChildren(cellTypes, 'sourceNomenclatureLabel'), + }, + ]; + + return options.filter((option) => option.children.length > 0); + }, + matchFieldFilter: function(cellType, filter) { + const filterFacet = this.normalizeFacetValue(filter.facet); + const filterTerm = this.normalizeFacetValue(filter.term); + + if (!filterFacet || filterFacet === 'show all') { + return true; + } + + if (filterTerm === 'species') { + return this.normalizeFacetValue(cellType.species) === filterFacet; + } + + if (filterTerm === 'soma location') { + return (cellType.somaLocations || []).some((location) => { + return this.normalizeFacetValue(location) === filterFacet; + }); + } + + if (filterTerm === 'circuit role') { + return this.normalizeFacetValue(cellType.circuitRole) === filterFacet; + } + + if (filterTerm === 'source') { + return this.normalizeFacetValue(cellType.sourceNomenclatureLabel) === filterFacet; + } + + return true; + }, + applyFilters: function(filters) { + const activeFilters = (filters || []).filter((filter) => { + return filter?.term && filter?.facet; + }); + + const filtered = this.allCellTypes.filter((cellType) => { + return activeFilters.every((filter) => this.matchFieldFilter(cellType, filter)); + }); + + this.totalFilteredCount = filtered.length; + this.cellTypes = filtered.slice(0, this.numberPerPage); + + if (!this.cellTypes.some((cellType) => cellType.id === this.activeCardId)) { + this.activeCardId = null; + } + }, }, } From 03a0aeed317b7608cfc53317025fb299358fccca Mon Sep 17 00:00:00 2001 From: akhuoa Date: Tue, 21 Apr 2026 10:52:49 +1200 Subject: [PATCH 016/100] Add pagination to cell cards explorer --- src/components/CellCardExplorer.vue | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/components/CellCardExplorer.vue b/src/components/CellCardExplorer.vue index 3884110e..128cdb59 100644 --- a/src/components/CellCardExplorer.vue +++ b/src/components/CellCardExplorer.vue @@ -30,6 +30,16 @@ :isActive="activeCardId === cellType.id" @toggle="activateCard(cellType.id)" /> +
@@ -38,6 +48,7 @@ /* eslint-disable no-alert, no-console */ import { ElCard as Card, + ElPagination as Pagination, } from 'element-plus' import 'element-plus/es/components/message/style/css'; import SearchFilters from './SearchFilters.vue' @@ -52,6 +63,7 @@ export default { SearchHistory, Card, CellCard, + Pagination, }, name: 'CellCardExplorer', props: { @@ -72,6 +84,8 @@ export default { filterOptions: [], activeFilters: [], numberPerPage: 10, + page: 1, + start: 0, totalFilteredCount: 0, loadingCards: false, activeCardId: null, @@ -128,12 +142,25 @@ export default { }, filterUpdate: function(filters) { this.activeFilters = [...filters]; + this.page = 1; + this.start = 0; this.applyFilters(this.activeFilters); this.loadingCards = false; }, numberPerPageUpdate: function(value) { this.numberPerPage = parseInt(value, 10) || 10; + this.pageChange(1); + }, + pageChange: function(page) { + this.page = page; + this.start = (page - 1) * this.numberPerPage; this.applyFilters(this.activeFilters); + this.scrollToTop(); + }, + scrollToTop: function() { + if (this.$refs.content) { + this.$refs.content.scroll({ top: 0, behavior: 'smooth' }); + } }, filtersLoading: function() { // SearchFilters only emits loading:true and never emits false. @@ -230,7 +257,7 @@ export default { }); this.totalFilteredCount = filtered.length; - this.cellTypes = filtered.slice(0, this.numberPerPage); + this.cellTypes = filtered.slice(this.start, this.start + this.numberPerPage); if (!this.cellTypes.some((cellType) => cellType.id === this.activeCardId)) { this.activeCardId = null; From ac40d1b3885f137f012e6c3ccfcacdac6ec22895 Mon Sep 17 00:00:00 2001 From: akhuoa Date: Tue, 21 Apr 2026 13:51:00 +1200 Subject: [PATCH 017/100] Fix cell card explorer filter label transformation --- src/components/SearchFilters.vue | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/SearchFilters.vue b/src/components/SearchFilters.vue index ec21d8b5..f95d5cd5 100644 --- a/src/components/SearchFilters.vue +++ b/src/components/SearchFilters.vue @@ -288,18 +288,22 @@ export default { return value; }, createChildrenCascaderValue: function(children, facet, facets) { + const parentKey = facet.key; + if (children?.length) { for (let i = 0; i < children.length; i++) { const facetItem = children[i]; //copy the facets into if (children[i].facetPropPath !== 'supportingAwards.consortium.name') { - children[i].label = convertReadableLabel( - facetItem.label - ) + // `sourceNomenclatureLabel` is from cell card explorer filters + if (parentKey === 'sourceNomenclatureLabel') { + children[i].label = facetItem.label; + } else { + children[i].label = convertReadableLabel(facetItem.label); + } } - if (facetItem.key && facet.key.includes('flatmap.connectivity.source.')) { + if (facetItem.key && parentKey.includes('flatmap.connectivity.source.')) { const childKey = facetItem.key; - const parentKey = facet.key; const key = childKey.replace(`${parentKey}.`, ''); children[i].value = this.createCascaderItemValue([facet.label, key]); } else { From 0dd7aa23f6b4b757c6c2ba616b9cc47809a4e8c8 Mon Sep 17 00:00:00 2001 From: akhuoa Date: Tue, 21 Apr 2026 16:43:08 +1200 Subject: [PATCH 018/100] Fix cell cards explorer filters between categories --- src/components/CellCardExplorer.vue | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/components/CellCardExplorer.vue b/src/components/CellCardExplorer.vue index 128cdb59..f14a6a76 100644 --- a/src/components/CellCardExplorer.vue +++ b/src/components/CellCardExplorer.vue @@ -245,15 +245,28 @@ export default { return this.normalizeFacetValue(cellType.sourceNomenclatureLabel) === filterFacet; } - return true; + return false; }, applyFilters: function(filters) { const activeFilters = (filters || []).filter((filter) => { - return filter?.term && filter?.facet; + return filter?.term && filter?.facet && this.normalizeFacetValue(filter.facet) !== 'show all'; }); + const filtersByTerm = activeFilters.reduce((grouped, filter) => { + const termKey = this.normalizeFacetValue(filter.term); + if (!grouped[termKey]) { + grouped[termKey] = []; + } + grouped[termKey].push(filter); + return grouped; + }, {}); + + const filterGroups = Object.values(filtersByTerm); + const filtered = this.allCellTypes.filter((cellType) => { - return activeFilters.every((filter) => this.matchFieldFilter(cellType, filter)); + return filterGroups.every((termGroup) => { + return termGroup.some((filter) => this.matchFieldFilter(cellType, filter)); + }); }); this.totalFilteredCount = filtered.length; From 746de135b97ab41da0f560379bbfe74370b8fb1e Mon Sep 17 00:00:00 2001 From: akhuoa Date: Mon, 4 May 2026 16:24:54 +1200 Subject: [PATCH 019/100] Add cell card search input UI --- src/components/CellCardExplorer.vue | 53 +++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/components/CellCardExplorer.vue b/src/components/CellCardExplorer.vue index f14a6a76..3902e6f1 100644 --- a/src/components/CellCardExplorer.vue +++ b/src/components/CellCardExplorer.vue @@ -1,9 +1,33 @@ -