fix reactivity on saved point change
This commit is contained in:
parent
a5bfed73a1
commit
5a1a20df6c
2 changed files with 81 additions and 112 deletions
|
|
@ -9,7 +9,7 @@
|
|||
Input,
|
||||
InputGroup,
|
||||
InputGroupText,
|
||||
Icon
|
||||
Icon,
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
|
||||
import { getForecast } from "$lib/prediction";
|
||||
|
|
@ -58,12 +58,11 @@
|
|||
|
||||
let {
|
||||
handleClickSelectOnMap = () => console.log("Select on map clicked"),
|
||||
handleClickPointListModal = () => console.log("Open Point List Modal")
|
||||
handleClickPointListModal = () => console.log("Open Point List Modal"),
|
||||
}: Props = $props();
|
||||
|
||||
// State
|
||||
let isCollapsed = $state(false);
|
||||
let startPoint = $state("Custom");
|
||||
|
||||
// Initialize date/time
|
||||
const now = new Date();
|
||||
|
|
@ -71,64 +70,31 @@
|
|||
let startTime = $state(now.toISOString().split("T")[1].split(".")[0]);
|
||||
|
||||
// Coordinate inputs
|
||||
let inputLat = $state($FlightParametersStore.launch_latitude.toFixed(6));
|
||||
let inputLng = $state($FlightParametersStore.launch_longitude.toFixed(6));
|
||||
|
||||
// Reactive effects
|
||||
$effect(() => {
|
||||
$FlightParametersStore;
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
// Watch for saved points changes
|
||||
$SavedPointsStore;
|
||||
|
||||
// This effect also depends on startPoint, which is changed by setCoordinatesFromSavedPoint
|
||||
// via other state variables. To avoid an infinite loop, we only call this when startPoint changes.
|
||||
const sp = startPoint;
|
||||
setCoordinatesFromSavedPoint();
|
||||
});
|
||||
|
||||
// Functions
|
||||
function setCoordinatesFromSavedPoint() {
|
||||
const lat = parseFloat(inputLat);
|
||||
const lng = parseFloat(inputLng);
|
||||
if (!isNaN(lat) && !isNaN(lng)) {
|
||||
$FlightParametersStore.launch_latitude = lat;
|
||||
$FlightParametersStore.launch_longitude = lng;
|
||||
}
|
||||
else {
|
||||
const selectedOption = document.querySelector(
|
||||
`#startPoint option[value="${startPoint}"]`
|
||||
) as HTMLOptionElement;
|
||||
|
||||
if (selectedOption) {
|
||||
const lat = parseFloat(selectedOption.getAttribute("data-lat") || "0");
|
||||
const lng = parseFloat(selectedOption.getAttribute("data-lng") || "0");
|
||||
const alt = parseFloat(selectedOption.getAttribute("data-alt") || "0");
|
||||
|
||||
inputLat = lat.toFixed(6);
|
||||
inputLng = lng.toFixed(6);
|
||||
$FlightParametersStore.launch_latitude = lat;
|
||||
$FlightParametersStore.launch_longitude = lng;
|
||||
$FlightParametersStore.launch_altitude = alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
let inputLat = $derived($FlightParametersStore.start_point === "Custom"
|
||||
? $FlightParametersStore.launch_latitude.toFixed(6)
|
||||
: $SavedPointsStore.find(point => point.name === $FlightParametersStore.start_point)?.lat.toFixed(6) || "0.000000");
|
||||
let inputLng = $derived($FlightParametersStore.start_point === "Custom"
|
||||
? $FlightParametersStore.launch_longitude.toFixed(6)
|
||||
: $SavedPointsStore.find(point => point.name === $FlightParametersStore.start_point)?.lon.toFixed(6) || "0.000000");
|
||||
let inputAlt = $derived($FlightParametersStore.start_point === "Custom"
|
||||
? $FlightParametersStore.launch_altitude.toFixed(2)
|
||||
: $SavedPointsStore.find(point => point.name === $FlightParametersStore.start_point)?.alt.toFixed(2) || "0.00");
|
||||
|
||||
function setToCustomOnChange() {
|
||||
if (startPoint !== "Custom") {
|
||||
startPoint = "Custom";
|
||||
if ($FlightParametersStore.start_point !== "Custom") {
|
||||
$FlightParametersStore.start_point = "Custom";
|
||||
}
|
||||
}
|
||||
|
||||
function applyCoordinatesFromInput() {
|
||||
const lat = parseFloat(inputLat);
|
||||
const lng = parseFloat(inputLng);
|
||||
const alt = parseFloat(inputAlt);
|
||||
|
||||
if (!isNaN(lat) && !isNaN(lng)) {
|
||||
$FlightParametersStore.launch_latitude = lat;
|
||||
$FlightParametersStore.launch_longitude = lng;
|
||||
$FlightParametersStore.launch_altitude = alt || 0; // Default to 0 if alt is NaN
|
||||
} else {
|
||||
console.error("Invalid coordinate input");
|
||||
// TODO: Show validation error to user
|
||||
|
|
@ -137,23 +103,27 @@
|
|||
|
||||
async function handleGetPrediction() {
|
||||
$FlightParametersStore.launch_datetime = `${startDate}T${startTime}Z`;
|
||||
$FlightParametersStore.launch_latitude = parseFloat(inputLat);
|
||||
$FlightParametersStore.launch_longitude = parseFloat(inputLng);
|
||||
$FlightParametersStore.launch_altitude = parseFloat(inputAlt);
|
||||
writeLocalStorage<FlightParameters>("flightParameters", $FlightParametersStore);
|
||||
|
||||
getForecast($FlightParametersStore).then(
|
||||
(data) => {
|
||||
getForecast($FlightParametersStore)
|
||||
.then((data) => {
|
||||
console.log("Forecast request successful:", data);
|
||||
addToast({
|
||||
header: "Forecast Request",
|
||||
body: "Forecast request successful!",
|
||||
color: "success"
|
||||
color: "success",
|
||||
});
|
||||
// Handle the response data as needed
|
||||
}).catch((error) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error getting forecast:", error);
|
||||
addToast({
|
||||
header: "Forecast Error",
|
||||
body: `Error getting forecast: ${error.message}`,
|
||||
color: "danger"
|
||||
color: "danger",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -165,7 +135,7 @@
|
|||
// Exported functions for parent components
|
||||
export function updateLaunchPosition(lat: number, lng: number) {
|
||||
$FlightParametersStore.launch_latitude = lat;
|
||||
$FlightParametersStore.launch_longitude = lng;
|
||||
$FlightParametersStore.launch_longitude = lng;
|
||||
inputLat = lat.toFixed(6);
|
||||
inputLng = lng.toFixed(6);
|
||||
setToCustomOnChange();
|
||||
|
|
@ -191,9 +161,17 @@
|
|||
isCollapsed = !isCollapsed;
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
const savedPoints = await getSavedPoints();
|
||||
SavedPointsStore.set(savedPoints);
|
||||
onMount(() => {
|
||||
getSavedPoints()
|
||||
.then((points) => SavedPointsStore.set(points))
|
||||
.catch((error) => {
|
||||
addToast({
|
||||
header: "Error Loading Points",
|
||||
body: `Failed to load saved points: ${error.message}`,
|
||||
color: "danger",
|
||||
});
|
||||
return [];
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -218,7 +196,7 @@
|
|||
</Button>
|
||||
</button>
|
||||
</CardHeader>
|
||||
|
||||
|
||||
{#if !isCollapsed}
|
||||
<CardBody>
|
||||
<div class="d-flex gap-2">
|
||||
|
|
@ -260,10 +238,19 @@
|
|||
<FormGroup spacing="mb-2">
|
||||
<Label for="startPoint" class="form-label">Точка старта:</Label>
|
||||
<InputGroup size="sm">
|
||||
<Input type="select" id="startPoint" bind:value={startPoint} onchange={setCoordinatesFromSavedPoint}>
|
||||
<Input
|
||||
type="select"
|
||||
id="startPoint"
|
||||
bind:value={$FlightParametersStore.start_point}
|
||||
>
|
||||
<optgroup label="Сохраненные точки">
|
||||
{#each $SavedPointsStore as point}
|
||||
<option value={point.name} data-lat={point.lat} data-lng={point.lon} data-alt={point.alt}>
|
||||
<option
|
||||
value={point.name}
|
||||
data-lat={point.lat}
|
||||
data-lng={point.lon}
|
||||
data-alt={point.alt}
|
||||
>
|
||||
{point.name}
|
||||
</option>
|
||||
{/each}
|
||||
|
|
@ -272,11 +259,7 @@
|
|||
<option value="Custom">Custom</option>
|
||||
</optgroup>
|
||||
</Input>
|
||||
<Button
|
||||
color="secondary"
|
||||
title="Edit Saved Locations"
|
||||
onclick={handleClickPointListModal}
|
||||
>
|
||||
<Button color="secondary" title="Edit Saved Locations" onclick={handleClickPointListModal}>
|
||||
<span>Редакт.</span>
|
||||
<Icon name="journal-bookmark-fill" />
|
||||
</Button>
|
||||
|
|
@ -286,39 +269,29 @@
|
|||
<FormGroup spacing="mb-2">
|
||||
<Label for="latitude" class="form-label">Широта/Долгота:</Label>
|
||||
<InputGroup size="sm">
|
||||
<Input
|
||||
id="latitude"
|
||||
type="text"
|
||||
bind:value={inputLat}
|
||||
placeholder="Latitude"
|
||||
onchange={setToCustomOnChange}
|
||||
<Input
|
||||
id="latitude"
|
||||
type="text"
|
||||
bind:value={inputLat}
|
||||
placeholder="Latitude"
|
||||
onchange={setToCustomOnChange}
|
||||
/>
|
||||
<InputGroupText>/</InputGroupText>
|
||||
<Input
|
||||
id="longitude"
|
||||
type="text"
|
||||
bind:value={inputLng}
|
||||
placeholder="Longitude"
|
||||
onchange={setToCustomOnChange}
|
||||
<Input
|
||||
id="longitude"
|
||||
type="text"
|
||||
bind:value={inputLng}
|
||||
placeholder="Longitude"
|
||||
onchange={setToCustomOnChange}
|
||||
/>
|
||||
<Button
|
||||
color="success"
|
||||
size="sm"
|
||||
onclick={applyCoordinatesFromInput}
|
||||
title="Apply Coordinates"
|
||||
>
|
||||
<Button color="success" size="sm" onclick={applyCoordinatesFromInput} title="Apply Coordinates">
|
||||
✓
|
||||
</Button>
|
||||
</InputGroup>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup spacing="mb-2">
|
||||
<Button
|
||||
color="outline-secondary"
|
||||
size="sm"
|
||||
class="w-100"
|
||||
onclick={handleClickSelectOnMap}
|
||||
>
|
||||
<Button color="outline-secondary" size="sm" class="w-100" onclick={handleClickSelectOnMap}>
|
||||
Указать на карте
|
||||
</Button>
|
||||
</FormGroup>
|
||||
|
|
@ -331,7 +304,7 @@
|
|||
id="startHeight"
|
||||
class="form-control-sm"
|
||||
onchange={setToCustomOnChange}
|
||||
bind:value={$FlightParametersStore.launch_altitude}
|
||||
bind:value={inputAlt}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup class="flex-fill w-50" spacing="mb-2">
|
||||
|
|
@ -370,9 +343,7 @@
|
|||
|
||||
<div class="d-grid gap-1">
|
||||
<Button color="outline-secondary" size="sm">Показать график высоты</Button>
|
||||
<Button size="sm" color="primary" onclick={handleGetPrediction}>
|
||||
Выполнить прогнозирование
|
||||
</Button>
|
||||
<Button size="sm" color="primary" onclick={handleGetPrediction}>Выполнить прогнозирование</Button>
|
||||
</div>
|
||||
</CardBody>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,9 @@
|
|||
import { getSavedPoints, savePoint, updatePoint, deletePoint } from '$lib/api/points';
|
||||
|
||||
// Props
|
||||
let { isOpen = $bindable(false), onClose = () => {} } = $props();
|
||||
let { isOpen = $bindable(false), onClose = () => {}, onChange = () => {} } = $props();
|
||||
|
||||
// Runes
|
||||
let points = $state<SavedPoint[]>([]);
|
||||
let selectedPoint = $state<SavedPoint | null>(null);
|
||||
let newPoint = $state<SavedPoint>({ id: 0, name: '', lat: 0, lon: 0, alt: 0 });
|
||||
let isEditing = $state(false);
|
||||
|
|
@ -32,18 +31,17 @@
|
|||
let modalTitle = $derived(isEditing ? 'Редактирование точки' : 'Сохраненные точки');
|
||||
|
||||
// Table handler
|
||||
let table = $derived(new TableHandler(points, { rowsPerPage: 10 }));
|
||||
let table = $derived(new TableHandler($SavedPointsStore, { rowsPerPage: 10 }));
|
||||
|
||||
// Sync with store
|
||||
$effect(() => {
|
||||
points = $SavedPointsStore || [];
|
||||
onChange();
|
||||
});
|
||||
|
||||
// On mount, fetch points
|
||||
onMount(async () => {
|
||||
const pts = await getSavedPoints();
|
||||
points = pts;
|
||||
SavedPointsStore.set(points);
|
||||
$SavedPointsStore = pts;
|
||||
SavedPointsStore.set($SavedPointsStore);
|
||||
});
|
||||
|
||||
// Modal controls
|
||||
|
|
@ -64,8 +62,8 @@
|
|||
|
||||
function handleDeletePoint(point: SavedPoint) {
|
||||
deletePoint(point.id).then(() => {
|
||||
points = points.filter(p => p.id !== point.id);
|
||||
SavedPointsStore.set(points);
|
||||
$SavedPointsStore = $SavedPointsStore.filter(p => p.id !== point.id);
|
||||
SavedPointsStore.set($SavedPointsStore);
|
||||
addToast({
|
||||
header: 'Точка удалена',
|
||||
body: `Точка "${point.name}" успешно удалена.`,
|
||||
|
|
@ -80,8 +78,8 @@
|
|||
function handleSavePoint() {
|
||||
if (isEditing && selectedPoint) {
|
||||
updatePoint(newPoint).then(updatedPoint => {
|
||||
points = points.map(p => (p.id === updatedPoint.id ? updatedPoint : p));
|
||||
SavedPointsStore.set(points);
|
||||
$SavedPointsStore = $SavedPointsStore.map(p => (p.id === updatedPoint.id ? updatedPoint : p));
|
||||
SavedPointsStore.set($SavedPointsStore);
|
||||
resetForm();
|
||||
addToast({
|
||||
header: 'Точка обновлена',
|
||||
|
|
@ -93,8 +91,8 @@
|
|||
});
|
||||
} else {
|
||||
savePoint(newPoint).then(savedPoint => {
|
||||
points = [...points, savedPoint];
|
||||
SavedPointsStore.set(points);
|
||||
$SavedPointsStore = [...$SavedPointsStore, savedPoint];
|
||||
SavedPointsStore.set($SavedPointsStore);
|
||||
resetForm();
|
||||
addToast({
|
||||
header: 'Точка сохранена',
|
||||
|
|
@ -136,7 +134,7 @@
|
|||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Имя</th>
|
||||
<th>Название точки</th>
|
||||
<th>Широта</th>
|
||||
<th>Долгота</th>
|
||||
<th>Высота</th>
|
||||
|
|
@ -147,9 +145,9 @@
|
|||
{#each table.rows as row}
|
||||
<tr>
|
||||
<td>{row.name}</td>
|
||||
<td>{row.lat}</td>
|
||||
<td>{row.lon}</td>
|
||||
<td>{row.alt}</td>
|
||||
<td>{row.lat} °</td>
|
||||
<td>{row.lon} °</td>
|
||||
<td>{row.alt} м</td>
|
||||
<td class="fit">
|
||||
<Button color="primary" size="sm" onclick={() => handleEditPoint(row)}>
|
||||
<Icon name="pencil" />
|
||||
|
|
@ -189,7 +187,7 @@
|
|||
</Alert>
|
||||
<form onsubmit={(e) => { e.preventDefault(); handleSavePoint(); }}>
|
||||
<div class="mb-2">
|
||||
<Label for="name" class="small">Имя:</Label>
|
||||
<Label for="name" class="small">Название точки:</Label>
|
||||
<Input class="form-control-sm" type="text" id="name" bind:value={newPoint.name} required />
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue