-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ALS-6161 + ALS-6162] Search, Filters, Exports
- Loading branch information
Showing
40 changed files
with
2,014 additions
and
387 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,15 @@ | ||
version: 2 | ||
updates: | ||
- package-ecosystem: "npm" | ||
directory: "/" | ||
- package-ecosystem: 'npm' | ||
directory: '/' | ||
schedule: | ||
interval: "weekly" | ||
interval: 'weekly' | ||
commit-message: | ||
prefix: "[npm] " | ||
prefix: '[npm] ' | ||
|
||
- package-ecosystem: "docker" | ||
directory: "/" | ||
- package-ecosystem: 'docker' | ||
directory: '/' | ||
schedule: | ||
interval: "weekly" | ||
interval: 'weekly' | ||
commit-message: | ||
prefix: "[docker] " | ||
prefix: '[docker] ' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<script lang="ts"> | ||
import { getModalStore } from '@skeletonlabs/skeleton'; | ||
const modalStore = getModalStore(); | ||
</script> | ||
|
||
{#if $modalStore[0]} | ||
<div class="card p-4 w-modal shadow-xl space-y-4"> | ||
<header data-testid="modal-wrapper-header" class="text-2xl font-bold"> | ||
{$modalStore[0].title ?? '(title missing)'} | ||
<button class="float-right" on:click={() => modalStore.close()}>×</button> | ||
</header> | ||
<svelte:component | ||
this={$modalStore[0].meta.component} | ||
existingFilter={$modalStore[0].meta.existingFilter} | ||
/> | ||
</div> | ||
{/if} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
<script lang="ts"> | ||
import { quintOut } from 'svelte/easing'; | ||
import { crossfade } from 'svelte/transition'; | ||
import { flip } from 'svelte/animate'; | ||
const [send, receive] = crossfade({ | ||
fallback(node) { | ||
const style = getComputedStyle(node); | ||
const transform = style.transform === 'none' ? '' : style.transform; | ||
return { | ||
easing: quintOut, | ||
css: (t) => ` | ||
transform: ${transform} scale(${t}); | ||
opacity: ${t} | ||
`, | ||
}; | ||
}, | ||
}); | ||
export let options: string[] = []; | ||
export let allOptions: string[] = options; | ||
export let selectedOptions: string[] = []; | ||
export let showSelectAll: boolean = true; | ||
export let loadMore: (searchTerm: string) => Promise<string[]>; | ||
let searchInput: string = ''; | ||
let selectedOptionEndLocation = 20; | ||
let optionsContainer: HTMLElement; | ||
let selelectedOptionsContainer: HTMLElement; | ||
async function handleScroll() { | ||
if (!optionsContainer) return; | ||
if (loadMore === undefined) return; //todo test this | ||
if (shouldLoadMore(optionsContainer)) { | ||
let nextOptions = await loadMore(searchInput); | ||
if (nextOptions && Array.isArray(nextOptions) && nextOptions.length > 0) { | ||
options = [...options, ...nextOptions]; | ||
} | ||
} | ||
} | ||
function onSelect(option: string) { | ||
options = options.filter((o) => o !== option); | ||
selectedOptions = [...selectedOptions, option]; | ||
if (elementHasScrollbar(optionsContainer)) { | ||
handleScroll(); | ||
} | ||
} | ||
function onUnselect(option: string) { | ||
selectedOptions = selectedOptions.filter((o) => o !== option); | ||
options = [option, ...options]; | ||
} | ||
function elementHasScrollbar(element: HTMLElement) { | ||
return element.scrollHeight > element.clientHeight; | ||
} | ||
function loadMoreSelectedOptions() { | ||
if (!selelectedOptionsContainer) return; | ||
if (shouldLoadMore(selelectedOptionsContainer)) { | ||
selectedOptionEndLocation = selectedOptionEndLocation + 20; | ||
} | ||
} | ||
function shouldLoadMore(element: HTMLElement) { | ||
const scrollTop = element.scrollTop; | ||
const containerHeight = element.clientHeight; | ||
const contentHeight = element.scrollHeight; | ||
const scrollBuffer = 30; | ||
return (contentHeight - (scrollTop + containerHeight) <= scrollBuffer); | ||
} | ||
$: filteredOptions = options?.filter((option) => | ||
option.toLowerCase().includes(searchInput.toLowerCase()), | ||
); | ||
$: displayedSelectedOptions = selectedOptions.slice(0, selectedOptionEndLocation); | ||
</script> | ||
|
||
<div data-testid="optional-selection-list" class="flex w-full"> | ||
<div class="flex flex-1 flex-col h-full p-3 m-1 card"> | ||
<header class="flex pb-1"> | ||
<input | ||
class="input" | ||
type="search" | ||
name="search" | ||
bind:value={searchInput} | ||
placeholder="Search..." | ||
/> | ||
{#if showSelectAll} | ||
<button | ||
id="select-all" | ||
class="btn variant-ringed-surface hover:variant-filled-primary ml-2" | ||
on:click={() => { | ||
selectedOptions = allOptions; | ||
selectedOptionEndLocation = 20; | ||
options = []; | ||
}}>Select All</button | ||
> | ||
{/if} | ||
</header> | ||
<section class="card-body"> | ||
<div | ||
id="options-container" | ||
bind:this={optionsContainer} | ||
class="overflow-scroll scrollbar-color h-25vh" | ||
on:scroll={() => handleScroll()} | ||
> | ||
{#each filteredOptions as option (option)} | ||
<label | ||
id="option-{option}" | ||
class="p-1 m-1 cursor-pointer hover:variant-soft-surface hover:rounded-md" | ||
in:receive={{ key: option }} | ||
out:send={{ key: option }} | ||
role="listitem" | ||
animate:flip | ||
> | ||
<input | ||
type="checkbox" | ||
value={option} | ||
on:click|preventDefault={() => onSelect(option)} | ||
/> | ||
{option} | ||
</label> | ||
{/each} | ||
</div> | ||
</section> | ||
</div> | ||
<div class="flex flex-1 flex-col h-full p-3 m-1 card"> | ||
<header class="flex justify-between pb-1"> | ||
<h5>Selected:</h5> | ||
<button | ||
id="clear" | ||
class="btn variant-ringed-surface hover:variant-filled-primary ml-2" | ||
on:click={() => { | ||
selectedOptionEndLocation = 20; | ||
displayedSelectedOptions.forEach((option) => onUnselect(option)); | ||
selectedOptions = []; | ||
}} | ||
disabled={selectedOptions.length === 0}>Clear</button | ||
> | ||
</header> | ||
<section class="card-body"> | ||
<!-- on:scroll={() => selectedOptionsScolling} --> | ||
<div | ||
id="selected-options-container" | ||
bind:this={selelectedOptionsContainer} | ||
class="overflow-scroll scrollbar-color h-25vh" | ||
on:scroll={() => loadMoreSelectedOptions()} | ||
> | ||
{#each displayedSelectedOptions as option (option)} | ||
<label | ||
id="option-{option}" | ||
class="p-1 m-1 hover:variant-soft-surface hover:rounded-md cursor-pointer" | ||
in:receive={{ key: option }} | ||
out:send={{ key: option }} | ||
animate:flip | ||
role="listitem" | ||
> | ||
<input | ||
type="checkbox" | ||
class="mr-1" | ||
value={option} | ||
on:click|preventDefault={() => onUnselect(option)} | ||
checked | ||
/> | ||
{option} | ||
</label> | ||
{/each} | ||
</div> | ||
</section> | ||
</div> | ||
</div> | ||
|
||
<style> | ||
.scrollbar-color { | ||
scrollbar-color: rgba(var(--color-surface-300)) rgb(var(--color-surface-100)); | ||
} | ||
.h-25vh { | ||
height: 25vh; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<script lang="ts"> | ||
export let placeholder = 'Search...'; | ||
export let search: () => void = () => {}; | ||
export let searchTerm = ''; | ||
</script> | ||
|
||
<div class="flex w-full"> | ||
<input | ||
type="search" | ||
autocomplete="off" | ||
class="search-box w-full" | ||
data-testid="search-box" | ||
aria-label="Type search terms here, use enter or the search button to submit search" | ||
title="Type search terms here, use enter or the search button to submit search" | ||
{placeholder} | ||
bind:value={searchTerm} | ||
on:keydown={(e) => e.key === 'Enter' && search()} | ||
required | ||
/> | ||
<button | ||
id="search-button" | ||
class="btn variant-filled-primary search-button" | ||
aria-label="Search" | ||
title="Search" | ||
disabled={!searchTerm} | ||
on:click={search} | ||
> | ||
<i class="fas fa-search"></i> | ||
</button> | ||
</div> | ||
|
||
<style> | ||
.search-box { | ||
border-top-right-radius: 0 !important; | ||
border-bottom-right-radius: 0 !important; | ||
margin-right: 0% !important; | ||
} | ||
.search-button { | ||
border-top-left-radius: 0; | ||
border-bottom-left-radius: 0; | ||
padding-right: 1rem; | ||
padding-left: 0.75rem; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<script lang="ts"> | ||
import type { Column } from '$lib/models/Tables'; | ||
import type { Indexable } from '$lib/types'; | ||
import Search from '$lib/stores/Search'; | ||
let { activeRow, expandableComponents, activeComponent } = Search; | ||
export let cellOverides: Indexable = {}; | ||
export let columns: Column[] = []; | ||
export let index: number = -2; | ||
export let row: Indexable = {}; | ||
export let rowClickHandler: (index: number) => void; | ||
function onClick(index: number) { | ||
if (!index || index === -1) return; | ||
rowClickHandler && rowClickHandler(index); | ||
} | ||
</script> | ||
|
||
<tr id={index.toString()} on:click|stopPropagation={() => onClick(index)} class="cursor-pointer"> | ||
{#each columns as column} | ||
<td> | ||
{#if cellOverides[column.dataElement]} | ||
<svelte:component | ||
this={cellOverides[column.dataElement]} | ||
data={{ index, row, cell: row[column.dataElement] }} | ||
/> | ||
{:else} | ||
{row[column.dataElement]} | ||
{/if} | ||
</td> | ||
{/each} | ||
</tr> | ||
|
||
{#if Object.keys($expandableComponents).length > 0 && $activeRow === index} | ||
<tr class="expandable-row"> | ||
<td colspan={columns.length}> | ||
{#if $activeComponent} | ||
<svelte:component this={$activeComponent} data={row} /> | ||
{/if} | ||
</td> | ||
</tr> | ||
{/if} | ||
|
||
<style> | ||
.expandable-row { | ||
background-color: rgb(var(--color-surface-300)) !important; | ||
} | ||
</style> |
Oops, something went wrong.