forked from 2sys/phonograph
123 lines
3.3 KiB
Svelte
123 lines
3.3 KiB
Svelte
|
|
<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>
|