From fc7511f2d856a95537e0b6f2e629b6518d425463 Mon Sep 17 00:00:00 2001
From: Xiao Gui <xgui3783@gmail.com>
Date: Fri, 20 Sep 2019 18:01:53 +0200
Subject: [PATCH] WIP new search UI

---
 src/atlasViewer/atlasViewer.component.ts      |   9 +-
 src/atlasViewer/atlasViewer.template.html     |  38 ++-
 src/res/css/extra_styles.css                  |  68 +++++
 src/services/effect/effect.ts                 |  13 +-
 src/services/state/uiState.store.ts           |  10 -
 .../databrowserModule/databrowser.module.ts   |  10 +-
 .../databrowser/databrowser.component.ts      |  14 +-
 .../databrowser/databrowser.style.css         | 188 +------------
 .../databrowser/databrowser.template.html     | 247 +++++++++---------
 .../detailedView/singleDataset.component.ts   |  28 ++
 .../singleDataset.style.css                   |   0
 .../singleDataset.template.html               |   0
 .../singleDatasetListView.component.ts        |  27 ++
 .../listView/singleDatasetListView.style.css  |   0
 .../singleDatasetListView.template.html       |  55 ++++
 ...set.component.ts => singleDataset.base.ts} |  30 +--
 .../util/datasetIsFaved.pipe.ts               |   1 +
 src/ui/menuicons/menuicons.component.ts       |  19 +-
 src/ui/menuicons/menuicons.template.html      |   2 -
 .../searchSideNav/searchSideNav.component.ts  |  77 ++++++
 src/ui/searchSideNav/searchSideNav.style.css  |   0
 .../searchSideNav/searchSideNav.template.html |  60 +++++
 src/ui/ui.module.ts                           |   6 +-
 .../currentlySelectedRegions.component.ts     |  46 ++++
 .../currentlySelectedRegions.style.css        |   4 +
 .../currentlySelectedRegions.template.html    |  24 ++
 .../regionHierachy/regionHierarchy.style.css  |   8 +-
 .../regionHierarchy.template.html             |  32 +--
 .../regionSearch/regionSearch.component.ts    |   7 +-
 .../regionSearch/regionSearch.template.html   |   9 +-
 .../viewerState.component.ts                  |  27 --
 .../viewerState.template.html                 | 103 +-------
 .../directives/stopPropagation.directive.ts   |  47 ++++
 src/util/util.module.ts                       |   7 +-
 34 files changed, 673 insertions(+), 543 deletions(-)
 create mode 100644 src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts
 rename src/ui/databrowserModule/singleDataset/{ => detailedView}/singleDataset.style.css (100%)
 rename src/ui/databrowserModule/singleDataset/{ => detailedView}/singleDataset.template.html (100%)
 create mode 100644 src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts
 create mode 100644 src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.style.css
 create mode 100644 src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html
 rename src/ui/databrowserModule/singleDataset/{singleDataset.component.ts => singleDataset.base.ts} (87%)
 create mode 100644 src/ui/searchSideNav/searchSideNav.component.ts
 create mode 100644 src/ui/searchSideNav/searchSideNav.style.css
 create mode 100644 src/ui/searchSideNav/searchSideNav.template.html
 create mode 100644 src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.component.ts
 create mode 100644 src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.style.css
 create mode 100644 src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.template.html
 create mode 100644 src/util/directives/stopPropagation.directive.ts

diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts
index af0debbaa..8a50086a1 100644
--- a/src/atlasViewer/atlasViewer.component.ts
+++ b/src/atlasViewer/atlasViewer.component.ts
@@ -14,7 +14,7 @@ import { NehubaContainer } from "../ui/nehubaContainer/nehubaContainer.component
 import { colorAnimation } from "./atlasViewer.animation"
 import { FixedMouseContextualContainerDirective } from "src/util/directives/FixedMouseContextualContainerDirective.directive";
 import { DatabrowserService } from "src/ui/databrowserModule/databrowser.service";
-import { AGREE_COOKIE, AGREE_KG_TOS, SHOW_KG_TOS, SHOW_SIDEBAR_TEMPLATE, SHOW_BOTTOM_SHEET } from "src/services/state/uiState.store";
+import { AGREE_COOKIE, AGREE_KG_TOS, SHOW_KG_TOS, SHOW_BOTTOM_SHEET } from "src/services/state/uiState.store";
 import { TabsetComponent } from "ngx-bootstrap/tabs";
 import { LocalFileService } from "src/services/localFile.service";
 import { MatDialog, MatDialogRef, MatSnackBar, MatSnackBarRef, MatBottomSheet, MatBottomSheetRef } from "@angular/material";
@@ -97,7 +97,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
   public unsupportedPreviews: any[] = UNSUPPORTED_PREVIEW
 
   public sidePanelOpen$: Observable<boolean>
-  public sideNavTemplate$: Observable<TemplateRef<any>>
 
   get toggleMessage(){
     return this.constantsService.toggleMessage
@@ -153,12 +152,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit {
       map(state => state.sidePanelOpen)
     )
 
-    this.sideNavTemplate$ = this.store.pipe(
-      select('uiState'),
-      select('sidebarTemplate'),
-      distinctUntilChanged()
-    )
-
     this.showHelp$ = this.constantsService.showHelpSubject$.pipe(
       debounceTime(170)
     )
diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html
index 5ca66e94d..5347548f8 100644
--- a/src/atlasViewer/atlasViewer.template.html
+++ b/src/atlasViewer/atlasViewer.template.html
@@ -36,8 +36,6 @@
             </layout-floating-container>
           </tab>
           <tab heading="Containers">
-            <menu-icons hidden #MenuIcons>
-            </menu-icons>
             <panel-component>
               <div class="m-2" heading>
                 Layer Browser
@@ -156,25 +154,41 @@
   
     <div class="z-index-10 position-absolute pe-none w-100 h-100">
       <mat-drawer-container
+        *ngIf="newViewer$ | async"
         [hasBackdrop]="false"
-        class="w-100 h-100 bg-none">
+        class="w-100 h-100 bg-none mat-drawer-content-overflow-visible">
         <mat-drawer
           mode="push"
-          class="pe-all col-sm-12 col-md-3"
+          class="col-sm-10 col-md-4 col-lg-3 col-xl-2 p-2 bg-none box-shadow-none overflow-visible"
           [disableClose]="true"
           [autoFocus]="false"
-          [opened]="sideNavTemplate$ | async">
+          [opened]="true"
+          #sideNavDrawer>
           
           <!-- template outlet -->
-          <mat-card class="h-100">
-            <ng-container *ngTemplateOutlet="sideNavTemplate$ | async">
-            </ng-container>
-          </mat-card>
+          <search-side-nav
+            (dismiss)="sideNavDrawer.close()"
+            (open)="sideNavDrawer.open()"
+            class="h-100 d-block overflow-visible"
+            #searchSideNav>
+          </search-side-nav>
 
         </mat-drawer>
-        <div class="d-flex h-100 justify-content-between align-items-start bg-none pe-none">
-          <menu-icons>
-          </menu-icons>
+        <div class="d-flex h-100 align-items-start bg-none pe-none">
+
+          <button mat-flat-button
+            matBadgePosition="above after"
+            matBadgeColor="accent"
+            [matBadge]="!sideNavDrawer.opened && (selectedRegions$ | async)?.length ? (selectedRegions$ | async)?.length : null"
+            [matTooltip]="!sideNavDrawer.opened ? (selectedRegions$ | async)?.length ?  ('Explore ' + (selectedRegions$ | async)?.length + ' selected regions.') : 'Explore current view' : null"
+            [ngClass]="{'translate-x-6-n': !sideNavDrawer.opened, 'translate-x-7-n': sideNavDrawer.opened}"
+            class="pe-all mt-5"
+            (click)="sideNavDrawer.toggle()">
+            <i [ngClass]="{'fa-chevron-left': sideNavDrawer.opened, 'fa-chevron-right': !sideNavDrawer.opened}" class="fas translate-x-3"></i>
+
+          </button>
+
+          <!-- TODO clean up menu icon -->
         </div>
       </mat-drawer-container>
     </div>
diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css
index a835d33bc..709122816 100644
--- a/src/res/css/extra_styles.css
+++ b/src/res/css/extra_styles.css
@@ -293,6 +293,11 @@ markdown-dom pre code
   transform: rotate(90deg)!important;
 }
 
