1
0
Fork 0
forked from 2sys/phonograph
phonograph/svelte/src/interactive-table.svelte

123 lines
3.3 KiB
Svelte
Raw Normal View History

2025-08-10 14:32:15 -07:00
<script lang="ts">
import {
type Coords,
type Field,
type Row,
coords_eq,
} from "./field.svelte";
type Props = {
fields: Field[];
header_classes?: (string | boolean | undefined)[];
on_cell_click?(ev: MouseEvent, coords: Coords): void;
on_cell_dblclick?(coords: Coords): void;
on_focus?(): void;
on_keydown?(ev: KeyboardEvent): void;
rows: Row[];
root_element?: HTMLDivElement;
selections: Coords[];
show_headers?: boolean;
tab_index?: number;
};
let {
fields,
header_classes = [],
on_cell_click,
on_cell_dblclick,
on_focus,
on_keydown,
root_element = $bindable(),
rows,
selections,
show_headers = true,
tab_index = 0,
}: Props = $props();
</script>
<div
bind:this={root_element}
class="viewer-table"
onfocus={on_focus}
onkeydown={on_keydown}
role="grid"
tabindex={tab_index}
>
{#if show_headers}
<div class={["viewer-table__headers", ...header_classes]}>
{#each fields as field, field_index}
<div
aria-colindex={field_index}
class="viewer-header"
role="columnheader"
style:width={`${field.width_px}px`}
>
<div>{field.label ?? field.name}</div>
</div>
{/each}
<div class="viewer-header-actions">
TODO
</div>
</div>
{/if}
<div class="viewer-table__body">
{#each rows as row, row_index}
<div aria-rowindex={row_index} class="viewer-row" role="row">
{#each fields as field, field_index}
{@const cell_data = row.data[field_index]}
{@const cell_coords: Coords = [row_index, field_index]}
{@const cell_selected = selections.some(
(sel_coords) => coords_eq(sel_coords, cell_coords),
)}
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
aria-colindex={field_index}
aria-rowindex={row_index}
aria-selected={cell_selected}
class={[
"viewer-cell",
cell_selected && "viewer-cell--selected",
]}
onclick={(ev) => on_cell_click?.(ev, cell_coords)}
ondblclick={() => on_cell_dblclick?.(cell_coords)}
role="gridcell"
style:width={`${field.width_px}px`}
tabindex="-1"
>
{#if cell_data.t === "Text"}
<div
class={[
"viewer-cell__content",
"viewer-cell__content--text",
cell_data.c === null && "viewer-cell__content--null",
]}
>
{cell_data.c}
</div>
{:else if cell_data.t === "Uuid"}
<div
class={[
"viewer-cell__content",
"viewer-cell__content--uuid",
cell_data.c === null && "viewer-cell__content--null",
]}
>
{cell_data.c}
</div>
{:else}
<div
class={[
"viewer-cell__content",
"viewer-cell__content--unknown",
]}
>
UNKNOWN
</div>
{/if}
</div>
{/each}
</div>
{/each}
</div>
</div>