working on table migration

This commit is contained in:
MinamiFunakoshiTR 2025-03-25 13:07:13 -07:00
parent 7cfd93f901
commit bbe68f489f
Failed to extract signature
3 changed files with 97 additions and 54 deletions

View file

@ -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} />

View file

@ -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: {

View file

@ -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">