feat: polish
This commit is contained in:
parent
2e6177fe74
commit
4bd927bb4e
137 changed files with 6357 additions and 137560 deletions
140
src/lib/features/prediction/PointEditor.svelte
Normal file
140
src/lib/features/prediction/PointEditor.svelte
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { FormGroup, Label, Input } from '@sveltestrap/sveltestrap';
|
||||
import type { SavedPoint } from '$domain';
|
||||
import { pointsApi } from '$api';
|
||||
import { addToast, ListEditor } from '$ui';
|
||||
import type { ListEditorApi, ListEditorConfig } from '$ui';
|
||||
import { t } from '$i18n';
|
||||
import { pointsStore } from './pointsStore';
|
||||
|
||||
interface Props {
|
||||
isOpen?: boolean;
|
||||
onClose?: () => void;
|
||||
onSave?: (p: SavedPoint) => void;
|
||||
onSelectPoint?: (p: SavedPoint | null) => void;
|
||||
point?: SavedPoint | null;
|
||||
}
|
||||
|
||||
let {
|
||||
isOpen = $bindable(false),
|
||||
onClose = () => {},
|
||||
onSave = (_: SavedPoint) => {},
|
||||
onSelectPoint = (_: SavedPoint | null) => {},
|
||||
point = null,
|
||||
}: Props = $props();
|
||||
|
||||
let editorRef: ListEditor<SavedPoint> | null = $state(null);
|
||||
|
||||
onMount(async () => {
|
||||
if ($pointsStore.length === 0) {
|
||||
try {
|
||||
pointsStore.set(await pointsApi.list());
|
||||
} catch (err: unknown) {
|
||||
addToast({
|
||||
header: $t('common.error'),
|
||||
body: (err as Error).message,
|
||||
color: 'danger',
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (point && editorRef) editorRef.open(point);
|
||||
});
|
||||
|
||||
const config: ListEditorConfig = {
|
||||
showTable: true,
|
||||
closeOnSave: false,
|
||||
closeOnDelete: false,
|
||||
searchBy: ['name'],
|
||||
labels: {
|
||||
item: 'Point',
|
||||
itemGenitive: 'point',
|
||||
items: 'Points',
|
||||
add: 'Add',
|
||||
edit: 'Edit',
|
||||
save: 'Save',
|
||||
update: 'Update',
|
||||
delete: 'Delete',
|
||||
cancel: 'Cancel',
|
||||
close: 'Close',
|
||||
searchPlaceholder: 'Search by name...',
|
||||
},
|
||||
};
|
||||
|
||||
const api: ListEditorApi<SavedPoint> = {
|
||||
save: (p) => pointsApi.create(p),
|
||||
update: (p) => pointsApi.update(p),
|
||||
delete: (p) => pointsApi.delete(p.id),
|
||||
};
|
||||
|
||||
const factory = (): SavedPoint => ({ id: 0, name: '', lat: 0, lon: 0, alt: 0 });
|
||||
|
||||
let items = $state<SavedPoint[]>($pointsStore);
|
||||
|
||||
$effect(() => {
|
||||
items = $pointsStore;
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
pointsStore.set(items);
|
||||
});
|
||||
|
||||
export function open(p: SavedPoint | null = null, showTable: boolean = config.showTable ?? true) {
|
||||
editorRef?.open(p, showTable);
|
||||
}
|
||||
</script>
|
||||
|
||||
<ListEditor
|
||||
bind:this={editorRef}
|
||||
bind:isOpen
|
||||
bind:items
|
||||
{api}
|
||||
{config}
|
||||
itemFactory={factory}
|
||||
onClose={() => onClose()}
|
||||
onSave={(p) => onSave(p)}
|
||||
onSelect={(p) => onSelectPoint(p)}>
|
||||
{#snippet tableHeader()}
|
||||
<tr>
|
||||
<th>{$t('points.name')}</th>
|
||||
<th>{$t('points.lat')}</th>
|
||||
<th>{$t('points.lon')}</th>
|
||||
<th>{$t('points.alt')}</th>
|
||||
<th class="fit"></th>
|
||||
</tr>
|
||||
{/snippet}
|
||||
|
||||
{#snippet tableRow({ row })}
|
||||
<td>{row.name}</td>
|
||||
<td>{row.lat.toFixed(5)} °</td>
|
||||
<td>{row.lon.toFixed(5)} °</td>
|
||||
<td>{row.alt} м</td>
|
||||
{/snippet}
|
||||
|
||||
{#snippet formFields({ item })}
|
||||
<div class="mb-2">
|
||||
<Label class="small">{$t('points.name')}</Label>
|
||||
<Input class="form-control-sm" type="text" bind:value={item.name} required />
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<FormGroup class="flex-grow-1">
|
||||
<Label class="small">{$t('points.lat')}</Label>
|
||||
<Input class="form-control-sm" type="number" step="any" bind:value={item.lat} required />
|
||||
<span class="form-text">{$t('points.degrees')}</span>
|
||||
</FormGroup>
|
||||
<FormGroup class="flex-grow-1">
|
||||
<Label class="small">{$t('points.lon')}</Label>
|
||||
<Input class="form-control-sm" type="number" step="any" bind:value={item.lon} required />
|
||||
<span class="form-text">{$t('points.degrees')}</span>
|
||||
</FormGroup>
|
||||
<FormGroup class="flex-grow-1">
|
||||
<Label class="small">{$t('points.alt')}</Label>
|
||||
<Input class="form-control-sm" type="number" step="any" bind:value={item.alt} required />
|
||||
<span class="form-text">{$t('points.metersAsl')}</span>
|
||||
</FormGroup>
|
||||
</div>
|
||||
{/snippet}
|
||||
</ListEditor>
|
||||
Loading…
Add table
Add a link
Reference in a new issue