working on table migration
This commit is contained in:
parent
7cfd93f901
commit
bbe68f489f
3 changed files with 97 additions and 54 deletions
|
|
@ -1,8 +1,8 @@
|
||||||
import { Meta, Canvas } from '@storybook/blocks';
|
import { Meta, Canvas } from '@storybook/blocks';
|
||||||
|
|
||||||
import * as TableTextStories from './Table.stories.svelte';
|
import * as TableStories from './Table.stories.svelte';
|
||||||
|
|
||||||
<Meta of={TableTextStories} />
|
<Meta of={TableStories} />
|
||||||
|
|
||||||
# Table
|
# Table
|
||||||
|
|
||||||
|
|
@ -16,4 +16,4 @@ The `Table` component TKTK
|
||||||
<Table />
|
<Table />
|
||||||
```
|
```
|
||||||
|
|
||||||
<Canvas of={TableTextStories.Demo} />
|
<Canvas of={TableStories.Demo} />
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import Table from './Table.svelte';
|
import Table from './Table.svelte';
|
||||||
|
|
||||||
const { Story } = defineMeta({
|
const { Story } = defineMeta({
|
||||||
title: 'Components/Text elements/Table',
|
title: 'Components/Graphics/Table',
|
||||||
component: Table,
|
component: Table,
|
||||||
argTypes: {
|
argTypes: {
|
||||||
width: {
|
width: {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,17 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
/** Import local helpers */
|
||||||
|
import Block from '../Block/Block.svelte';
|
||||||
|
import Pagination from './Pagination.svelte';
|
||||||
|
import Select from './Select.svelte';
|
||||||
|
import SortArrow from './SortArrow.svelte';
|
||||||
|
import SearchInput from '../SearchInput/SearchInput.svelte';
|
||||||
|
import { filterArray, paginateArray, getOptions } from './utils';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/** Data for the table. */
|
/** Data for the table as an array of objects. */
|
||||||
data: [];
|
data: object[];
|
||||||
/** A title that runs above the table. */
|
/** A title that runs above the table. */
|
||||||
title?: string;
|
title?: string;
|
||||||
/** A block of text that runs above the table. */
|
/** A block of text that runs above the table. */
|
||||||
|
|
@ -55,48 +63,54 @@
|
||||||
dek,
|
dek,
|
||||||
notes,
|
notes,
|
||||||
source,
|
source,
|
||||||
includedFields = Object.keys(data?[0]).filter((f) => f !== 'searchStr'),
|
includedFields,
|
||||||
truncated = false,
|
truncated = false,
|
||||||
truncateLength = 5,
|
truncateLength = 5,
|
||||||
paginated= false,
|
paginated = false,
|
||||||
pageSize = 25,
|
pageSize = 25,
|
||||||
searchable = false,
|
searchable = false,
|
||||||
searchPlaceholder = 'Search in table',
|
searchPlaceholder = 'Search in table',
|
||||||
filterField,
|
filterField,
|
||||||
filterLabel,
|
filterLabel,
|
||||||
sortable = false,
|
sortable = false,
|
||||||
sortField = Object.keys(data[0])[0],
|
sortField,
|
||||||
sortableFields = Object.keys(data[0]).filter((f) => f !== 'searchStr'),
|
sortableFields,
|
||||||
sortDirection = 'ascending',
|
sortDirection = $bindable('ascending'),
|
||||||
fieldFormatters = {},
|
fieldFormatters = {},
|
||||||
width = 'normal',
|
width = 'normal',
|
||||||
id = '',
|
id = '',
|
||||||
class: cls = '',
|
class: cls = '',
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
|
|
||||||
/** Import local helpers */
|
/** Derived variables */
|
||||||
import Block from '../Block/Block.svelte';
|
let includedFieldsDerived = $derived.by(() => {
|
||||||
import Pagination from './Pagination.svelte';
|
if (includedFields) return includedFields;
|
||||||
import SearchInput from '../SearchInput/SearchInput.svelte';
|
if (data.length > 0)
|
||||||
import Select from './Select.svelte';
|
return Object.keys(data[0]).filter((f) => f !== 'searchStr');
|
||||||
import SortArrow from './SortArrow.svelte';
|
return [];
|
||||||
import { filterArray, paginateArray, getOptions } from './utils.ts';
|
});
|
||||||
|
|
||||||
|
let sortableFieldsDerived = $derived.by(() => {
|
||||||
|
if (sortableFields) return sortableFields;
|
||||||
|
if (data.length > 0)
|
||||||
|
return Object.keys(data[0]).filter((f) => f !== 'searchStr');
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
let sortFieldDerived = $derived.by(() => {
|
||||||
|
if (sortField) return sortField;
|
||||||
|
if (data.length > 0) return Object.keys(data[0])[0];
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
/** Set truncate, filtering and pagination configuration */
|
/** Set truncate, filtering and pagination configuration */
|
||||||
let showAll = false;
|
let showAll = $state(false);
|
||||||
let pageNumber = 1;
|
let pageNumber = $state(1);
|
||||||
let searchText = '';
|
let searchText = $state('');
|
||||||
const filterList = filterField ? getOptions(data, filterField) : undefined;
|
let filterList = $derived(
|
||||||
|
filterField ? getOptions(data, filterField) : undefined
|
||||||
|
);
|
||||||
let filterValue = '';
|
let filterValue = '';
|
||||||
$: filteredData = filterArray(data, searchText, filterField, filterValue);
|
|
||||||
$: sortedData = sortArray(filteredData, sortField, sortDirection);
|
|
||||||
$: currentPageData =
|
|
||||||
truncated ?
|
|
||||||
showAll ? sortedData
|
|
||||||
: sortedData.slice(0, truncateLength + 1)
|
|
||||||
: paginated ? paginateArray(sortedData, pageSize, pageNumber)
|
|
||||||
: sortedData;
|
|
||||||
|
|
||||||
//* * Handle show all, search, filter, sort and pagination events */
|
//* * Handle show all, search, filter, sort and pagination events */
|
||||||
function toggleTruncate(_event) {
|
function toggleTruncate(_event) {
|
||||||
|
|
@ -143,12 +157,39 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set up the data pipeline */
|
||||||
|
let filteredData = $derived.by(() =>
|
||||||
|
filterArray(data, searchText, filterField, filterValue)
|
||||||
|
);
|
||||||
|
|
||||||
|
let sortedData = $derived.by(() =>
|
||||||
|
sortArray(filteredData, sortField, sortDirection)
|
||||||
|
);
|
||||||
|
|
||||||
|
let currentPageData = $derived.by(() => {
|
||||||
|
if (truncated) {
|
||||||
|
return showAll ? sortedData : sortedData.slice(0, truncateLength + 1);
|
||||||
|
}
|
||||||
|
if (paginated) {
|
||||||
|
return paginateArray(sortedData, pageSize, pageNumber);
|
||||||
|
}
|
||||||
|
return sortedData;
|
||||||
|
});
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
console.log('includedFieldsDerived', includedFieldsDerived);
|
||||||
|
console.log('sortableFieldsDerived', sortableFieldsDerived);
|
||||||
|
console.log('sortFieldDerived', sortFieldDerived);
|
||||||
|
console.log('sortedData', sortedData);
|
||||||
|
console.log('currentPageData', currentPageData);
|
||||||
|
});
|
||||||
|
|
||||||
/** Boot it up. */
|
/** Boot it up. */
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
data.forEach((d: any) => {
|
data.forEach((d: any) => {
|
||||||
// Compose the string we will allow users to search
|
// Compose the string we will allow users to search
|
||||||
d.searchStr = includedFields
|
d.searchStr = includedFieldsDerived
|
||||||
.map((field) => d[field])
|
.map((field) => d[field])
|
||||||
.join(' ')
|
.join(' ')
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
|
|
@ -157,9 +198,9 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Block {width} {id} class="fmy-6 {cls}">
|
<Block {width} {id} class="fmy-6 {cls}">
|
||||||
<article class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
{#if title || dek || searchable || filterList}
|
{#if title || dek || searchable || filterList}
|
||||||
<header class="table--header w-full">
|
<div class="table--header w-full">
|
||||||
{#if title}
|
{#if title}
|
||||||
<h3 class="table--header--title">{@html title}</h3>
|
<h3 class="table--header--title">{@html title}</h3>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
@ -187,9 +228,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
</nav>
|
</nav>
|
||||||
{/if}
|
{/if}
|
||||||
</header>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<section class="table w-full">
|
<div class="table w-full">
|
||||||
<table
|
<table
|
||||||
class="w-full"
|
class="w-full"
|
||||||
class:paginated
|
class:paginated
|
||||||
|
|
@ -197,11 +238,12 @@
|
||||||
>
|
>
|
||||||
<thead class="table--thead">
|
<thead class="table--thead">
|
||||||
<tr>
|
<tr>
|
||||||
{#each includedFields as field}
|
{#each includedFieldsDerived as field}
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
class="table--thead--th h4 pl-0 py-2 pr-2"
|
class="table--thead--th h4 pl-0 py-2 pr-2"
|
||||||
class:sortable={sortable && sortableFields.includes(field)}
|
class:sortable={sortable &&
|
||||||
|
sortableFieldsDerived.includes(field)}
|
||||||
class:sort-ascending={sortable &&
|
class:sort-ascending={sortable &&
|
||||||
sortField === field &&
|
sortField === field &&
|
||||||
sortDirection === 'ascending'}
|
sortDirection === 'ascending'}
|
||||||
|
|
@ -209,15 +251,12 @@
|
||||||
sortField === field &&
|
sortField === field &&
|
||||||
sortDirection === 'descending'}
|
sortDirection === 'descending'}
|
||||||
data-field={field}
|
data-field={field}
|
||||||
on:click={handleSort}
|
onclick={handleSort}
|
||||||
>
|
>
|
||||||
{field}
|
{field}
|
||||||
{#if sortable && sortableFields.includes(field)}
|
{#if sortable && sortableFieldsDerived.includes(field)}
|
||||||
<div class="table--thead--sortarrow fml-1 avoid-clicks">
|
<div class="table--thead--sortarrow fml-1 avoid-clicks">
|
||||||
<SortArrow
|
<SortArrow {sortDirection} active={sortField === field} />
|
||||||
bind:sortDirection
|
|
||||||
active={sortField === field}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</th>
|
</th>
|
||||||
|
|
@ -227,7 +266,7 @@
|
||||||
<tbody class="table--tbody">
|
<tbody class="table--tbody">
|
||||||
{#each currentPageData as item, idx}
|
{#each currentPageData as item, idx}
|
||||||
<tr data-row-index={idx}>
|
<tr data-row-index={idx}>
|
||||||
{#each includedFields as field}
|
{#each includedFieldsDerived as field}
|
||||||
<td
|
<td
|
||||||
class="body-note pl-0 py-2 pr-2"
|
class="body-note pl-0 py-2 pr-2"
|
||||||
data-row-index={idx}
|
data-row-index={idx}
|
||||||
|
|
@ -241,7 +280,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
{#if searchable && searchText && currentPageData.length === 0}
|
{#if searchable && searchText && currentPageData.length === 0}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="no-results" colspan={includedFields.length}>
|
<td class="no-results" colspan={includedFieldsDerived.length}>
|
||||||
No results found for "{searchText}"
|
No results found for "{searchText}"
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -251,7 +290,7 @@
|
||||||
<tfoot class="table--tfoot">
|
<tfoot class="table--tfoot">
|
||||||
{#if notes}
|
{#if notes}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="" colspan={includedFields.length}>
|
<td class="" colspan={includedFieldsDerived.length}>
|
||||||
<div class="fmt-2">
|
<div class="fmt-2">
|
||||||
{@html notes}
|
{@html notes}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -260,7 +299,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
{#if source}
|
{#if source}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="" colspan={includedFields.length}>
|
<td class="" colspan={includedFieldsDerived.length}>
|
||||||
<div class="fmt-1">
|
<div class="fmt-1">
|
||||||
{@html source}
|
{@html source}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -270,16 +309,20 @@
|
||||||
</tfoot>
|
</tfoot>
|
||||||
{/if}
|
{/if}
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</div>
|
||||||
{#if truncated && data.length > truncateLength}
|
{#if truncated && data.length > truncateLength}
|
||||||
<nav
|
<nav
|
||||||
aria-label="Show all button"
|
aria-label="Show all button"
|
||||||
class="show-all flex items-center justify-center fmt-2"
|
class="show-all flex items-center justify-center fmt-2"
|
||||||
>
|
>
|
||||||
<button class="body-caption" on:click={toggleTruncate}
|
<button class="body-caption" onclick={toggleTruncate}>
|
||||||
>{#if showAll}Show fewer rows{:else}Show {data.length -
|
{#if showAll}
|
||||||
truncateLength} more rows{/if}</button
|
Show fewer rows
|
||||||
>
|
{:else}
|
||||||
|
Show {data.length - truncateLength}
|
||||||
|
more rows
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
{/if}
|
{/if}
|
||||||
{#if paginated}
|
{#if paginated}
|
||||||
|
|
@ -289,7 +332,7 @@
|
||||||
bind:pageLength={currentPageData.length}
|
bind:pageLength={currentPageData.length}
|
||||||
bind:n={sortedData.length}
|
bind:n={sortedData.length}
|
||||||
/>{/if}
|
/>{/if}
|
||||||
</article>
|
</div>
|
||||||
</Block>
|
</Block>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue