Skip to content

Commit

Permalink
redesign internal logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ryu-man committed Nov 24, 2023
1 parent bbc0024 commit 252fa73
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 35 deletions.
7 changes: 5 additions & 2 deletions packages/core/src/table/Table.stories.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,16 @@
schemeMedia.removeEventListener('change', handler);
};
});
let selectedItems = [];
$: console.log(selectedItems);
</script>

<Meta title="Components/Table" component={Table} {argTypes} />

<Story id="table" name="Table" args={defaultValues} let:args>
<App {theme}>
<Table {...args} {data} let:data>
<Table {...args} {data} bind:selectedItems let:data>
<thead>
<Tr header>
<TdSelection />
Expand All @@ -116,7 +119,7 @@
</thead>
<tbody>
{#each data as item (JSON.stringify(item))}
<Tr appearance="none">
<Tr appearance="none" data={item}>
<TdSelection />
<Td class="flex items-center gap-2">
<Icon src={item.file.icon} />
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/table/Table.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
export let size: TableSize = 'md';
export let sortable = false;
export let data: any[] = [];
export let selectedItems: any[] = [];
let sorted: any[] = [];
let orderBy: typeof _orderBy | undefined = undefined;
Expand All @@ -16,14 +17,19 @@
});
}
const { sortable$, sorting$, size$ } = setTableContext();
const { sortable$, sorting$, size$, allRows$, selectedKeys$ } = setTableContext();
sortable$.set(sortable);
size$.set(size);
$: sortable$.set(sortable);
$: [key, direction] = $sorting$ || [];
$: sorted = orderBy && key ? orderBy(data, key, direction === 'ascending' ? 'asc' : 'desc') : data;
$: if ($selectedKeys$) {
selectedItems = $allRows$.filter((d) => d.selected$.value).map((d) => d.data);
}
</script>

<table class="fui-table">
Expand Down
37 changes: 18 additions & 19 deletions packages/core/src/table/TdSelection.svelte
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
<script lang="ts">
import { derived } from 'svelte/store';
import { type Readable, derived } from 'svelte/store';
import { getTableRowContext } from './Tr';
import { getTableContext } from './context';
import type { RowStore } from './store';
import { Checkbox } from '../checkbox';
import { classnames } from '../internal';
import { Radio } from '../radio';
const { size$, selectedRows$, allRows$ } = getTableContext();
const { size$, allRows$, selectedKeys$ } = getTableContext();
const rowContext = getTableRowContext();
const checked$ = derived([selectedRows$, allRows$], ([rows, all]) => {
if (rowContext.header) {
return rows.size === all.size;
} else {
return rows.has(rowContext.id);
}
});
const row$ = $allRows$.find((d) => d.id === rowContext.id) as RowStore;
let checked$: Readable<boolean>;
if (rowContext.header) {
checked$ = derived([allRows$, selectedKeys$], ([rows, selected]) => {
return selected.length > 0 && rows.length === selected.length;
});
} else {
checked$ = row$.selected$;
}
// export let checked: boolean | 'mixed' = false;
export let type: 'checkbox' | 'radio' = 'checkbox';
Expand All @@ -27,20 +31,15 @@
if (rowContext.header) {
if (currentTarget.checked) {
selectedRows$.set(new Set($allRows$));
$allRows$.forEach((d) => d.selected$.set(true));
} else {
selectedRows$.set(new Set());
$allRows$.forEach((d) => d.selected$.set(false));
}
return;
}
if (currentTarget.checked) {
selectedRows$.update((rows) => rows.add(rowContext.id));
} else {
selectedRows$.update((rows) => {
rows.delete(rowContext.id);
return rows;
});
if (row$) {
row$.selected$.set(currentTarget.checked);
}
}
</script>
Expand Down
21 changes: 13 additions & 8 deletions packages/core/src/table/Tr/Tr.svelte
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
<script lang="ts">
import { onMount, tick } from 'svelte';
import { derived } from 'svelte/store';
import { nanoid } from 'nanoid';
import { setTableRowContext } from './context';
import { classnames } from '../../internal';
import { getTableContext } from '../context';
import { rowStore } from '../store';
const id = nanoid();
export let appearance: 'none' | 'neutral' | 'brand' = 'none';
export let header = false;
export let key = header ? 'fui-table-header-row-' + nanoid(4) : nanoid();
export let data: any = undefined;
export let appearance: 'none' | 'neutral' | 'brand' = 'none';
let klass = '';
export { klass as class };
const context = setTableRowContext();
context.id = id;
context.id = key;
context.header = header;
const { selectedRows$, allRows$, size$ } = getTableContext();
const isSelected$ = derived(selectedRows$, (ids) => ids.has(id));
const { size$, mountRow } = getTableContext();
const row$ = rowStore(key, data);
const isSelected$ = derived(row$.selected$, (val) => val);
if (!header) {
allRows$.update((array) => array.add(id));
onMount(() => mountRow(row$));
}
</script>

<tr class={classnames('fui-table-row', $size$, appearance !== 'none' ? appearance : '', { header, brand: !header && $isSelected$ }, klass)}>
<slot />
{#await tick() then _}
<slot />
{/await}
</tr>

<style lang="postcss">
Expand Down
37 changes: 32 additions & 5 deletions packages/core/src/table/context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getContext, setContext } from 'svelte';
import { type Writable, writable } from 'svelte/store';
import { type Readable, type Writable, readonly, writable } from 'svelte/store';
import { type RowStore } from './store';
import type { SortingDirection, TableSize } from './type';

export const SVELTE_FUI_TABLE_CONTEXT_KEY = 'svelte-fui-table-context-key';
Expand All @@ -8,22 +9,48 @@ export type TableContext = {
sortable$: Writable<boolean>;
size$: Writable<TableSize>;
sorting$: Writable<[key: (d: any) => any, direction: SortingDirection] | undefined>;
allRows$: Writable<Set<string>>;
selectedRows$: Writable<Set<string>>;
allRows$: Readable<RowStore[]>;
selectedKeys$: Readable<string[]>;
mountRow: <T>(store: RowStore<T>) => () => void;
};

export function getTableContext(): TableContext {
return getContext(SVELTE_FUI_TABLE_CONTEXT_KEY);
}

export function setTableContext() {
const allRows$ = writable<any[]>([]);
const selectedKeys$ = writable<string[]>([]);

const context: TableContext = {
sortable$: writable(false),
size$: writable('md'),
sorting$: writable(undefined),
allRows$: writable(new Set()),
selectedRows$: writable(new Set())
allRows$: readonly(allRows$),
selectedKeys$: readonly(selectedKeys$),
mountRow
};

return setContext(SVELTE_FUI_TABLE_CONTEXT_KEY, context);

function mountRow<T>(store: RowStore<T>) {
allRows$.update((old) => [...old, store]);

const unsubscribe = store.selected$.subscribe((value) => {
if (value) {
selectedKeys$.update((old) => [...new Set([...old, store.id])]);
} else {
selectedKeys$.update((old) => old.filter((d) => d !== store.id));
}
});

return () => {
unsubscribe();
destroyRow(store.id);
};
}

function destroyRow(id: string) {
allRows$.update((old) => old.filter((d) => d !== id));
}
}
37 changes: 37 additions & 0 deletions packages/core/src/table/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { type Updater, type Writable, writable } from 'svelte/store';

export type RowStore<T = any> = {
id: string;
data: T;
selected$: Writable<boolean> & { value: boolean };
};

export function rowStore<T>(id: string, data: T) {
let selected = false;

const selected$ = writable(selected);

return {
id,
data,
selected$: {
subscribe: selected$.subscribe,
set(value: boolean) {
return selected$.set((selected = value));
},
update(updater: Updater<boolean>) {
return selected$.update((val) => {
return (selected = updater(val));
});
},

get value() {
return selected;
},
set value(value: boolean) {
this.set(value);
}
}
};
}

2 changes: 2 additions & 0 deletions packages/core/src/table/type.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export type TableSize = 'xs' | 'sm' | 'md';
export type SortingDirection = 'ascending' | 'descending';

export type RowItem<T = any> = {
id: string
selected: boolean;
data: T;
};

0 comments on commit 252fa73

Please sign in to comment.