+.r-270
+{
+  transform: rotate(270deg)!important;
+}
+
 .ws-no-wrap
 {
   white-space: nowrap!important;
@@ -512,3 +517,66 @@ cdk-virtual-scroll-viewport > .cdk-virtual-scroll-content-wrapper
 {
   flex-grow:1;
 }
+
+.transform-origin-left-center
+{
+  transform-origin: 0% 50%;
+}
+
+.transform-origin-center
+{
+  transform-origin: 50% 50%;
+}
+
+.box-shadow-none
+{
+  box-shadow: none!important;
+}
+
+.translate-x-2
+{
+  transform: translateX(1em);
+}
+
+.translate-x-3
+{
+  transform: translateX(1.5em);
+}
+
+.translate-x-4
+{
+  transform: translateX(2em);
+}
+
+.translate-x-6
+{
+  transform: translateX(3em);
+}
+
+.translate-x-6-n
+{
+  transform: translateX(-3em);
+}
+
+.translate-x-7-n
+{
+  transform: translateX(-3.5em);
+}
+
+.translate-x-8-n
+{
+  transform: translateX(-4em);
+}
+
+/* this is required to physically link label with side bar */
+.mat-drawer-content-overflow-visible > mat-drawer-content,
+/* this is required to show the popout info of template and parcellation */
+.mat-drawer-content-overflow-visible > mat-drawer > .mat-drawer-inner-container
+{
+  overflow: visible!important;
+}
+
+.overflow-visible
+{
+  overflow: visible!important;
+}
diff --git a/src/services/effect/effect.ts b/src/services/effect/effect.ts
index 603ed7be8..054568c67 100644
--- a/src/services/effect/effect.ts
+++ b/src/services/effect/effect.ts
@@ -36,8 +36,9 @@ export class UseEffects implements OnDestroy{
       withLatestFrom(this.regionsSelected$),
       map(([action, regionsSelected]) => {
         const { deselectRegions } = action
-        const deselectSet = new Set((deselectRegions as any[]).map(r => r.name))
-        const selectRegions = regionsSelected.filter(r => !deselectSet.has(r.name))
+        const selectRegions = regionsSelected.filter(r => {
+          return !(deselectRegions as any[]).find(dr => compareRegions(dr, r))
+        })
         return {
           type: SELECT_REGIONS,
           selectRegions
@@ -206,4 +207,12 @@ export class UseEffects implements OnDestroy{
       updatedParcellation: parcellation
     }))
   )
+}
+
+export const compareRegions: (r1: any,r2: any) => boolean = (r1, r2) => {
+  if (!r1) return !r2
+  if (!r2) return !r1
+  return r1.ngId === r2.ngId
+    && r1.labelIndex === r2.labelIndex
+    && r1.name === r2.name
 }
\ No newline at end of file
diff --git a/src/services/state/uiState.store.ts b/src/services/state/uiState.store.ts
index 1caa0f942..80cfaaef4 100644
--- a/src/services/state/uiState.store.ts
+++ b/src/services/state/uiState.store.ts
@@ -13,7 +13,6 @@ const defaultState : UIStateInterface = {
 
   snackbarMessage: null,
 
-  sidebarTemplate: null,
   bottomSheetTemplate: null,
 
   /**
@@ -87,12 +86,6 @@ export function uiState(state:UIStateInterface = defaultState,action:UIAction){
         ...state,
         agreedKgTos: true
       }
-    case SHOW_SIDEBAR_TEMPLATE:
-      const { sidebarTemplate } = action
-      return {
-        ...state,
-        sidebarTemplate
-      }
     case SHOW_BOTTOM_SHEET:
         const { bottomSheetTemplate } = action
         return {
@@ -121,7 +114,6 @@ export interface UIStateInterface{
   agreedCookies: boolean
   agreedKgTos: boolean
 
-  sidebarTemplate: TemplateRef<any>
   bottomSheetTemplate: TemplateRef<any>
 }
 
@@ -137,7 +129,6 @@ export interface UIAction extends Action{
   }[],
   snackbarMessage: string
 
-  sidebarTemplate: TemplateRef<any>
   bottomSheetTemplate: TemplateRef<any>
 }
 
@@ -154,5 +145,4 @@ export const AGREE_KG_TOS = `AGREE_KG_TOS`
 export const SHOW_KG_TOS = `SHOW_KG_TOS`
 
 export const SNACKBAR_MESSAGE = `SNACKBAR_MESSAGE`
-export const SHOW_SIDEBAR_TEMPLATE = `SHOW_SIDEBAR_TEMPLATE`
 export const SHOW_BOTTOM_SHEET = `SHOW_BOTTOM_SHEET`
\ No newline at end of file
diff --git a/src/ui/databrowserModule/databrowser.module.ts b/src/ui/databrowserModule/databrowser.module.ts
index 25b236e98..d2d2644e7 100644
--- a/src/ui/databrowserModule/databrowser.module.ts
+++ b/src/ui/databrowserModule/databrowser.module.ts
@@ -21,7 +21,7 @@ import { PopoverModule } from "ngx-bootstrap/popover";
 import { UtilModule } from "src/util/util.module";
 import { AggregateArrayIntoRootPipe } from "./util/aggregateArrayIntoRoot.pipe";
 import { KgSingleDatasetService } from "./kgSingleDatasetService.service"
-import { SingleDatasetView } from './singleDataset/singleDataset.component'
+import { SingleDatasetView } from './singleDataset/detailedView/singleDataset.component'
 import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module'
 import { DoiParserPipe } from "src/util/pipes/doiPipe.pipe";
 import { DatasetIsFavedPipe } from "./util/datasetIsFaved.pipe";
@@ -32,6 +32,8 @@ import { ScrollingModule } from "@angular/cdk/scrolling";
 import { GetKgSchemaIdFromFullIdPipe } from "./util/getKgSchemaIdFromFullId.pipe";
 import { PreviewFileIconPipe } from "./preview/previewFileIcon.pipe";
 import { PreviewFileTypePipe } from "./preview/previewFileType.pipe";
+import { SingleDatasetListView } from "./singleDataset/listView/singleDatasetListView.component";
+import { CurrentlySelectedRegions } from "../viewerStateController/currentlySelectedRegions/currentlySelectedRegions.component";
 
 @NgModule({
   imports:[
@@ -55,8 +57,10 @@ import { PreviewFileTypePipe } from "./preview/previewFileType.pipe";
     LineChart,
     DedicatedViewer,
     SingleDatasetView,
+    SingleDatasetListView,
     RegionTextSearchAutocomplete,
     RegionHierarchy,
+    CurrentlySelectedRegions,
 
     /**
      * pipes
@@ -76,12 +80,14 @@ import { PreviewFileTypePipe } from "./preview/previewFileType.pipe";
   exports:[
     DataBrowser,
     SingleDatasetView,
+    SingleDatasetListView,
     PreviewComponent,
     ModalityPicker,
     FilterDataEntriesbyMethods,
     FileViewer,
     RegionTextSearchAutocomplete,
-    RegionHierarchy
+    RegionHierarchy,
+    CurrentlySelectedRegions,
   ],
   entryComponents:[
     DataBrowser,
diff --git a/src/ui/databrowserModule/databrowser/databrowser.component.ts b/src/ui/databrowserModule/databrowser/databrowser.component.ts
index 8190e7d30..eb67dcfc0 100644
--- a/src/ui/databrowserModule/databrowser/databrowser.component.ts
+++ b/src/ui/databrowserModule/databrowser/databrowser.component.ts
@@ -3,7 +3,7 @@ import { DataEntry } from "src/services/stateStore.service";
 import { Subscription, merge, Observable } from "rxjs";
 import { DatabrowserService, CountedDataModality } from "../databrowser.service";
 import { ModalityPicker } from "../modalityPicker/modalityPicker.component";
-import { MatDialog } from "@angular/material";
+import { MatDialog, MatExpansionPanel } from "@angular/material";
 import { KgSingleDatasetService } from "../kgSingleDatasetService.service";
 import { scan, shareReplay } from "rxjs/operators";
 import { ViewerPreviewFile } from "src/services/state/dataStore.store";
@@ -22,6 +22,8 @@ const scanFn: (acc: any[], curr: any) => any[] = (acc, curr) => [curr, ...acc]
 
 export class DataBrowser implements OnChanges, OnDestroy,OnInit{
 
+  @ViewChild('selectedRegionExpansionPanel') selectedRegionExpansionPanel: MatExpansionPanel
+
   @Input()
   public regions: any[] = []
 
@@ -86,6 +88,8 @@ export class DataBrowser implements OnChanges, OnDestroy,OnInit{
 
   ngOnChanges(changes){
 
+    if (this.regions.length === 0) this.selectedRegionExpansionPanel && (this.selectedRegionExpansionPanel.expanded = false)
+
     this.regions = this.regions.map(r => {
       /**
        * TODO to be replaced with properly region UUIDs from KG
@@ -98,6 +102,9 @@ export class DataBrowser implements OnChanges, OnDestroy,OnInit{
     const { regions, parcellation, template } = this
     this.fetchingFlag = true
 
+    // input may be undefined/null
+    if (!parcellation) return
+
     /**
      * reconstructing parcellation region is async (done on worker thread)
      * if parcellation region is not yet defined, return. 
@@ -171,6 +178,7 @@ export class DataBrowser implements OnChanges, OnDestroy,OnInit{
   handleModalityFilterEvent(modalityFilter:CountedDataModality[]){
     this.countedDataM = modalityFilter
     this.visibleCountedDataM = modalityFilter.filter(dm => dm.visible)
+    this.cdr.markForCheck()
     this.resetCurrentPage()
   }
 
@@ -215,6 +223,10 @@ export class DataBrowser implements OnChanges, OnDestroy,OnInit{
     this.focusedDataset = dataset
     this.dialog.open(this.detailDatasetTemplateRef)
   }
+
+  trackbyFn(index:number, dataset:DataEntry) {
+    return dataset.id
+  }
 }
 
 export interface DataEntryFilter{
diff --git a/src/ui/databrowserModule/databrowser/databrowser.style.css b/src/ui/databrowserModule/databrowser/databrowser.style.css
index 1e2d2b616..831c351d9 100644
--- a/src/ui/databrowserModule/databrowser/databrowser.style.css
+++ b/src/ui/databrowserModule/databrowser/databrowser.style.css
@@ -1,167 +1,9 @@
-div[heading]
-{
-  padding : 0.5em 1em;
-  white-space: nowrap;
-  overflow: hidden;
-}
-
-div[heading][displayflex]
-{
-  display:flex;
-  flex-direction: row;
-}
-
-  div[heading][displayflex] > [maintext]
-  {
-    flex : 1 1 0px;
-    white-space: nowrap;
-    overflow: hidden;
-  }
-
-  div[heading][displayflex] > [propertyicons]
-  {
-    flex : 0 0 auto;
-  }
-
-  div[heading][displayflex] > [propertyicons] > *
-  {
-    margin-left:0.5em;
-  }
-
-[dataentry]
-{
-  width:100%;
-}
-
-[dataentry] div[heading]
-{
-  padding : 0.4em 1.6em;
-  background-color:rgba(0,0,0,0.2);
-}
-
-[dataentry] div[body]
-{
-  padding : 0.4em 2.0em;
-  background-color:rgba(0,0,0,0.3);
-} 
-
-div[databrowserheader]
-{
-  padding : 0.5em 1.0em;
-  background-color:rgba(128,128,128,0.05);
-}
-
-div[databrowserheader] > *
-{
-  padding : 0;
-  margin : 0 ;
-  border : 0 ;
-}
-
-div[databrowserheader]
-{
-  white-space: nowrap;
-}
-
-div[filterBlock] > *
-{
-  white-space: nowrap;
-  overflow: hidden;
-}
-
-.clickable:hover
-{
-  color:#dbb556;
-  cursor:default;
-}
-
-div.unclickable
-{
-  color:rgba(128,128,128,0.8);
-}
-
-div.unclickable:hover
-{
-  cursor:default;
-}
-
-div.noResultClass
-{
-  color:rgba(128,128,128,0.8);
-  text-decoration: line-through;
-}
-
-div[spatialSearchCell]
-{
-  padding: 0.3em 1em;
-  overflow:hidden;
-  white-space: nowrap;
-}
-
-div[noSelectedRegion]
-{
-  padding: 1em 1em;
-  font-size:2em;
-  color: rgba(128,128,128,0.5);
-  max-height: 6em;
-}
-
-:host-context([darktheme="true"]) div[noSelectedRegion]
-{
-  background-color: rgba(0,0,0,0.2);
-}
-
-div[regionsContainer]
-{
-  background-color:rgba(0, 0, 0, 0.3);
-  padding:0.5em 0.7em;
-}
-
-div[regionContainer]
-{
-  margin-top:0.5em;
-}
-
 modality-picker
 {
   font-size: 90%;
   display:inline-block;
 }
 
-.spinnerAnimationCircleContainer
-{
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-}
-
-.spinnerAnimationCircleContainer > .spinnerAnimationCircle
-{
-  font-size: 300%;
-}
-
-.parcellationSelectionWrapper
-{
-  width: 100%;
-  display:flex;
-  flex-direction: row;
-  align-items: flex-start;
-}
-.parcellationSelectionWrapper > *:first-child
-{
-  flex: 1 0 0;
-}
-.parcellationSelectionWrapper > *:last-child
-{
-  flex: 0 0 0;
-}
-
-.toggleParcellationBtn
-{
-  margin-left: -2.5em;
-  z-index: 1;
-}
-
 radio-list
 {
   display: block;
@@ -173,33 +15,7 @@ radio-list
   width: 100%;
 }
 
-/* datawrapper */
-:host .dataEntryWrapper
-{
-  white-space: nowrap;
-  overflow: hidden;
-  width:200%;
-  padding: 0;
-  transition: transform 190ms ease;
-}
-
-.dataEntryWrapper > *
-{
-  vertical-align: top;
-  white-space: initial;
-  width: 50%;
-  display:inline-block;
-}
-
-.filePreviewContainer
-{
-  max-height: 100%;
-  overflow:auto;
-}
-
-
-div[regionTagsContainer]
+cdk-virtual-scroll-viewport
 {
-  max-height: 4em;
-  overflow:hidden;
+  width: calc(100% + 1em);
 }
\ No newline at end of file
diff --git a/src/ui/databrowserModule/databrowser/databrowser.template.html b/src/ui/databrowserModule/databrowser/databrowser.template.html
index 7e802978d..90a524586 100644
--- a/src/ui/databrowserModule/databrowser/databrowser.template.html
+++ b/src/ui/databrowserModule/databrowser/databrowser.template.html
@@ -1,102 +1,59 @@
-<mat-tab-group [dynamicHeight]="false" class="h-100 w-100 overflow-hidden">
-  <mat-tab label="Search Criteria">
-    <ng-container *ngTemplateOutlet="searchPanel">
+<mat-card
+  [ngClass]="{'h-100': (!fetchingFlag && !fetchError)}"
+  class="w-100 overflow-hidden d-block flex-grow-1 flex-shrink-1 d-flex flex-column">
 
-    </ng-container>
-  </mat-tab>
-  <mat-tab label="History">
-    <ng-container *ngTemplateOutlet="history">
+  <!-- transclusion header -->
+  <mat-card-header>
+    <ng-content select="[card-header]">
+
+    </ng-content>
+  </mat-card-header>
+  
+  <!-- transclusion header -->
+  <ng-content select="[card-content='prepend']">
+  </ng-content>
+
+  <!-- modality filter -->
+  <ng-container *ngTemplateOutlet="modalitySelector">
+  </ng-container>
 
+  <!-- if still loading, show spinner -->
+  <ng-template [ngIf]="fetchingFlag">
+    <ng-container *ngTemplateOutlet="loadingSpinner">
     </ng-container>
-  </mat-tab>
-</mat-tab-group>
+  </ng-template>
 
-<ng-template #searchPanel>
+  <!-- else, show fetched -->
+  <ng-template [ngIf]="!fetchingFlag">
 
-  <div class="h-100 w-100 d-flex flex-column overflow-hidden">
+    <!-- if error, show error only -->
+    <ng-template [ngIf]="fetchError">
+      <ng-container *ngTemplateOutlet="errorTemplate">
+      </ng-container>
+    </ng-template>
+
+    <!-- if not error, show dataset template -->
     
-    <!-- search criterial -->
-    <mat-selection-list class="flex-grow-0 flex-shrink-0 mb-2" checkboxPosition="before">
-      <h3 mat-subheader>
-        Search criteria
-      </h3>
-
-      <!-- selected regions -->
-      <mat-list-option
-        selected="true"
-        checkboxPosition="before">
-        <h4 mat-line>
-          {{ regions.length }} selected region{{ regions.length !== 1 ? 's' : ''}}
-
-          <!-- stop mousedown propagation to avoid ripple from mat-list-option -->
-          <region-text-search-autocomplete (mousedown)="$event.stopPropagation()" (click)="$event.stopPropagation()" [showAutoComplete]="false">
-          </region-text-search-autocomplete>
-        </h4>
-      </mat-list-option>
-
-      <mat-list-option
-        *ngIf="dbService.viewportBoundingBox$ | async as bbox"
-        checkboxPosition="before"
-        [selected]="true">
-        <h4 class="mat-line">
-          viewport bounding box 
-        </h4>
-
-        <!-- from -->
-        <p class="mat-line">
-          <small>
-            <span *ngFor="let v of bbox[0]; let lastval = last">
-              {{ v | number : '1.2-2' }}<span *ngIf="!lastval">, </span>
-            </span>
-            <span>
-              to
-            </span>
-            <span *ngFor="let v of bbox[1]; let lastval = last">
-              {{ v | number : '1.2-2' }}<span *ngIf="!lastval">, </span>
-            </span>
-          </small>
-        </p>
-        
-      </mat-list-option>
-    </mat-selection-list>
-
-    <mat-divider class="position-relative"></mat-divider>
-
-    <!-- modality picker / filter -->
-    <mat-accordion class="flex-grow-0 flex-shrink-0 mb-2">
-      <mat-expansion-panel>
-        <mat-expansion-panel-header>
-          <mat-panel-title>
-            Filter
-          </mat-panel-title>
-          <mat-panel-description>
-            <i *ngIf="dataentries.length > 0">
-              <span *ngIf="visibleCountedDataM && visibleCountedDataM.length > 0 "> 
-                {{ (dataentries | filterDataEntriesByMethods : visibleCountedDataM).length }} filtered /
-              </span>
-              {{ dataentries.length }} results
-            </i>
-            <i *ngIf="dataentries.length === 0">
-              No results to show.
-            </i>
-          </mat-panel-description>
-        </mat-expansion-panel-header>
-
-        <ng-container *ngTemplateOutlet="modalityPicker">
-
-        </ng-container>
-      </mat-expansion-panel>
-    </mat-accordion>
-
-    <!-- datasets container -->
-    <div *ngIf="fetchingFlag; else fetched"
-      class="spinnerAnimationCircleContainer">
-      <div class="spinnerAnimationCircle"></div>
-      <div>Fetching datasets...</div>
+    <ng-template [ngIf]="!fetchError">
+      <ng-container *ngTemplateOutlet="datasetTemplate">
+      </ng-container>
+    </ng-template>
+  </ng-template>
+
+  <!-- footer, populated by content transclusion -->
+  <mat-card-footer>
+    <ng-content select="[card-footer]">
+    </ng-content>
+  </mat-card-footer>
+</mat-card>
+
+<ng-template #loadingSpinner>
+  <mat-card-content>
+    <div class="m-2 d-flex flex-row align-items-center justify-content star">
+      <div class="d-inline-block mr-2 spinnerAnimationCircle"></div>
+      <span>Fetching datasets...</span>
     </div>
-
-  </div>
-
+  </mat-card-content>
 </ng-template>
 
 <ng-template #modalityPicker>
@@ -109,35 +66,35 @@
   </modality-picker>
 </ng-template>
 
-<ng-template #fetched>
-  <div class="ml-2 mr-2 alert alert-danger" *ngIf="fetchError; else showData">
-    <i class="fas fa-exclamation-triangle"></i> Error fetching data. <a href="#" (click)="retryFetchData($event)" class="btn btn-link text-info">retry</a>
-  </div>
+<ng-template #errorTemplate>
+  <mat-card-content>
+    <div class="ml-2 mr-2 alert alert-danger">
+      <i class="fas fa-exclamation-triangle"></i> Error fetching data. <a href="#" (click)="retryFetchData($event)" class="btn btn-link text-info">retry</a>    
+    </div>
+  </mat-card-content>
 </ng-template>
 
-<ng-template #showData>
+<ng-template #datasetTemplate>
   <!-- datawrapper -->
-  <ng-container *ngIf="dataentries | filterDataEntriesByMethods : visibleCountedDataM as filteredDataEntry">
 
-    <!-- dataentries -->
-    <div class="h-100 w-100">
-      <cdk-virtual-scroll-viewport 
+  <ng-container *ngIf="dataentries | filterDataEntriesByMethods : visibleCountedDataM as filteredDataEntry">
+    <mat-card-content class="h-100 w-100 overflow-hidden">
+      <cdk-virtual-scroll-viewport
         class="h-100"
-        itemSize="50">
-        <single-dataset-view
-          *cdkVirtualFor="let dataset of filteredDataEntry"
-          (click)="showFocusedDataset(dataset)"
-          class="m-2"
-          [kgSchema]="(dataset.fullId | getKgSchemaIdFromFullIdPipe)[0]"
-          [kgId]="(dataset.fullId | getKgSchemaIdFromFullIdPipe)[1]"
-          [prefetched]="dataset"
-          [simpleMode]="true"
-          [ripple]="true">
-
-        </single-dataset-view>
-    </cdk-virtual-scroll-viewport>
-  </div>
-
+        itemSize="40">
+        <mat-nav-list dense>
+          <single-dataset-list-view
+            *cdkVirtualFor="let dataset of filteredDataEntry; trackBy: trackbyFn; templateCacheSize: 0"
+            (click)="showFocusedDataset(dataset)"
+            [kgSchema]="(dataset.fullId | getKgSchemaIdFromFullIdPipe)[0]"
+            [kgId]="(dataset.fullId | getKgSchemaIdFromFullIdPipe)[1]"
+            [dataset]="dataset"
+            [ripple]="true">
+    
+          </single-dataset-list-view>
+        </mat-nav-list>
+      </cdk-virtual-scroll-viewport>
+    </mat-card-content>
   </ng-container>
 </ng-template>
 
@@ -150,13 +107,63 @@
 </ng-template>
 
 <ng-template #detailDataset>
+  <single-dataset-view [dataset]="focusedDataset">
+  </single-dataset-view>
+</ng-template>
 
-  <single-dataset-view
-    [prefetched]="focusedDataset">
+<!-- modality picker / filter -->
+<ng-template #modalitySelector>
+  <mat-accordion class="flex-grow-0 flex-shrink-0">
+
+    <!-- currently selected regions -->
+    <mat-expansion-panel
+      [disabled]="regions.length === 0"
+      #selectedRegionExpansionPanel
+      hideToggle>
+      <mat-expansion-panel-header>
+        <mat-panel-title>
+          {{ regions.length > 0 ? regions.length : 'No' }} region{{ regions.length > 1 ? 's' : '' }} selected
+        </mat-panel-title>
+
+        <mat-panel-description class="d-flex flex-row justify-content-end align-items-center">
+          <i class="fas fa-brain"></i>
+        </mat-panel-description>
+      </mat-expansion-panel-header>
+
+      <div class="h-10em">
+        <currently-selected-regions class="h-100 d-block">
+        </currently-selected-regions>
+      </div>
+    </mat-expansion-panel>
+
+    <!-- Filters -->
+    <mat-expansion-panel hideToggle>
+
+      <mat-expansion-panel-header class="align-items-center">
+        <mat-panel-title>
+          <span *ngIf="visibleCountedDataM && visibleCountedDataM.length > 0; else defaultCount"> 
+            {{ (dataentries | filterDataEntriesByMethods : visibleCountedDataM).length }} filtered datasets
+          </span>
+          <ng-template #defaultCount>
+            {{ dataentries.length }} related datasets
+          </ng-template>
+        </mat-panel-title>
+
+        <mat-panel-description class="d-flex flex-row justify-content-end align-items-center">
+          <i class="fas fa-filter"></i>
+        </mat-panel-description>
+
+      </mat-expansion-panel-header>
+
+      <ng-container *ngTemplateOutlet="modalityPicker">
+      </ng-container>
+
+    </mat-expansion-panel>
+  </mat-accordion>
 
-  </single-dataset-view>
 </ng-template>
-  
+
+<!-- currently unused -->
 <ng-template #history>
   <mat-list>
     <mat-list-item *ngFor="let item of (history$ | async)">
diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts
new file mode 100644
index 000000000..29f499577
--- /dev/null
+++ b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts
@@ -0,0 +1,28 @@
+import { Component, ChangeDetectionStrategy, ChangeDetectorRef} from "@angular/core";
+import { 
+  SingleDatasetBase,
+  DatabrowserService,
+  KgSingleDatasetService,
+  AtlasViewerConstantsServices
+} from "../singleDataset.base";
+
+@Component({
+  selector: 'single-dataset-view',
+  templateUrl: './singleDataset.template.html',
+  styleUrls: [
+    `./singleDataset.style.css`
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class SingleDatasetView extends SingleDatasetBase{
+
+  constructor(
+    dbService: DatabrowserService,
+    singleDatasetService: KgSingleDatasetService,
+    cdr: ChangeDetectorRef,
+    constantService: AtlasViewerConstantsServices
+  ){
+    super(dbService, singleDatasetService, cdr, constantService)
+  }
+}
diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.style.css b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.style.css
similarity index 100%
rename from src/ui/databrowserModule/singleDataset/singleDataset.style.css
rename to src/ui/databrowserModule/singleDataset/detailedView/singleDataset.style.css
diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.template.html b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html
similarity index 100%
rename from src/ui/databrowserModule/singleDataset/singleDataset.template.html
rename to src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html
diff --git a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts
new file mode 100644
index 000000000..1f7811c6e
--- /dev/null
+++ b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts
@@ -0,0 +1,27 @@
+import { Component,ChangeDetectionStrategy, ChangeDetectorRef } from "@angular/core";import { 
+  SingleDatasetBase,
+  DatabrowserService,
+  KgSingleDatasetService,
+  AtlasViewerConstantsServices
+} from "../singleDataset.base";
+
+@Component({
+  selector: 'single-dataset-list-view',
+  templateUrl: './singleDatasetListView.template.html',
+  styleUrls: [
+    './singleDatasetListView.style.css'
+  ],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+
+export class SingleDatasetListView extends SingleDatasetBase {
+
+  constructor(
+    dbService: DatabrowserService,
+    singleDatasetService: KgSingleDatasetService,
+    cdr: ChangeDetectorRef,
+    constantService: AtlasViewerConstantsServices
+  ){
+    super(dbService, singleDatasetService, cdr, constantService)
+  }
+}
\ No newline at end of file
diff --git a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.style.css b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.style.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html
new file mode 100644
index 000000000..a98815d99
--- /dev/null
+++ b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html
@@ -0,0 +1,55 @@
+<mat-list-item [matTooltip]="name">
+
+  <small mat-line>
+    {{ name }}
+  </small>
+
+  <!-- preview -->
+  <button mat-icon-button
+    *ngIf="preview"
+    iav-stop="click mousedown"
+    (click)="showPreviewList(previewFilesListTemplate)">
+    <i class="far fa-eye"></i>
+  </button>
+
+  <button mat-icon-button>
+    <i class="fas fa-info"></i>
+  </button>
+</mat-list-item>
+
+<ng-template #previewFilesListTemplate>
+  <preview-component
+    (previewFile)="handlePreviewFile($event)"
+    [datasetName]="name">
+  </preview-component>
+</ng-template>
+
+<ng-template #fullIcons>
+
+  <!-- references -->
+  <a *ngFor="let kgRef of kgReference"
+    [href]="kgRef | doiParserPipe"
+    target="_blank"
+    iav-stop="click mousedown"
+    mat-icon-button>
+    <mat-icon fontSet="fas" fontIcon="fa-external-link-alt"></mat-icon>
+  </a>
+
+  <!-- pin dataset -->
+  <button mat-icon-button
+    *ngIf="downloadEnabled"
+    iav-stop="click mousedown"
+    (click)="toggleFav()"
+    [color]="(favedDataentries$ | async | datasetIsFaved : dataset) ? 'primary' : 'basic'">
+    <i class="fas fa-thumbtack"></i>
+  </button>
+
+  <!-- download dataset -->
+  <button mat-icon-button
+    *ngIf="downloadEnabled"
+    iav-stop="click mousedown"
+    (click)="downloadZipFromKg()"
+    [disabled]="downloadInProgress">
+    <i class="fas" [ngClass]="!downloadInProgress? 'fa-download' :'fa-spinner fa-pulse'"></i>
+  </button>
+</ng-template>
\ No newline at end of file
diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.component.ts b/src/ui/databrowserModule/singleDataset/singleDataset.base.ts
similarity index 87%
rename from src/ui/databrowserModule/singleDataset/singleDataset.component.ts
rename to src/ui/databrowserModule/singleDataset/singleDataset.base.ts
index 3cb41fc60..034d7e250 100644
--- a/src/ui/databrowserModule/singleDataset/singleDataset.component.ts
+++ b/src/ui/databrowserModule/singleDataset/singleDataset.base.ts
@@ -1,4 +1,4 @@
-import { Component, Input, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, TemplateRef, Output, EventEmitter } from "@angular/core";
+import { Input, OnInit, ChangeDetectorRef, TemplateRef, Output, EventEmitter } from "@angular/core";
 import { KgSingleDatasetService } from "../kgSingleDatasetService.service";
 import { Publication, File, DataEntry, ViewerPreviewFile } from 'src/services/state/dataStore.store'
 import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service";
@@ -6,15 +6,14 @@ import { HumanReadableFileSizePipe } from "src/util/pipes/humanReadableFileSize.
 import { DatabrowserService } from "../databrowser.service";
 import { Observable } from "rxjs";
 
-@Component({
-  selector: 'single-dataset-view',
-  templateUrl: './singleDataset.template.html',
-  styleUrls: [
-    `./singleDataset.style.css`
-  ],
-  changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class SingleDatasetView implements OnInit {
+export {
+  DatabrowserService,
+  KgSingleDatasetService,
+  ChangeDetectorRef,
+  AtlasViewerConstantsServices
+}
+
+export class SingleDatasetBase implements OnInit {
 
   @Input() ripple: boolean = false
 
@@ -29,7 +28,7 @@ export class SingleDatasetView implements OnInit {
   @Input() kgSchema?: string
   @Input() kgId?: string
 
-  @Input() prefetched: any = null
+  @Input() dataset: any = null
   @Input() simpleMode: boolean = false
 
   @Output() previewingFile: EventEmitter<ViewerPreviewFile> = new EventEmitter()
@@ -54,8 +53,6 @@ export class SingleDatasetView implements OnInit {
   public downloadInProgress = false
 
   public favedDataentries$: Observable<DataEntry[]>
-  public dataset: any
-
   constructor(
     private dbService: DatabrowserService,
     private singleDatasetService: KgSingleDatasetService,
@@ -66,9 +63,9 @@ export class SingleDatasetView implements OnInit {
   }
 
   ngOnInit() {
-    const { kgId, kgSchema, prefetched } = this
-    if ( prefetched ) {
-      const { name, description, kgReference, publications, files, preview, ...rest } = prefetched
+    const { kgId, kgSchema, dataset } = this
+    if ( dataset ) {
+      const { name, description, kgReference, publications, files, preview, ...rest } = dataset
       this.name = name
       this.description = description
       this.kgReference = kgReference
@@ -76,7 +73,6 @@ export class SingleDatasetView implements OnInit {
       this.files = files
       this.preview = preview
       
-      this.dataset = prefetched
       return
     }
     if (!kgSchema || !kgId) return
diff --git a/src/ui/databrowserModule/util/datasetIsFaved.pipe.ts b/src/ui/databrowserModule/util/datasetIsFaved.pipe.ts
index 2befe6fed..35ec1bea3 100644
--- a/src/ui/databrowserModule/util/datasetIsFaved.pipe.ts
+++ b/src/ui/databrowserModule/util/datasetIsFaved.pipe.ts
@@ -6,6 +6,7 @@ import { DataEntry } from "src/services/stateStore.service";
 })
 export class DatasetIsFavedPipe implements PipeTransform{
   public transform(favedDataEntry: DataEntry[], dataentry: DataEntry):boolean{
+    if (!dataentry) return false
     return favedDataEntry.findIndex(ds => ds.id === dataentry.id) >= 0
   }
 }
\ No newline at end of file
diff --git a/src/ui/menuicons/menuicons.component.ts b/src/ui/menuicons/menuicons.component.ts
index e71f41c69..2c1da620c 100644
--- a/src/ui/menuicons/menuicons.component.ts
+++ b/src/ui/menuicons/menuicons.component.ts
@@ -21,7 +21,6 @@ import { PluginServices, PluginManifest } from "src/atlasViewer/atlasViewer.plug
 import { Store, select } from "@ngrx/store";
 import { Observable, combineLatest, Subscription } from "rxjs";
 import { map, shareReplay, startWith } from "rxjs/operators";
-import { SHOW_SIDEBAR_TEMPLATE } from "src/services/state/uiState.store";
 import { LayerBrowser } from "../layerbrowser/layerbrowser.component";
 import { MatDialogRef, MatDialog } from "@angular/material";
 import { NgLayerInterface } from "src/atlasViewer/atlasViewer.component";
@@ -166,19 +165,6 @@ export class MenuIconsBar implements OnInit, OnDestroy {
   }
 
   ngOnInit(){
-    /**
-     * on opening nifti volume, collapse side bar
-     */
-    this.subscriptions.push(
-      this.singleDatasetService.previewingFile$.subscribe(({ file }) => {
-        if (determinePreviewFileType(file) === PREVIEW_FILE_TYPES.NIFTI) {
-          this.store.dispatch({
-            type: SHOW_SIDEBAR_TEMPLATE,
-            sidebarTemplate: null
-          })
-        }
-      })
-    )
   }
 
   ngOnDestroy(){
@@ -263,10 +249,7 @@ export class MenuIconsBar implements OnInit, OnDestroy {
   }
 
   public showKgSearchSideNav(kgSearchTemplate: TemplateRef<any> = null){
-    this.store.dispatch({
-      type: SHOW_SIDEBAR_TEMPLATE,
-      sidebarTemplate: kgSearchTemplate
-    })
+    
   }
 
   handleNonbaseLayerEvent(layers: NgLayerInterface[]){
diff --git a/src/ui/menuicons/menuicons.template.html b/src/ui/menuicons/menuicons.template.html
index aa83120e4..2db3ad3d3 100644
--- a/src/ui/menuicons/menuicons.template.html
+++ b/src/ui/menuicons/menuicons.template.html
@@ -1,6 +1,4 @@
 <div class="w-0 ml-4 d-flex flex-column align-items-start" root>
-  <logo-container *ngIf="!isMobile">
-  </logo-container>
   
   <!-- hide icons when templates has yet been selected -->
   <ng-template [ngIf]="selectedTemplate$ | async">
diff --git a/src/ui/searchSideNav/searchSideNav.component.ts b/src/ui/searchSideNav/searchSideNav.component.ts
new file mode 100644
index 000000000..d1629dbd8
--- /dev/null
+++ b/src/ui/searchSideNav/searchSideNav.component.ts
@@ -0,0 +1,77 @@
+import { Component, Output, EventEmitter, OnInit, OnDestroy } from "@angular/core";
+import { MatDialogRef, MatDialog } from "@angular/material";
+import { NgLayerInterface } from "src/atlasViewer/atlasViewer.component";
+import { LayerBrowser } from "../layerbrowser/layerbrowser.component";
+import { Observable, Subscription } from "rxjs";
+import { Store, select } from "@ngrx/store";
+import { map, startWith, scan, filter, mapTo } from "rxjs/operators";
+
+@Component({
+  selector: 'search-side-nav',
+  templateUrl: './searchSideNav.template.html',
+  styleUrls:[
+    './searchSideNav.style.css'
+  ]
+})
+
+export class SearchSideNav implements OnInit, OnDestroy {
+  public showDataset: boolean = false
+  public availableDatasets: number = 0
+
+  private subscriptions: Subscription[] = []
+  private layerBrowserDialogRef: MatDialogRef<any>
+
+  @Output() dismiss: EventEmitter<any> = new EventEmitter()
+  @Output() open: EventEmitter<any> = new EventEmitter()
+
+  public autoOpenSideNav$: Observable<any>
+
+  constructor(
+    private dialog: MatDialog,
+    store$: Store<any>
+  ){
+    this.autoOpenSideNav$ = store$.pipe(
+      select('viewerState'),
+      select('regionsSelected'),
+      map(arr => arr.length),
+      startWith(0),
+      scan((acc, curr) => [curr, ...acc], []),
+      filter(([curr, prev]) => prev === 0 && curr > 0),
+      mapTo(true)
+    )
+  }
+
+  ngOnInit(){
+    this.subscriptions.push(
+      this.autoOpenSideNav$.subscribe(() => {
+        this.open.emit(true)
+        this.showDataset = true
+      })
+    )
+  }
+
+  ngOnDestroy(){
+    while(this.subscriptions.length > 0) {
+      this.subscriptions.pop().unsubscribe()
+    }
+  }
+
+  handleNonbaseLayerEvent(layers: NgLayerInterface[]){
+    if (layers.length  === 0) {
+      this.layerBrowserDialogRef && this.layerBrowserDialogRef.close()
+      this.layerBrowserDialogRef = null
+      return  
+    }
+    if (this.layerBrowserDialogRef) return
+
+    this.dismiss.emit(true)
+    this.layerBrowserDialogRef = this.dialog.open(LayerBrowser, {
+      hasBackdrop: false,
+      autoFocus: false,
+      position: {
+        top: '1em'
+      },
+      disableClose: true
+    })
+  }
+}
\ No newline at end of file
diff --git a/src/ui/searchSideNav/searchSideNav.style.css b/src/ui/searchSideNav/searchSideNav.style.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/ui/searchSideNav/searchSideNav.template.html b/src/ui/searchSideNav/searchSideNav.template.html
new file mode 100644
index 000000000..10d9c6953
--- /dev/null
+++ b/src/ui/searchSideNav/searchSideNav.template.html
@@ -0,0 +1,60 @@
+<div class="d-flex flex-column h-100">
+  <viewer-state-controller class="pe-all mb-2" #viewerStateController>
+
+    <!-- content append -->
+    <ng-container card-content="append">
+      <mat-card-content (focusin)="showDataset = true">
+        <region-text-search-autocomplete
+          [showBadge]="true"
+          class="d-block w-100">
+        </region-text-search-autocomplete>
+      </mat-card-content>
+    </ng-container>
+
+    <!-- footer content -->
+    <div class="d-flex flex-row justify-content-center" card-footer>
+      <button mat-stroked-button
+        *ngIf="!showDataset"
+        (click)="showDataset = true"
+        class="m-1 flex-grow-1 overflow-hidden" >
+        <i class="fas fa-chevron-down"></i>
+        <ng-container *ngIf="viewerStateController.regionsSelected$ | async as regionsSelected">
+          {{ regionsSelected.length === 0 ? 'Explore the current view' : regionsSelected.length === 1 ? ('Explore ' + regionsSelected[0].name) : ('Explore selected regions (' + regionsSelected.length + ' selected)') }}
+        </ng-container>
+      </button>
+    </div>
+  </viewer-state-controller>
+
+  <data-browser
+    class="pe-all"
+    *ngIf="showDataset"
+    [template]="viewerStateController.templateSelected$ | async"
+    [parcellation]="viewerStateController.parcellationSelected$ | async"
+    [regions]="viewerStateController.regionsSelected$ | async"
+    (dataentriesUpdated)="availableDatasets = $event.length">
+    
+
+    <!-- content prepend -->
+    <ng-container *ngIf="viewerStateController.regionsSelected$ | async as regionsSelected" card-content="prepend">
+      <mat-card-content>
+        <p class="font-weight-bold">
+          {{ regionsSelected.length === 0 ? 'In the current view' : regionsSelected.length === 1 ? regionsSelected[0].name : 'Multi-region selection' }}
+        </p>
+      </mat-card-content>
+    </ng-container>
+
+    <!-- footer content -->
+    <div class="d-flex flex-row justify-content-center" card-footer>
+      <button mat-stroked-button
+        class="m-1"
+        (click)="showDataset = false" >
+        <i class="fas fa-chevron-up"></i>
+      </button>
+    </div>
+  </data-browser>
+</div>
+
+<div [hidden]>
+  <layer-browser (nonBaseLayersChanged)="handleNonbaseLayerEvent($event)" #layerBrowser>
+  </layer-browser>
+</div>
\ No newline at end of file
diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts
index 8ffb79888..884c73ce5 100644
--- a/src/ui/ui.module.ts
+++ b/src/ui/ui.module.ts
@@ -67,6 +67,7 @@ import {ElementOutClickDirective} from "src/util/directives/elementOutClick.dire
 import {SearchItemPreviewComponent} from "src/ui/searchItemPreview/searchItemPreview.component";
 import {SelectedRegionsComponent} from "src/ui/selectedRegions/selectedRegions.component";
 import {FilterWithStringPipe} from "src/util/pipes/filterWithString.pipe";
+import { SearchSideNav } from "./searchSideNav/searchSideNav.component";
 
 @NgModule({
   imports : [
@@ -106,10 +107,12 @@ import {FilterWithStringPipe} from "src/util/pipes/filterWithString.pipe";
     SinglePanel,
     CurrentLayout,
     ViewerStateController,
+
     MaximmisePanelButton,
     SearchPanel,
     SearchItemPreviewComponent,
     SelectedRegionsComponent,
+    SearchSideNav,
 
     /* pipes */
     GroupDatasetByRegion,
@@ -170,7 +173,8 @@ import {FilterWithStringPipe} from "src/util/pipes/filterWithString.pipe";
     KGToS,
     StatusCardComponent,
     SearchPanel,
-    ElementOutClickDirective
+    ElementOutClickDirective,
+    SearchSideNav,
   ]
 })
 
diff --git a/src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.component.ts b/src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.component.ts
new file mode 100644
index 000000000..ca1231d80
--- /dev/null
+++ b/src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.component.ts
@@ -0,0 +1,46 @@
+import { Component } from "@angular/core";
+import { Store, select } from "@ngrx/store";
+import { Observable } from "rxjs";
+import { distinctUntilChanged, startWith } from "rxjs/operators";
+import { DESELECT_REGIONS } from "src/services/state/viewerState.store";
+import { VIEWERSTATE_ACTION_TYPES } from "../viewerState.component";
+
+@Component({
+  selector: 'currently-selected-regions',
+  templateUrl: './currentlySelectedRegions.template.html',
+  styleUrls: [
+    './currentlySelectedRegions.style.css'
+  ]
+})
+
+export class CurrentlySelectedRegions {
+
+  
+  public regionSelected$: Observable<any[]>
+  
+  constructor(
+    private store$: Store<any>
+  ){
+
+    this.regionSelected$ = this.store$.pipe(
+      select('viewerState'),
+      select('regionsSelected'),
+      startWith([]),
+      distinctUntilChanged()
+    )
+  }
+
+  public deselectRegion(event: MouseEvent, region: any){
+    this.store$.dispatch({
+      type: DESELECT_REGIONS,
+      deselectRegions: [region]
+    })
+  }
+
+  public gotoRegion(event: MouseEvent, region:any){
+    this.store$.dispatch({
+      type: VIEWERSTATE_ACTION_TYPES.DOUBLE_CLICK_ON_REGIONHIERARCHY,
+      payload: { region }
+    })
+  }
+}
\ No newline at end of file
diff --git a/src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.style.css b/src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.style.css
new file mode 100644
index 000000000..0a4edb055
--- /dev/null
+++ b/src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.style.css
@@ -0,0 +1,4 @@
+mat-chip-list >>> .mat-chip-list-wrapper
+{
+  height: 100%;
+}
diff --git a/src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.template.html b/src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.template.html
new file mode 100644
index 000000000..d1bf21a6f
--- /dev/null
+++ b/src/ui/viewerStateController/currentlySelectedRegions/currentlySelectedRegions.template.html
@@ -0,0 +1,24 @@
+<mat-chip-list class="d-block w-100 h-100">
+  <cdk-virtual-scroll-viewport
+    class="w-100 h-100 overflow-x-hidden"
+    [itemSize]="32">
+    <mat-chip
+      *cdkVirtualFor="let region of (regionSelected$ | async)"
+      class="w-90">
+      <span class="flex-grow-1 flex-shrink-1 text-truncate">
+        {{ region.name }}
+      </span>
+      <button
+        *ngIf="region.position"
+        (click)="gotoRegion($event, region)"
+        mat-icon-button>
+        <i class="fas fa-map-marked-alt"></i>
+      </button>
+      <button
+        (click)="deselectRegion($event, region)"
+        mat-icon-button>
+        <i class="fas fa-trash"></i>
+      </button>
+    </mat-chip>
+  </cdk-virtual-scroll-viewport>
+</mat-chip-list>
\ No newline at end of file
diff --git a/src/ui/viewerStateController/regionHierachy/regionHierarchy.style.css b/src/ui/viewerStateController/regionHierachy/regionHierarchy.style.css
index 483444c3d..20624e141 100644
--- a/src/ui/viewerStateController/regionHierachy/regionHierarchy.style.css
+++ b/src/ui/viewerStateController/regionHierachy/regionHierarchy.style.css
@@ -25,7 +25,7 @@ div[treeContainer]
 
 [hideScrollbarcontainer] [hideScrollbarInnerContainer]
 {
-  width: calc(100% + 8em);
+  width: calc(100% + 2em);
 }
 
 input[type="text"]
@@ -54,9 +54,3 @@ input[type="text"]
 {
   flex: 0 0 auto;
 }
-
-mat-chip-list >>> .mat-chip-list-wrapper
-{
-  height: 100%;
-  width:110%;
-}
\ No newline at end of file
diff --git a/src/ui/viewerStateController/regionHierachy/regionHierarchy.template.html b/src/ui/viewerStateController/regionHierachy/regionHierarchy.template.html
index 308e53b6b..0e4d9475b 100644
--- a/src/ui/viewerStateController/regionHierachy/regionHierarchy.template.html
+++ b/src/ui/viewerStateController/regionHierachy/regionHierarchy.template.html
@@ -26,34 +26,10 @@
     </div>
 
     <div hideScrollbarcontainer>
-      <mat-chip-list
-        class="w-100 flex-grow-1 flex-shrink-1">
-        <cdk-virtual-scroll-viewport
-          hideScrollbarInnerContainer
-          class="h-100 p-4 overflow-x-hidden"
-          [itemSize]="32">
-          <mat-chip
-            *cdkVirtualFor="let region of (selectedRegions | filterRowsByVisbilityPipe : null : filterTreeBySearch)"
-            class="w-90">
-
-            <span class="dot mr-1 flex-grow-0 flex-shrink-0" [ngStyle]="{backgroundColor: (region | regionBackgroundToRgbPipe)}"></span>
-            <span class="flex-grow-1 flex-shrink-1 text-truncate">
-              {{ region.name }}
-            </span>
-            <button
-              *ngIf="region.position"
-              (click)="gotoRegion(region)"
-              mat-icon-button>
-              <i class="fas fa-map-marked-alt"></i>
-            </button>
-            <button
-              (click)="deselectRegion(region)"
-              mat-icon-button>
-              <i class="fas fa-trash"></i>
-            </button>
-          </mat-chip>
-        </cdk-virtual-scroll-viewport>
-      </mat-chip-list>
+      <currently-selected-regions
+        class="d-block h-100"
+        hideScrollbarInnerContainer>
+      </currently-selected-regions>
     </div>
   </div>
 
diff --git a/src/ui/viewerStateController/regionSearch/regionSearch.component.ts b/src/ui/viewerStateController/regionSearch/regionSearch.component.ts
index 379923331..40ae397d4 100644
--- a/src/ui/viewerStateController/regionSearch/regionSearch.component.ts
+++ b/src/ui/viewerStateController/regionSearch/regionSearch.component.ts
@@ -1,7 +1,7 @@
 import { Component, EventEmitter, Output, ViewChild, ElementRef, TemplateRef, Input } from "@angular/core";
 import { Store, select } from "@ngrx/store";
 import { Observable } from "rxjs";
-import { map, distinctUntilChanged, startWith, withLatestFrom, debounceTime, shareReplay, take } from "rxjs/operators";
+import { map, distinctUntilChanged, startWith, withLatestFrom, debounceTime, shareReplay, take, filter } from "rxjs/operators";
 import { getMultiNgIdsRegionsLabelIndexMap, generateLabelIndexId } from "src/services/stateStore.service";
 import { FormControl } from "@angular/forms";
 import { MatAutocompleteSelectedEvent, MatDialog } from "@angular/material";
@@ -20,8 +20,8 @@ const filterRegionBasedOnText = searchTerm => region => region.name.toLowerCase(
 
 export class RegionTextSearchAutocomplete{
 
-  @Input()
-  public showAutoComplete: boolean = true
+  @Input() public showBadge: boolean = false
+  @Input() public showAutoComplete: boolean = true
 
   @ViewChild('autoTrigger', {read: ElementRef}) autoTrigger: ElementRef 
   @ViewChild('regionHierarchy', {read:TemplateRef}) regionHierarchyTemplate: TemplateRef<any>
@@ -58,6 +58,7 @@ export class RegionTextSearchAutocomplete{
     this.autocompleteList$ = this.formControl.valueChanges.pipe(
       startWith(''),
       debounceTime(200),
+      filter(string => string.length > 0),
       withLatestFrom(this.regionsWithLabelIndex$.pipe(
         startWith([])
       )),
diff --git a/src/ui/viewerStateController/regionSearch/regionSearch.template.html b/src/ui/viewerStateController/regionSearch/regionSearch.template.html
index 8da2718d7..bf67e5428 100644
--- a/src/ui/viewerStateController/regionSearch/regionSearch.template.html
+++ b/src/ui/viewerStateController/regionSearch/regionSearch.template.html
@@ -1,9 +1,9 @@
-<div class="d-inline-flex flex-row align-items-center">
+<div class="w-100 d-inline-flex flex-row align-items-center">
 
   <form *ngIf="showAutoComplete" class="flex-grow-1 flex-shrink-1">
     <mat-form-field class="w-100">
       <input
-        placeholder="Regions"
+        placeholder="Search for regions"
         #autoTrigger
         #trigger="matAutocompleteTrigger"
         type="text"
@@ -26,9 +26,12 @@
   </form>
     
   <button
+    matBadgeColor="accent"
+    [matBadge]="showBadge && (regionsSelected$ | async).length ? (regionsSelected$ | async).length : null"
     class="flex-grow-0 flex-shrink-0"
     (click)="showHierarchy($event)"
-    mat-icon-button color="primary">
+    mat-icon-button
+    color="primary">
     <i class="fas fa-sitemap"></i>
   </button>
 </div>
diff --git a/src/ui/viewerStateController/viewerState.component.ts b/src/ui/viewerStateController/viewerState.component.ts
index a0c33fd2b..b83181f7a 100644
--- a/src/ui/viewerStateController/viewerState.component.ts
+++ b/src/ui/viewerStateController/viewerState.component.ts
@@ -3,7 +3,6 @@ import { Store, select } from "@ngrx/store";
 import { Observable, Subscription } from "rxjs";
 import { distinctUntilChanged, shareReplay, filter } from "rxjs/operators";
 import { SELECT_REGIONS, USER_CONFIG_ACTION_TYPES } from "src/services/stateStore.service";
-import { DESELECT_REGIONS, CHANGE_NAVIGATION } from "src/services/state/viewerState.store";
 import { ToastService } from "src/services/toastService.service";
 import { MatSelectChange, MatBottomSheet, MatBottomSheetRef } from "@angular/material";
 import { DialogService } from "src/services/dialogService.service";
@@ -212,32 +211,6 @@ export class ViewerStateController implements OnInit{
     })
   }
 
-  public deselectRegion(event: MouseEvent, region: any){
-    this.store$.dispatch({
-      type: DESELECT_REGIONS,
-      deselectRegions: [region]
-    })
-  }
-
-  public gotoRegion(event: MouseEvent, region:any){
-    if (region.position) {
-      this.store$.dispatch({
-        type: CHANGE_NAVIGATION,
-        navigation: {
-          position: region.position,
-          animation: {}
-        }
-      })
-    } else {
-      /**
-       * TODO convert to snack bar
-       */
-      this.toastService.showToast(`${region.name} does not have a position defined`, {
-        timeout: 5000,
-        dismissable: true
-      })
-    }
-  }
 }
 
 const ACTION_TYPES = {
diff --git a/src/ui/viewerStateController/viewerState.template.html b/src/ui/viewerStateController/viewerState.template.html
index 8d80cc044..7b4b40449 100644
--- a/src/ui/viewerStateController/viewerState.template.html
+++ b/src/ui/viewerStateController/viewerState.template.html
@@ -1,8 +1,8 @@
 <mat-card>
 
   <!-- template selection -->
-  <mat-card-content class="d-inline-flex flex-nowrap">
-    <mat-form-field>
+  <mat-card-content class="d-inline-flex flex-nowrap w-100">
+    <mat-form-field class="flex-grow-1">
       <mat-label>
         Template
       </mat-label>
@@ -18,9 +18,6 @@
       </mat-select>
     </mat-form-field>
   
-    <!-- padding so that info icon lines up -->
-    <button mat-icon-button class="pe-none invisible"></button>
-  
     <ng-container *ngIf="templateSelected$ | async as templateSelected">
       <!-- show on hover component -->
       <sleight-of-hand
@@ -64,11 +61,11 @@
   </mat-card-content>
 
   <!-- parcellation selection -->
-  <mat-card-content class="d-inline-flex flex-nowrap">
+  <mat-card-content class="d-inline-flex flex-nowrap w-100">
 
     <mat-form-field
       *ngIf="templateSelected$ | async as templateSelected"
-      class="d-inline-flex flex-nowrap">
+      class="flex-grow-1">
       <mat-label>
         Parcellation
       </mat-label>
@@ -84,12 +81,6 @@
       </mat-select>
     </mat-form-field>
   
-    <!-- selected regions -->
-    <region-text-search-autocomplete
-      [showAutoComplete]="false"
-      (focusedStateChanged)="focused = $event">
-    </region-text-search-autocomplete>
-  
     <ng-container *ngIf="parcellationSelected$ | async as parcellationSelected">
       <!-- show on hover component -->
       <sleight-of-hand
@@ -125,93 +116,17 @@
               [kgId]="originDataset && originDataset.kgId">
             </single-dataset-view>
           </div>
-          
         </div>
-      </sleight-of-hand>  
-  
+      </sleight-of-hand>
     </ng-container>
-  
-  </mat-card-content>
-
-  <!-- selected regions -->
-  <mat-card-content>
-    <mat-chip-list *ngIf="(regionsSelected$ | async)?.length > 0 ">
-      <cdk-virtual-scroll-viewport
-        class="w-100 h-10em overflow-x-hidden"
-        [itemSize]="32">
-        <mat-chip
-          *cdkVirtualFor="let region of (regionsSelected$ | async)"
-          class="w-90">
-          <span class="flex-grow-1 flex-shrink-1 text-truncate">
-            {{ region.name }}
-          </span>
-          <button
-            *ngIf="region.position"
-            (click)="gotoRegion($event, region)"
-            mat-icon-button>
-            <i class="fas fa-map-marked-alt"></i>
-          </button>
-          <button
-            (click)="deselectRegion($event, region)"
-            mat-icon-button>
-            <i class="fas fa-trash"></i>
-          </button>
-        </mat-chip>
-      </cdk-virtual-scroll-viewport>
-    </mat-chip-list>
-  
-    <!-- place holder when no regions has been selected -->
-    <span class="muted"  *ngIf="(regionsSelected$ | async).length === 0">
-      No regions selected. Double click on any regions in the viewer, or use the search tool to select regions of interest.
-    </span>
   </mat-card-content>
 
-  <!-- control btns -->
-  <mat-card-actions class="d-flex justify-content-between">
-    
-    <div class="d-flex">
-      <!-- save  -->
-      <button
-        matTooltip="Bookmark this selection of regions"
-        matTooltipPosition="below"
-        mat-button
-        (click)="saveSelection($event)"
-        color="primary">
-        <i class="far fa-bookmark"></i>
-        
-      </button>
+  <ng-content select="[card-content='append']">
+  </ng-content>
 
-      <!-- load -->
-      <button
-        (click)="loadSelection($event)"
-        matTooltip="Load a bookmarked region selection"
-        matTooltipPosition="below"
-        mat-button
-        color="primary"
-        [disabled]="(savedRegionsSelections$ | async)?.length === 0">
-        <i
-          matBadgeColor="accent"
-          [matBadgeOverlap]="false"
-          [matBadge]="(savedRegionsSelections$ | async)?.length > 0 ? (savedRegionsSelections$ | async)?.length : null"
-          class="fas fa-folder-open"></i>
-        
-      </button>
-    </div>
-
-    <!-- deselect all  -->
-    <button
-      (click)="deselectAllRegions($event)"
-      matTooltip="Deselect all selected regions"
-      matTooltipPosition="below"
-      mat-raised-button
-      color="warn"
-      [disabled]="(regionsSelected$ | async)?.length === 0">
-      <i class="fas fa-trash"></i>
-    </button>
-  </mat-card-actions>
-  
   <mat-card-footer>
-
+    <ng-content select="[card-footer]">
+    </ng-content>
   </mat-card-footer>
 </mat-card>
 
diff --git a/src/util/directives/stopPropagation.directive.ts b/src/util/directives/stopPropagation.directive.ts
new file mode 100644
index 000000000..e211a5668
--- /dev/null
+++ b/src/util/directives/stopPropagation.directive.ts
@@ -0,0 +1,47 @@
+import { Directive, Input, ElementRef, OnDestroy, OnChanges } from "@angular/core";
+
+const VALID_EVENTNAMES = new Set([
+  'mousedown',
+  'mouseup',
+  'click',
+  'mouseenter',
+  'mouseleave'
+])
+
+const stopPropagation = ev => ev.stopPropagation()
+
+@Directive({
+  selector: '[iav-stop]'
+})
+
+export class StopPropagationDirective implements OnChanges, OnDestroy{
+
+  @Input('iav-stop') stopString: string = ''
+
+  private destroyCb: (() => void)[] = []
+
+  constructor(private el: ElementRef){}
+
+  ngOnChanges(){
+    
+    this.ngOnDestroy()
+
+    const element = (this.el.nativeElement as HTMLElement)
+    for (const evName of this.stopString.split(' ')){
+      if(VALID_EVENTNAMES.has(evName)){
+        element.addEventListener(evName, stopPropagation)
+        this.destroyCb.push(() => {
+          element.removeEventListener(evName, stopPropagation)
+        })
+      } else {
+        console.warn(`${evName} is not a valid event name in the supported set: `, VALID_EVENTNAMES)
+      }
+    }
+  }
+
+  ngOnDestroy(){
+    while (this.destroyCb.length > 0) {
+      this.destroyCb.pop()()
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/util/util.module.ts b/src/util/util.module.ts
index c19d9046c..0ec4c182d 100644
--- a/src/util/util.module.ts
+++ b/src/util/util.module.ts
@@ -1,15 +1,18 @@
 import { NgModule } from "@angular/core";
 import { FilterNullPipe } from "./pipes/filterNull.pipe";
 import { FilterRowsByVisbilityPipe } from "src/components/flatTree/filterRowsByVisibility.pipe";
+import { StopPropagationDirective } from "./directives/stopPropagation.directive";
 
 @NgModule({
   declarations: [
     FilterNullPipe,
-    FilterRowsByVisbilityPipe
+    FilterRowsByVisbilityPipe,
+    StopPropagationDirective
   ],
   exports: [
     FilterNullPipe,
-    FilterRowsByVisbilityPipe
+    FilterRowsByVisbilityPipe,
+    StopPropagationDirective
   ]
 })
 
-- 
GitLab