Skip to content

Commit

Permalink
Add explorer tags, searchbar, and search results table.
Browse files Browse the repository at this point in the history
  • Loading branch information
srpiatt committed Mar 12, 2024
1 parent f7c8da3 commit 026971c
Show file tree
Hide file tree
Showing 17 changed files with 463 additions and 28 deletions.
12 changes: 12 additions & 0 deletions src/app.postcss
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ section.main-content {
margin: 2rem auto 0;
}

section.main-content.w-full {
width: calc(100% - 3rem);
margin: 2rem 1.5rem;
}

/* Use a media query to add a breakpoint at 800px: */
@media screen and (max-width: 1366px) {
section.main-content {
Expand Down Expand Up @@ -208,6 +213,13 @@ nav#page-navigation a:active {
vertical-align: middle;
}

.accordion-item {
border-top: solid 1px rgb(var(--color-surface-400));
}
.accordion {
border-bottom: solid 1px rgb(var(--color-surface-400));
}

#user-token span.badge.expired {
@apply variant-filled-error;
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/Content.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script lang="ts">
export let title: string = '';
export let subtitle: string = '';
export let full = false;
</script>

<section class="main-content">
<section class={`main-content ${full && 'w-full'}`}>
{#if title}<h1 class="mb-4">{title}</h1>{/if}
{#if subtitle}<p class="subtitle mb-4 text-center">{subtitle}</p>{/if}
<slot />
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/dataset/cell/Actions.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { page } from '$app/stores';
import DataSetStore from '$lib/stores/dataset';
import DataSetStore from '$lib/stores/Dataset';
export let data = { cell: '', row: { archived: false } };
let toggleButton: HTMLButtonElement;
Expand Down
21 changes: 14 additions & 7 deletions src/lib/components/datatable/Table.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
interface Column {
dataElement: string;
label: string;
class?: string;
sort?: boolean;
filter?: boolean;
}
// Parameters
export let filter = false;
export let search = false;
export let sort = false;
export let defaultRowsPerPage = 5;
export let columns: Column[] = [];
export let cellOverides: Indexable = {};
Expand All @@ -39,12 +40,18 @@
<thead>
<tr>
{#each columns as column}
{#if sort}
<ThSort {handler} orderBy={column.dataElement}>{column.label}</ThSort>
{:else if filter}
<ThFilter {handler} filterBy={column.dataElement} />
{#if column.sort}
<ThSort {handler} class={`bg-primary-300 ${column.class}`} orderBy={column.dataElement}
>{column.label}</ThSort
>
{:else if column.filter}
<ThFilter
{handler}
class={`bg-primary-300 ${column.class}`}
filterBy={column.dataElement}
/>
{:else}
<th>{column.label}</th>
<th class={`bg-primary-300 ${column.class}`}>{column.label}</th>
{/if}
{/each}
</tr>
Expand Down
6 changes: 5 additions & 1 deletion src/lib/components/datatable/accessories/Filter.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
<script lang="ts">
import type { DataHandler } from '@vincjo/datatables';
let clazz = '';
export { clazz as class };
export let handler: DataHandler;
export let filterBy: string;
let value: string;
</script>

<th>
<th class={clazz}>
<input
class="input text-sm w-full"
type="text"
Expand Down
4 changes: 3 additions & 1 deletion src/lib/components/datatable/accessories/Sort.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<script lang="ts">
import type { DataHandler } from '@vincjo/datatables';
let clazz = '';
export { clazz as class };
export let handler: DataHandler;
export let orderBy: string;
const sorted = handler.getSort();
</script>

<th on:click={() => handler.sort(orderBy)} class="cursor-pointer select-none">
<th on:click={() => handler.sort(orderBy)} class={`cursor-pointer select-none ${clazz}`}>
<div class="flex h-full items-center justify-start gap-x-2">
<slot />
{#if $sorted.identifier === orderBy}
Expand Down
51 changes: 51 additions & 0 deletions src/lib/components/explorer/Checkbox.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script lang="ts">
import type { Indexable } from '$lib/types';
import { TagCheckbox } from '$lib/models/Search';
import SearchStore from '$lib/stores/Search';
const { updateTag } = SearchStore;
const icons: Indexable = {
[TagCheckbox.Include]: {
inactive: 'fa-regular fa-lg fa-square-plus text-primary-700',
active: 'fa-solid fa-lg fa-square-plus text-sky-600',
},
[TagCheckbox.Exclude]: {
inactive: 'fa-regular fa-lg fa-square-minus text-primary-700',
active: 'fa-solid fa-lg fa-square-minus text-red-600',
},
};
export let type = '';
export let tag = '';
export let state: TagCheckbox = TagCheckbox.Default;
const normalize = (n: string) => n.replaceAll(' ', '_').toLowerCase();
const elementId = `${normalize(type)}-${normalize(tag)}`;
function newTagState(stateToggle: TagCheckbox) {
const newState = state == stateToggle ? TagCheckbox.Default : stateToggle;
updateTag(type, tag, newState);
}
</script>

<div class="flex">
<div class="flex-auto pl-2">
{tag}
</div>
<div class="filter-group flex-none">
{#each [TagCheckbox.Include, TagCheckbox.Exclude] as boxType}
<span
role="checkbox"
tabindex="0"
id={`tag-${elementId}-${boxType}`}
title={`${boxType} ${tag} tag in search`}
aria-checked={state == boxType}
on:click={() => newTagState(boxType)}
on:keydown={(e) => e.key === ' ' && newTagState(boxType)}
><i class={icons[boxType][state == boxType ? 'active' : 'inactive']}></i></span
>
<label for={`tag-${elementId}-${boxType}`} class="sr-only">
<span>{boxType}</span>
</label>
{/each}
</div>
</div>
28 changes: 28 additions & 0 deletions src/lib/components/explorer/cell/Actions.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
// export let data = { cell: '', row: { archived: false } };
</script>

<button
type="button"
title="Information"
class="bg-initial text-secondary-600 hover:text-primary-600"
>
<i class="fa-solid fa-circle-info fa-xl"></i>
</button>
<button type="button" title="Filter" class="bg-initial text-secondary-600 hover:text-primary-600">
<i class="fa-solid fa-filter fa-xl"></i>
</button>
<button
type="button"
title="Data Hierarchy"
class="bg-initial text-secondary-600 hover:text-primary-600"
>
<i class="fa-solid fa-sitemap fa-xl"></i>
</button>
<button
type="button"
title="Data Export"
class="bg-initial text-secondary-600 hover:text-primary-600"
>
<i class="fa-solid fa-right-from-bracket fa-xl"></i>
</button>
45 changes: 45 additions & 0 deletions src/lib/models/Search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Indexable } from '$lib/types';

export enum TagCheckbox {
Include = 'include',
Exclude = 'exclude',
Default = 'default',
}

export type SearchTag = Indexable & {
name: string;
type: string;
state: TagCheckbox;
};

export type SearchTagType = Indexable & {
title: string;
tags: SearchTag[];
};

export type SearchResult = Indexable & {
id: string;
name: string;
description: string;
};

export function mapTags(typeData: { title: string; tags: string[] }): SearchTagType {
return {
title: typeData.title,
tags: typeData.tags.map((tag) => ({
name: tag,
type: typeData.title,
state: TagCheckbox.Default,
})),
};
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function mapSearchResults(data: any): SearchResult {
const segments = data.name.split('\\').filter((x: string) => x);
return {
id: data.name,
name: segments[segments.length - 1],
description: `categorical: ${data.categorical}, patients: ${data.patientCount}`,
};
}
File renamed without changes.
58 changes: 58 additions & 0 deletions src/lib/stores/Search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { get, writable, type Writable } from 'svelte/store';

import {
mapTags,
type SearchTagType,
mapSearchResults,
type SearchResult,
TagCheckbox,
} from '$lib/models/Search';
import * as api from '$lib/api';

const searchUrl = 'picsure/search/bf638674-053b-46c4-96a1-4cd6c8395248';

const tags: Writable<SearchTagType[]> = writable([]);
const searchTerm: Writable<string> = writable('');
const searchResults: Writable<SearchResult[]> = writable([]);

const tagsMock = [
{
title: 'Dataset',
tags: ['NHANES', '1000 Genomes'],
},
{
title: 'Category',
tags: ['laboratory', 'questionaire'],
},
{
title: 'Data Type',
tags: ['type 1', 'type 2'],
},
];

async function search(search: string) {
if (!search) return;
const response = await api.post(searchUrl, { query: search });
tags.set(tagsMock.map(mapTags));
searchResults.set(Object.values(response.results.phenotypes).map(mapSearchResults));
searchTerm.set(search);
}

// TODO: Update include/exclude method to send api update to the server, maybe with a debounce?
function updateTag(type: string, tag: string, newState: TagCheckbox) {
const newTags = get(tags);
const typeIndex = newTags.findIndex((t) => t.title == type);
const tagIndex = newTags[typeIndex].tags.findIndex((t) => t.name == tag);

newTags[typeIndex].tags[tagIndex].state = newState;
tags.set(newTags);
}

export default {
subscribe: tags.subscribe,
tags,
searchTerm,
searchResults,
search,
updateTag,
};
2 changes: 1 addition & 1 deletion src/routes/dataset/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { ProgressBar } from '@skeletonlabs/skeleton';
import type { Indexable } from '$lib/types';
import DataSetStore from '$lib/stores/dataset';
import DataSetStore from '$lib/stores/Dataset';
import ErrorAlert from '$lib/components/ErrorAlert.svelte';
import Content from '$lib/components/Content.svelte';
import Datatable from '$lib/components/datatable/Table.svelte';
Expand Down
2 changes: 1 addition & 1 deletion src/routes/dataset/[uuid]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { page } from '$app/stores';
import type { DataSet } from '$lib/models/Dataset';
import DataSetStore from '$lib/stores/dataset';
import DataSetStore from '$lib/stores/Dataset';
import Content from '$lib/components/Content.svelte';
import ErrorAlert from '$lib/components/ErrorAlert.svelte';
Expand Down
Loading

0 comments on commit 026971c

Please sign in to comment.