rewrites utils as ts
This commit is contained in:
parent
d622258413
commit
a5b93e5f53
4 changed files with 65 additions and 80 deletions
|
|
@ -48,10 +48,3 @@ export interface ScrollerStep {
|
||||||
*/
|
*/
|
||||||
foregroundProps?: object;
|
foregroundProps?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Datum type for data that goes into the Table component */
|
|
||||||
type TableDatum = {
|
|
||||||
[key: string]: unknown;
|
|
||||||
searchStr?: string;
|
|
||||||
};
|
|
||||||
export type TableData = TableDatum[];
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- <Story
|
<Story
|
||||||
name="Metadata"
|
name="Metadata"
|
||||||
args={{
|
args={{
|
||||||
width: 'normal',
|
width: 'normal',
|
||||||
|
|
@ -131,4 +131,4 @@
|
||||||
title: 'The Richest Women in the World',
|
title: 'The Richest Women in the World',
|
||||||
source: 'Source: Forbes',
|
source: 'Source: Forbes',
|
||||||
}}
|
}}
|
||||||
/> -->
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<!-- @component `Table` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-text-elements-table--docs) -->
|
<!-- @component `Table` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-text-elements-table--docs) -->
|
||||||
<script lang="ts">
|
<script lang="ts" generics="T">
|
||||||
/** Import local helpers */
|
/** Import local helpers */
|
||||||
import Block from '../Block/Block.svelte';
|
import Block from '../Block/Block.svelte';
|
||||||
import Pagination from './components/Pagination.svelte';
|
import Pagination from './components/Pagination.svelte';
|
||||||
|
|
@ -8,12 +8,9 @@
|
||||||
import SearchInput from '../SearchInput/SearchInput.svelte';
|
import SearchInput from '../SearchInput/SearchInput.svelte';
|
||||||
import { filterArray, paginateArray, getOptions } from './utils';
|
import { filterArray, paginateArray, getOptions } from './utils';
|
||||||
|
|
||||||
// Types
|
interface Props<T extends Record<string, unknown>> {
|
||||||
import type { TableData } from '../@types/global';
|
/** Data for the table as an array. */
|
||||||
|
data: T[];
|
||||||
interface Props {
|
|
||||||
/** Data for the table as an array of objects. */
|
|
||||||
data: TableData;
|
|
||||||
|
|
||||||
/** A title that runs above the table. */
|
/** A title that runs above the table. */
|
||||||
title?: string;
|
title?: string;
|
||||||
|
|
@ -65,7 +62,7 @@
|
||||||
dek,
|
dek,
|
||||||
notes,
|
notes,
|
||||||
source,
|
source,
|
||||||
includedFields,
|
includedFields = Object.keys(data[0]).filter((f) => f !== 'searchStr'),
|
||||||
truncated = false,
|
truncated = false,
|
||||||
truncateLength = 5,
|
truncateLength = 5,
|
||||||
paginated = false,
|
paginated = false,
|
||||||
|
|
@ -75,35 +72,35 @@
|
||||||
filterField,
|
filterField,
|
||||||
filterLabel,
|
filterLabel,
|
||||||
sortable = false,
|
sortable = false,
|
||||||
sortField,
|
sortField = Object.keys(data[0])[0],
|
||||||
sortableFields,
|
sortableFields = Object.keys(data[0]).filter((f) => f !== 'searchStr'),
|
||||||
sortDirection = $bindable('ascending'),
|
sortDirection = $bindable('ascending'),
|
||||||
fieldFormatters = {},
|
fieldFormatters = {},
|
||||||
width = 'normal',
|
width = 'normal',
|
||||||
id = '',
|
id = '',
|
||||||
class: cls = '',
|
class: cls = '',
|
||||||
}: Props = $props();
|
}: Props<Record<string, unknown>> = $props();
|
||||||
|
|
||||||
/** Derived variables */
|
/** Derived variables */
|
||||||
let includedFieldsDerived = $derived.by(() => {
|
// let includedFieldsDerived = $derived.by(() => {
|
||||||
if (includedFields) return includedFields;
|
// if (includedFields) return includedFields;
|
||||||
if (data.length > 0)
|
// if (data.length > 0)
|
||||||
return Object.keys(data[0]).filter((f) => f !== 'searchStr');
|
// return Object.keys(data[0]).filter((f) => f !== 'searchStr');
|
||||||
return [];
|
// return [];
|
||||||
});
|
// });
|
||||||
|
|
||||||
let sortableFieldsDerived = $derived.by(() => {
|
// let sortableFieldsDerived = $derived.by(() => {
|
||||||
if (sortableFields) return sortableFields;
|
// if (sortableFields) return sortableFields;
|
||||||
if (data.length > 0)
|
// if (data.length > 0)
|
||||||
return Object.keys(data[0]).filter((f) => f !== 'searchStr');
|
// return Object.keys(data[0]).filter((f) => f !== 'searchStr');
|
||||||
return [];
|
// return [];
|
||||||
});
|
// });
|
||||||
|
|
||||||
let sortFieldDerived = $derived.by(() => {
|
// let sortFieldDerived = $derived.by(() => {
|
||||||
if (sortField) return sortField;
|
// if (sortField) return sortField;
|
||||||
if (data.length > 0) return Object.keys(data[0])[0];
|
// if (data.length > 0) return Object.keys(data[0])[0];
|
||||||
return '';
|
// return '';
|
||||||
});
|
// });
|
||||||
|
|
||||||
/** Set truncate, filtering and pagination configuration */
|
/** Set truncate, filtering and pagination configuration */
|
||||||
let showAll = $state(false);
|
let showAll = $state(false);
|
||||||
|
|
@ -178,20 +175,12 @@
|
||||||
return sortedData;
|
return sortedData;
|
||||||
});
|
});
|
||||||
|
|
||||||
// $effect(() => {
|
|
||||||
// console.log('includedFieldsDerived', includedFieldsDerived);
|
|
||||||
// console.log('sortableFieldsDerived', sortableFieldsDerived);
|
|
||||||
// console.log('sortFieldDerived', sortFieldDerived);
|
|
||||||
// console.log('sortedData', sortedData);
|
|
||||||
// console.log('currentPageData', currentPageData);
|
|
||||||
// });
|
|
||||||
|
|
||||||
/** Add the `searchStr` field to data */
|
/** Add the `searchStr` field to data */
|
||||||
let searchableData = $derived.by(() => {
|
let searchableData = $derived.by(() => {
|
||||||
return data.map((d) => {
|
return data.map((d) => {
|
||||||
return {
|
return {
|
||||||
...d,
|
...d,
|
||||||
searchStr: includedFieldsDerived
|
searchStr: includedFields
|
||||||
.map((field) => d[field])
|
.map((field) => d[field])
|
||||||
.join(' ')
|
.join(' ')
|
||||||
.toLowerCase(),
|
.toLowerCase(),
|
||||||
|
|
@ -243,12 +232,11 @@
|
||||||
>
|
>
|
||||||
<thead class="table--thead">
|
<thead class="table--thead">
|
||||||
<tr>
|
<tr>
|
||||||
{#each includedFieldsDerived as field}
|
{#each includedFields 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 &&
|
class:sortable={sortable && sortableFields.includes(field)}
|
||||||
sortableFieldsDerived.includes(field)}
|
|
||||||
class:sort-ascending={sortable &&
|
class:sort-ascending={sortable &&
|
||||||
sortField === field &&
|
sortField === field &&
|
||||||
sortDirection === 'ascending'}
|
sortDirection === 'ascending'}
|
||||||
|
|
@ -259,7 +247,7 @@
|
||||||
onclick={handleSort}
|
onclick={handleSort}
|
||||||
>
|
>
|
||||||
{field}
|
{field}
|
||||||
{#if sortable && sortableFieldsDerived.includes(field)}
|
{#if sortable && sortableFields.includes(field)}
|
||||||
<div class="table--thead--sortarrow fml-1 avoid-clicks">
|
<div class="table--thead--sortarrow fml-1 avoid-clicks">
|
||||||
<SortArrow {sortDirection} active={sortField === field} />
|
<SortArrow {sortDirection} active={sortField === field} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -271,7 +259,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 includedFieldsDerived as field}
|
{#each includedFields 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}
|
||||||
|
|
@ -285,7 +273,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
{#if searchable && searchText && currentPageData.length === 0}
|
{#if searchable && searchText && currentPageData.length === 0}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="no-results" colspan={includedFieldsDerived.length}>
|
<td class="no-results" colspan={includedFields.length}>
|
||||||
No results found for "{searchText}"
|
No results found for "{searchText}"
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -295,7 +283,7 @@
|
||||||
<tfoot class="table--tfoot">
|
<tfoot class="table--tfoot">
|
||||||
{#if notes}
|
{#if notes}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="" colspan={includedFieldsDerived.length}>
|
<td class="" colspan={includedFields.length}>
|
||||||
<div class="fmt-2">
|
<div class="fmt-2">
|
||||||
{@html notes}
|
{@html notes}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -304,7 +292,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
{#if source}
|
{#if source}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="" colspan={includedFieldsDerived.length}>
|
<td class="" colspan={includedFields.length}>
|
||||||
<div class="fmt-1">
|
<div class="fmt-1">
|
||||||
{@html source}
|
{@html source}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -343,10 +331,10 @@
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@use '../../scss/mixins' as mixins;
|
@use '../../scss/mixins' as mixins;
|
||||||
|
|
||||||
section.table {
|
.table {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
section.table table {
|
.table table {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-collapse: separate;
|
border-collapse: separate;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,60 @@
|
||||||
import type { TableData } from '../@types/global';
|
export function filterArray<T extends { searchStr: string }>(
|
||||||
|
data: T[],
|
||||||
export function filterArray(
|
|
||||||
data: TableData,
|
|
||||||
searchText: string,
|
searchText: string,
|
||||||
filterField: string | undefined,
|
filterField: keyof T,
|
||||||
filterValue: string
|
filterValue: T[keyof T]
|
||||||
) {
|
) {
|
||||||
if (searchText) {
|
if (searchText) {
|
||||||
data = data.filter((item) => {
|
data = data.filter((item) => {
|
||||||
return item.searchStr?.includes(searchText.toLowerCase());
|
return item.searchStr.includes(searchText.toLowerCase());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (filterField && filterValue) {
|
|
||||||
|
if (filterValue) {
|
||||||
data = data.filter((item) => {
|
data = data.filter((item) => {
|
||||||
return item[filterField] === filterValue;
|
return item[filterField] === filterValue;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function paginateArray(
|
export function paginateArray<T>(
|
||||||
array: TableData,
|
array: T[],
|
||||||
pageSize: number,
|
pageSize: number,
|
||||||
pageNumber: number
|
pageNumber: number
|
||||||
) {
|
) {
|
||||||
return array.slice((pageNumber - 1) * pageSize, pageNumber * pageSize);
|
return array.slice((pageNumber - 1) * pageSize, pageNumber * pageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
function uniqueAttr(array: TableData, attr: string) {
|
/**
|
||||||
|
* We specify the output type here by adding `string` to the union because we want to explicitly define the output array as accepting strings.
|
||||||
|
*
|
||||||
|
* This is to get rid of the type error from `attrList.unshift('All')`
|
||||||
|
*/
|
||||||
|
function uniqueAttr<T>(array: T[], attr: keyof T): (T[keyof T] | string)[] {
|
||||||
return array.map((e) => e[attr]).filter(unique);
|
return array.map((e) => e[attr]).filter(unique);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
function unique<T>(value: T, index: number, array: T[]) {
|
||||||
function unique(value: any, index: number, array: TableData) {
|
|
||||||
console.log('unique', value, index, array);
|
|
||||||
return array.indexOf(value) === index;
|
return array.indexOf(value) === index;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOptions(data: TableData, attr: string) {
|
export function getOptions<T>(data: T[], attr: keyof T) {
|
||||||
// Get all the unique values in the provided field. Sort it.
|
// Get all the unique values in the provided field. Sort it.
|
||||||
|
const attrList = uniqueAttr(data, attr).sort((a, b) => {
|
||||||
|
// Throw errors if a and b are not strings.
|
||||||
|
// a and b should be strings since they are keys of T.
|
||||||
|
if (typeof a !== 'string' || typeof b !== 'string') {
|
||||||
|
throw new Error(`Expected string, got ${typeof a} and ${typeof b}`);
|
||||||
|
}
|
||||||
|
|
||||||
// @TODO - check if a and b need to be typed and sorted for non-strings
|
return a.localeCompare(b);
|
||||||
const attrList = uniqueAttr(data, attr).sort((a: string, b: string) =>
|
});
|
||||||
a.localeCompare(b)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('attrList', attrList);
|
// Tack 'All' at the start of `attrList`, making it the first option.
|
||||||
|
|
||||||
// Tack 'All' as the front as the first option.
|
|
||||||
attrList.unshift('All');
|
attrList.unshift('All');
|
||||||
|
|
||||||
// Convert the list into Option typed objects ready for our Select component
|
// Convert the list into Option typed objects ready for our Select component
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
return attrList.map((a) => ({ text: a, value: a }));
|
||||||
return attrList.map((a: any) => ({ text: a, value: a }));
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue