svelte cleanup
This commit is contained in:
parent
0b4b7db0be
commit
dc82afe00d
13 changed files with 6 additions and 467 deletions
|
|
@ -11,6 +11,6 @@
|
|||
<table-viewer root-path="{{ settings.root_path }}"></table-viewer>
|
||||
</main>
|
||||
</div>
|
||||
<script src="{{ settings.root_path }}/js_dist/TableViewer.js"></script>
|
||||
<script src="{{ settings.root_path }}/js_dist/table-viewer.webc.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Svelte + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Learn more at https://docs.deno.com/runtime/manual/examples/module_metadata#concepts
|
||||
if (import.meta.main) {
|
||||
console.log("Add 2 + 3 =", add(2, 3));
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import { assertEquals } from "@std/assert";
|
||||
import { add } from "./main.ts";
|
||||
|
||||
Deno.test(function addTest() {
|
||||
assertEquals(add(2, 3), 5);
|
||||
});
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<script lang="ts">
|
||||
import svelteLogo from './assets/svelte.svg'
|
||||
import viteLogo from '/vite.svg'
|
||||
import Counter from './lib/Counter.svelte'
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div>
|
||||
<a href="https://vite.dev" target="_blank" rel="noreferrer">
|
||||
<img src={viteLogo} class="logo" alt="Vite Logo" />
|
||||
</a>
|
||||
<a href="https://svelte.dev" target="_blank" rel="noreferrer">
|
||||
<img src={svelteLogo} class="logo svelte" alt="Svelte Logo" />
|
||||
</a>
|
||||
</div>
|
||||
<h1>Vite + Svelte</h1>
|
||||
|
||||
<div class="card">
|
||||
<Counter />
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out <a href="https://github.com/sveltejs/kit#readme" target="_blank" rel="noreferrer">SvelteKit</a>, the official Svelte app framework powered by Vite!
|
||||
</p>
|
||||
|
||||
<p class="read-the-docs">
|
||||
Click on the Vite and Svelte logos to learn more
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.svelte:hover {
|
||||
filter: drop-shadow(0 0 2em #ff3e00aa);
|
||||
}
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
:root {
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="26.6" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 308"><path fill="#FF3E00" d="M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244"></path><path fill="#FFF" d="M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
|
|
@ -1,122 +0,0 @@
|
|||
<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>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<script lang="ts">
|
||||
let count: number = $state(0)
|
||||
const increment = () => {
|
||||
count += 1
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={increment}>
|
||||
count is {count}
|
||||
</button>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import { mount } from 'svelte'
|
||||
import './app.css'
|
||||
import App from './App.svelte'
|
||||
|
||||
const app = mount(App, {
|
||||
target: document.getElementById('app')!,
|
||||
})
|
||||
|
||||
export default app
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
<svelte:options customElement={{ tag: "table-viewer", shadow: "none" }} />
|
||||
|
||||
<script lang="ts">
|
||||
import { type Encodable, type Field } from "./field.svelte";
|
||||
|
||||
type Coords = [string, string];
|
||||
interface CommittedChange {
|
||||
coords_initial: Coords;
|
||||
// This will be identical to coords_initial, unless the change altered a
|
||||
// primary key.
|
||||
coords_updated: Coords;
|
||||
value_initial: Encodable;
|
||||
value_updated: Encodable;
|
||||
}
|
||||
|
||||
let selections = $state<Coords[]>([]);
|
||||
let table_focused = $state(false);
|
||||
let editing = $state(false);
|
||||
let editor_input_value = $state("");
|
||||
let committed_changes = $state<CommittedChange[][]>([]);
|
||||
let reverted_changes = $state<CommittedChange[][]>([]);
|
||||
let editor_input_element = $state<HTMLInputElement | undefined>();
|
||||
|
||||
interface GetDataResult {
|
||||
pkeys: string[];
|
||||
data: Record<string, Record<string, Encodable>>;
|
||||
fields: Record<string, Field>;
|
||||
field_names: string[];
|
||||
}
|
||||
|
||||
function coords_eq(a: Coords, b: Coords): boolean {
|
||||
return a[0] === b[0] && a[1] === b[1];
|
||||
}
|
||||
|
||||
function handle_click(ev: MouseEvent, coords: Coords, cell_data: Encodable) {
|
||||
if (!editing) {
|
||||
if (ev.metaKey || ev.ctrlKey) {
|
||||
// TODO
|
||||
// selections = [...selections.filter((prev) => !coords_eq(prev, coords)), coords];
|
||||
// editor_input_value = "";
|
||||
} else {
|
||||
selections = [coords];
|
||||
if (cell_data.t === "Text" || cell_data.t === "Uuid") {
|
||||
editor_input_value = cell_data.c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handle_dblclick(_coords: Coords) {
|
||||
if (!editing) {
|
||||
editing = true;
|
||||
editor_input_element?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function handle_table_focusin() {
|
||||
table_focused = true;
|
||||
}
|
||||
|
||||
function handle_table_focusout() {
|
||||
table_focused = false;
|
||||
}
|
||||
|
||||
function handle_keydown(ev: KeyboardEvent) {
|
||||
if (table_focused && ev.key === "ArrowDown") {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
document.addEventListener("keydown", handle_keydown);
|
||||
return () => document.removeEventListener("keydown", handle_keydown);
|
||||
});
|
||||
|
||||
const data_promise: Promise<GetDataResult> = fetch("get-data")
|
||||
.then((resp) => resp.json())
|
||||
.then((body) => ({
|
||||
...body,
|
||||
data: Object.fromEntries(body.data.map(({ pkey, data }) => [
|
||||
pkey,
|
||||
Object.fromEntries(body.fields.map(({ name }, i) => [name, data[i]])),
|
||||
])),
|
||||
field_names: body.fields.map(({ name }) => name),
|
||||
fields: Object.fromEntries(body.fields.map((field) => [field.name, field])),
|
||||
}));
|
||||
</script>
|
||||
|
||||
{#await data_promise}
|
||||
<div class="loading-indicator">Loading...</div>
|
||||
{:then data}
|
||||
<div
|
||||
class="viewer-table"
|
||||
onfocusin={handle_table_focusin}
|
||||
onfocusout={handle_table_focusout}
|
||||
role="grid"
|
||||
tabindex="0"
|
||||
>
|
||||
<div class="viewer-table__headers">
|
||||
{#each data.field_names as field_name, field_index}
|
||||
<div
|
||||
aria-colindex={field_index}
|
||||
class="viewer-header"
|
||||
role="columnheader"
|
||||
style:width={`${data.fields[field_name].width_px}px`}
|
||||
>
|
||||
<div>{data.fields[field_name].label ?? field_name}</div>
|
||||
</div>
|
||||
{/each}
|
||||
<div class="viewer-header-actions">
|
||||
TODO
|
||||
</div>
|
||||
</div>
|
||||
<div class="viewer-table__body">
|
||||
{#each data.pkeys as pkey, row_index}
|
||||
<div aria-rowindex={row_index} class="viewer-row" role="row">
|
||||
{#each data.field_names as field_name, field_index}
|
||||
{@const cell_data = data.data[pkey][field_name]}
|
||||
{@const selected = selections.some(
|
||||
([sel_pkey, sel_field]) => sel_pkey === pkey && sel_field === field_name,
|
||||
)}
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div
|
||||
aria-colindex={field_index}
|
||||
aria-rowindex={row_index}
|
||||
aria-selected={selected}
|
||||
class="viewer-cell"
|
||||
onclick={(ev) => handle_click(ev, [pkey, field_name], cell_data)}
|
||||
ondblclick={() => handle_dblclick([pkey, field_name])}
|
||||
role="gridcell"
|
||||
style:width={`${data.fields[field_name].width_px}px`}
|
||||
tabindex="-1"
|
||||
>
|
||||
{#if cell_data.t === "Text"}
|
||||
<div
|
||||
class={[
|
||||
"viewer-cell__content",
|
||||
"viewer-cell__content--text",
|
||||
selections.some((coords) => coords_eq(coords, [pkey, field_name])) && "viewer-cell__content--selected",
|
||||
]}
|
||||
>
|
||||
{cell_data.c}
|
||||
</div>
|
||||
{:else if cell_data.t === "Uuid"}
|
||||
<div class="viewer-cell__content viewer-cell__content--uuid">{cell_data.c}</div>
|
||||
{:else}
|
||||
<div class="viewer-cell__content viewer-cell__content--unknown">Unknown type</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor">
|
||||
<input
|
||||
class={["editor__input", selections.length !== 1 && "editor__input--hidden"]}
|
||||
onfocus={() => {
|
||||
editing = true;
|
||||
}}
|
||||
role="combobox"
|
||||
bind:this={editor_input_element}
|
||||
bind:value={editor_input_value}
|
||||
/>
|
||||
</div>
|
||||
{:catch err}
|
||||
Error {err}
|
||||
{/await}
|
||||
|
|
@ -7,9 +7,11 @@ export default defineConfig({
|
|||
plugins: [svelte()],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: path.fromFileUrl(
|
||||
new URL("./src/TableViewer.svelte", import.meta.url),
|
||||
),
|
||||
input: [
|
||||
...Deno.readDirSync("./src")
|
||||
.filter(({ name }) => name.endsWith(".webc.svelte"))
|
||||
.map(({ name }) => path.join("./src", name)),
|
||||
],
|
||||
output: {
|
||||
dir: path.fromFileUrl(new URL("../js_dist", import.meta.url)),
|
||||
entryFileNames: "[name].js",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue