refactor zo zod and felte
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let name: string = "";
|
||||
export let value: string | Date = "";
|
||||
export let label: string = "";
|
||||
export let error: string = "";
|
||||
export let error: string | string[] | null = "";
|
||||
</script>
|
||||
|
||||
<div>
|
||||
@@ -15,15 +17,17 @@
|
||||
<input
|
||||
{name}
|
||||
class="bg-input-background border border-input-border rounded-md p-3 transition-colors focus:border-input-border-focus focus:outline-none focus:ring-0 w-full"
|
||||
class:border-red-400={error.length > 0}
|
||||
class:bg-red-50={error.length > 0}
|
||||
class:border-red-400={(error?.length ?? 0) > 0}
|
||||
class:bg-input-background-error={(error?.length ?? 0) > 0}
|
||||
type="date"
|
||||
bind:value
|
||||
on:change
|
||||
/>
|
||||
</div>
|
||||
|
||||
<span class="textfield-error text-xs text-red-400">
|
||||
{error}
|
||||
</span>
|
||||
{#if error}
|
||||
<span class="textfield-error text-xs text-red-400">
|
||||
{error instanceof Array ? $_(error[0]) : error}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
export let name: string = "";
|
||||
export let value: any;
|
||||
export let items: SelectItem[] = [];
|
||||
export let value: any = items.at(0)?.value ?? "";
|
||||
export let label: string = "";
|
||||
export let disabled: boolean = false;
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let name: string = "";
|
||||
export let value: string | number = "";
|
||||
export let placeholder: string = "";
|
||||
export let disabled: boolean = false;
|
||||
export let label: string = "";
|
||||
export let error: string = "";
|
||||
export let error: string | string[] | null = "";
|
||||
export let icon: string = "";
|
||||
export let extraClasses: string = "";
|
||||
export let type: "text" | "password" | "search" = "text";
|
||||
@@ -29,8 +31,8 @@
|
||||
<input
|
||||
{name}
|
||||
class="bg-input-background border border-input-border rounded-md p-3 transition-colors focus:border-input-border-focus focus:outline-none focus:ring-0 w-full {extraClasses}"
|
||||
class:border-red-400={error.length > 0}
|
||||
class:bg-input-background-error={error.length > 0}
|
||||
class:border-red-400={(error?.length ?? 0) > 0}
|
||||
class:bg-input-background-error={(error?.length ?? 0) > 0}
|
||||
class:text-gray-500={disabled}
|
||||
{disabled}
|
||||
{autocomplete}
|
||||
@@ -44,7 +46,9 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<span class="textfield-error text-xs text-red-400">
|
||||
{error}
|
||||
</span>
|
||||
{#if error}
|
||||
<span class="textfield-error text-xs text-red-400">
|
||||
{error instanceof Array ? $_(error[0]) : error}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let name: string = "";
|
||||
export let value: string | number = "";
|
||||
export let placeholder: string = "";
|
||||
export let rows: number = 3;
|
||||
export let label: string = "";
|
||||
export let error: string = "";
|
||||
export let error: string | string[] | null = "";
|
||||
export let extraClasses: string = "";
|
||||
</script>
|
||||
|
||||
@@ -19,12 +21,14 @@
|
||||
class="bg-input-background border border-input-border rounded-md p-3 resize-none transition-colors focus:border-input-border-focus focus:outline-none focus:ring-0 w-full {extraClasses}"
|
||||
{rows}
|
||||
{placeholder}
|
||||
class:border-red-400={error.length > 0}
|
||||
class:bg-red-50={error.length > 0}
|
||||
class:border-red-400={(error?.length ?? 0) > 0}
|
||||
class:bg-input-background-error={(error?.length ?? 0) > 0}
|
||||
bind:value
|
||||
on:change
|
||||
/>
|
||||
<span class="textfield-error text-xs text-red-400">
|
||||
{error}
|
||||
</span>
|
||||
{#if error}
|
||||
<span class="textfield-error text-xs text-red-400">
|
||||
{error instanceof Array ? $_(error[0]) : error}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -125,13 +125,17 @@
|
||||
{#each { length: 5 } as _, index}
|
||||
<SkeletonNotificationCard></SkeletonNotificationCard>
|
||||
{/each}
|
||||
{:else}
|
||||
{:else if notifications.length}
|
||||
{#each notifications as notification}
|
||||
<NotificationCard
|
||||
on:click={handleNotificationClick}
|
||||
{notification}
|
||||
></NotificationCard>
|
||||
{/each}
|
||||
{:else}
|
||||
<p class="text-gray-500 text-sm text-center py-6">
|
||||
No notifications
|
||||
</p>
|
||||
{/if}
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<script lang="ts">
|
||||
import Modal from "$lib/components/base/modal.svelte";
|
||||
import { validator } from "@felte/validator-zod";
|
||||
import { createForm } from "felte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { z } from "zod";
|
||||
import TextField from "../base/text_field.svelte";
|
||||
import { createForm } from "$lib/vendor/svelte-form-lib";
|
||||
import { object, string } from "yup";
|
||||
|
||||
let _openModal: (() => void) | undefined = undefined;
|
||||
export let closeModal: (() => void) | undefined = undefined;
|
||||
export function openModal() {
|
||||
$form.email = email
|
||||
$errors.email = "";
|
||||
setFields("email", email);
|
||||
setErrors("email", []);
|
||||
_openModal!();
|
||||
}
|
||||
|
||||
@@ -18,17 +19,20 @@
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const { form, errors, handleChange, handleSubmit } = createForm<{
|
||||
const { form, errors, setFields, setErrors } = createForm<{
|
||||
email: string;
|
||||
}>({
|
||||
initialValues: { email: email },
|
||||
validationSchema: object({
|
||||
email: string()
|
||||
.email($_("not-a-valid-email-address"))
|
||||
.required($_("required")),
|
||||
extend: validator({
|
||||
schema: z.object({
|
||||
email: z
|
||||
.string()
|
||||
.min(1, "required")
|
||||
.email("not-a-valid-email-address"),
|
||||
}),
|
||||
}),
|
||||
onSubmit: async (_) => {
|
||||
dispatch("save", $form.email);
|
||||
onSubmit: async (form) => {
|
||||
dispatch("save", form.email);
|
||||
closeModal!();
|
||||
},
|
||||
});
|
||||
@@ -41,13 +45,8 @@
|
||||
bind:openModal={_openModal}
|
||||
bind:closeModal
|
||||
>
|
||||
<form id="email-form" slot="content" on:submit={handleSubmit}>
|
||||
<TextField
|
||||
name="email"
|
||||
bind:value={$form.email}
|
||||
error={$errors.email}
|
||||
on:change={handleChange}
|
||||
></TextField>
|
||||
<form id="email-form" slot="content" use:form>
|
||||
<TextField name="email" error={$errors.email}></TextField>
|
||||
</form>
|
||||
<div slot="footer" class="flex items-center gap-4">
|
||||
<button class="btn-secondary" on:click={closeModal}
|
||||
|
||||
@@ -3,46 +3,61 @@
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import TextField from "../base/text_field.svelte";
|
||||
import { createForm } from "$lib/vendor/svelte-form-lib";
|
||||
import { object, string } from "yup";
|
||||
import { validator } from "@felte/validator-zod";
|
||||
import { createForm } from "felte";
|
||||
import { z } from "zod";
|
||||
|
||||
let _openModal: (() => void) | undefined = undefined;
|
||||
export let closeModal: (() => void) | undefined = undefined;
|
||||
export function openModal() {
|
||||
$form.password = ""
|
||||
$errors.password = "";
|
||||
$form.oldPassword = ""
|
||||
$errors.oldPassword = "";
|
||||
setFields("password", "")
|
||||
setFields("oldPassword", "")
|
||||
setErrors("password", [])
|
||||
setErrors("oldPassword", [])
|
||||
_openModal!();
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const { form, errors, handleChange, handleSubmit } = createForm<{
|
||||
const { form, errors, setFields, setErrors } = createForm<{
|
||||
oldPassword: string;
|
||||
password: string;
|
||||
passwordConfirm: string;
|
||||
}>({
|
||||
initialValues: { oldPassword: "", password: "" },
|
||||
validationSchema: object({
|
||||
oldPassword: string()
|
||||
.min(
|
||||
8,
|
||||
$_("must-be-at-least-n-characters-long", {
|
||||
values: { n: 8 },
|
||||
}),
|
||||
)
|
||||
.required($_("required")),
|
||||
password: string()
|
||||
.min(
|
||||
8,
|
||||
$_("must-be-at-least-n-characters-long", {
|
||||
values: { n: 8 },
|
||||
}),
|
||||
)
|
||||
.required($_("required")),
|
||||
extend: validator({
|
||||
schema: z
|
||||
.object({
|
||||
oldPassword: z.string().min(
|
||||
8,
|
||||
$_("must-be-at-least-n-characters-long", {
|
||||
values: { n: 8 },
|
||||
}),
|
||||
),
|
||||
password: z.string().min(
|
||||
8,
|
||||
$_("must-be-at-least-n-characters-long", {
|
||||
values: { n: 8 },
|
||||
}),
|
||||
),
|
||||
passwordConfirm: z.string().min(
|
||||
8,
|
||||
$_("must-be-at-least-n-characters-long", {
|
||||
values: { n: 8 },
|
||||
}),
|
||||
),
|
||||
})
|
||||
.refine((d) => d.password === d.passwordConfirm, {
|
||||
message: "passwords-must-match",
|
||||
path: ["passwordConfirm"],
|
||||
}),
|
||||
}),
|
||||
onSubmit: async (_) => {
|
||||
dispatch("save", {oldPassword: $form.oldPassword, password: $form.password, passwordConfirm: $form.password});
|
||||
onSubmit: async (form) => {
|
||||
dispatch("save", {
|
||||
oldPassword: form.oldPassword,
|
||||
password: form.password,
|
||||
passwordConfirm: form.password,
|
||||
});
|
||||
closeModal!();
|
||||
},
|
||||
});
|
||||
@@ -55,30 +70,35 @@
|
||||
bind:openModal={_openModal}
|
||||
bind:closeModal
|
||||
>
|
||||
<form id="password-form" slot="content" on:submit={handleSubmit}>
|
||||
<form id="password-form" slot="content" use:form>
|
||||
<TextField
|
||||
name="oldPassword"
|
||||
label={$_("current-password")}
|
||||
type="password"
|
||||
bind:value={$form.oldPassword}
|
||||
error={$errors.oldPassword}
|
||||
on:change={handleChange}
|
||||
></TextField>
|
||||
<TextField
|
||||
name="password"
|
||||
label={$_("new-password")}
|
||||
type="password"
|
||||
bind:value={$form.password}
|
||||
error={$errors.password}
|
||||
on:change={handleChange}
|
||||
></TextField>
|
||||
<TextField
|
||||
name="passwordConfirm"
|
||||
label={$_("password-confirm")}
|
||||
type="password"
|
||||
error={$errors.passwordConfirm}
|
||||
></TextField>
|
||||
</form>
|
||||
<div slot="footer" class="flex items-center gap-4">
|
||||
<button class="btn-secondary" on:click={closeModal}
|
||||
>{$_("cancel")}</button
|
||||
>
|
||||
<button class="btn-primary" type="submit" form="password-form" name="save"
|
||||
>{$_("save")}</button
|
||||
<button
|
||||
class="btn-primary"
|
||||
type="submit"
|
||||
form="password-form"
|
||||
name="save">{$_("save")}</button
|
||||
>
|
||||
</div></Modal
|
||||
>
|
||||
|
||||
@@ -1,41 +1,50 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import { type SummitLog } from "$lib/models/summit_log";
|
||||
import {
|
||||
SummitLogCreateSchema,
|
||||
} from "$lib/models/api/summit_log_schema";
|
||||
import GPX from "$lib/models/gpx/gpx";
|
||||
import { summitLog } from "$lib/stores/summit_log_store";
|
||||
import { createForm } from "$lib/vendor/svelte-form-lib/index";
|
||||
import { util } from "$lib/vendor/svelte-form-lib/util";
|
||||
import { createForm } from "felte";
|
||||
import { validator } from "@felte/validator-zod";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { date, object, string } from "yup";
|
||||
import { z } from "zod";
|
||||
import Datepicker from "../base/datepicker.svelte";
|
||||
import Modal from "../base/modal.svelte";
|
||||
import Textarea from "../base/textarea.svelte";
|
||||
import TrailPicker from "../trail/trail_picker.svelte";
|
||||
import GPX from "$lib/models/gpx/gpx";
|
||||
import PhotoPicker from "../trail/photo_picker.svelte";
|
||||
import TrailPicker from "../trail/trail_picker.svelte";
|
||||
import { cloneDeep } from "$lib/util/deep_util";
|
||||
export let openModal: (() => void) | undefined = undefined;
|
||||
export let closeModal: (() => void) | undefined = undefined;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const summitLogSchema = object<SummitLog>({
|
||||
id: string().optional(),
|
||||
date: date().required("Required").typeError($_("invalid-date")),
|
||||
text: string().optional(),
|
||||
const ClientSummitLogCreateSchema = SummitLogCreateSchema.extend({
|
||||
_photos: z.array(z.instanceof(File)).optional(),
|
||||
_gpx: z.instanceof(File).optional().nullable(),
|
||||
expand: z
|
||||
.object({
|
||||
gpx_data: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
const { form, errors, handleChange, handleSubmit } = createForm<SummitLog>({
|
||||
const { form, errors, data, setFields } = createForm<
|
||||
z.infer<typeof ClientSummitLogCreateSchema>
|
||||
>({
|
||||
initialValues: $summitLog,
|
||||
validationSchema: summitLogSchema,
|
||||
onSubmit: async (submittedValues) => {
|
||||
if (!$form.expand.gpx_data) {
|
||||
$form.gpx = "";
|
||||
extend: validator({ schema: ClientSummitLogCreateSchema }),
|
||||
onSubmit: async (form) => {
|
||||
if (!form.expand?.gpx_data) {
|
||||
form.gpx = "";
|
||||
}
|
||||
|
||||
if (
|
||||
!$form._photos?.length &&
|
||||
!$form.photos?.length &&
|
||||
$form.expand?.gpx_data
|
||||
!form._photos?.length &&
|
||||
!form.photos?.length &&
|
||||
form.expand?.gpx_data
|
||||
) {
|
||||
const canvas = document.querySelector(
|
||||
"#trail-picker-map .maplibregl-canvas",
|
||||
@@ -44,25 +53,25 @@
|
||||
const dataURL = canvas.toDataURL();
|
||||
const response = await fetch(dataURL);
|
||||
const blob = await response.blob();
|
||||
$form._photos = [new File([blob], "route")];
|
||||
form._photos = [new File([blob], "route")];
|
||||
}
|
||||
|
||||
dispatch("save", submittedValues);
|
||||
dispatch("save", form);
|
||||
closeModal!();
|
||||
},
|
||||
});
|
||||
$: form.set(util.cloneDeep($summitLog));
|
||||
$: setFields(cloneDeep($summitLog));
|
||||
|
||||
$: if ($summitLog._gpx) {
|
||||
$form._gpx = $summitLog._gpx;
|
||||
$data._gpx = $summitLog._gpx;
|
||||
}
|
||||
|
||||
async function handleTrailSelection(trailData: string | null) {
|
||||
if (!trailData) {
|
||||
$form.duration = undefined;
|
||||
$form.elevation_gain = undefined;
|
||||
$form.elevation_loss = undefined;
|
||||
$form.distance = undefined;
|
||||
$data.duration = undefined;
|
||||
$data.elevation_gain = undefined;
|
||||
$data.elevation_loss = undefined;
|
||||
$data.distance = undefined;
|
||||
return;
|
||||
}
|
||||
const gpxObject = await GPX.parse(trailData);
|
||||
@@ -72,16 +81,15 @@
|
||||
|
||||
const totals = gpxObject.getTotals();
|
||||
|
||||
$form.duration = totals.duration / 1000;
|
||||
$form.elevation_gain = totals.elevationGain;
|
||||
$form.elevation_loss = totals.elevationLoss;
|
||||
$form.distance = totals.distance;
|
||||
$data.duration = totals.duration / 1000;
|
||||
$data.elevation_gain = totals.elevationGain;
|
||||
$data.elevation_loss = totals.elevationLoss;
|
||||
$data.distance = totals.distance;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
id="summit-log-modal"
|
||||
title={$form.id ? $_("edit-entry") : $_("add-entry")}
|
||||
title={$data.id ? $_("edit-entry") : $_("add-entry")}
|
||||
let:openModal
|
||||
bind:openModal
|
||||
bind:closeModal
|
||||
@@ -91,15 +99,10 @@
|
||||
id="summit-log-form"
|
||||
slot="content"
|
||||
class="modal-content space-y-4"
|
||||
on:submit={handleSubmit}
|
||||
use:form
|
||||
>
|
||||
<div class="flex">
|
||||
<Datepicker
|
||||
name="date"
|
||||
label={$_("date")}
|
||||
bind:value={$form.date}
|
||||
error={$errors.date}
|
||||
on:change={handleChange}
|
||||
<Datepicker name="date" label={$_("date")} error={$errors.date}
|
||||
></Datepicker>
|
||||
</div>
|
||||
<div>
|
||||
@@ -108,27 +111,27 @@
|
||||
</label>
|
||||
<PhotoPicker
|
||||
id="summitlog-photo-input"
|
||||
parent={$form}
|
||||
bind:photos={$form.photos}
|
||||
bind:photoFiles={$form._photos}
|
||||
parent={$data}
|
||||
bind:photos={$data.photos}
|
||||
bind:photoFiles={$data._photos}
|
||||
showThumbnailControls={false}
|
||||
></PhotoPicker>
|
||||
</div>
|
||||
<div class="flex gap-4">
|
||||
<TrailPicker
|
||||
bind:trailFile={$form._gpx}
|
||||
bind:trailData={$form.expand.gpx_data}
|
||||
label={$_("trail", { values: { n: 1 } })}
|
||||
on:change={(e) => handleTrailSelection(e.detail)}
|
||||
></TrailPicker>
|
||||
{#if $data.expand}
|
||||
<TrailPicker
|
||||
bind:trailFile={$data._gpx}
|
||||
bind:trailData={$data.expand.gpx_data}
|
||||
label={$_("trail", { values: { n: 1 } })}
|
||||
on:change={(e) => handleTrailSelection(e.detail)}
|
||||
></TrailPicker>
|
||||
{/if}
|
||||
<div class="basis-full">
|
||||
<Textarea
|
||||
name="text"
|
||||
extraClasses="h-28"
|
||||
label={$_("text")}
|
||||
bind:value={$form.text}
|
||||
error={$errors.text}
|
||||
on:change={handleChange}
|
||||
></Textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { fromFile, toGeoJson } from "$lib/util/gpx_util";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
export let trailFile: File | null;
|
||||
export let trailFile: File | undefined | null;
|
||||
export let trailData: string | undefined;
|
||||
export let label: string = "";
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
attributionControl: false,
|
||||
dragPan: false,
|
||||
scrollZoom: false,
|
||||
preserveDrawingBuffer: true
|
||||
preserveDrawingBuffer: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -95,9 +95,12 @@
|
||||
},
|
||||
});
|
||||
layer = map.getLayer(layerId) as M.LineLayerSpecification;
|
||||
source = map.getSource(sourceId) as M.GeoJSONSource;
|
||||
map.resize()
|
||||
map.fitBounds(geojson.bbox as M.LngLatBoundsLike, {animate: false, padding: 8});
|
||||
source = map.getSource(sourceId) as M.GeoJSONSource;
|
||||
map.resize();
|
||||
map.fitBounds(geojson.bbox as M.LngLatBoundsLike, {
|
||||
animate: false,
|
||||
padding: 8,
|
||||
});
|
||||
}
|
||||
|
||||
function removeTrailFromMap() {
|
||||
|
||||
@@ -1,42 +1,58 @@
|
||||
<script lang="ts">
|
||||
import { Waypoint, waypointSchema } from "$lib/models/waypoint";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import { WaypointCreateSchema } from "$lib/models/api/waypoint_schema";
|
||||
import { convertDMSToDD } from "$lib/models/gpx/utils";
|
||||
import { show_toast } from "$lib/stores/toast_store";
|
||||
import { waypoint } from "$lib/stores/waypoint_store";
|
||||
import { cloneDeep } from "$lib/util/deep_util";
|
||||
import { icons } from "$lib/util/icon_util";
|
||||
import EXIF from "$lib/vendor/exif-js/exif";
|
||||
import { createForm } from "$lib/vendor/svelte-form-lib/index";
|
||||
import { util } from "$lib/vendor/svelte-form-lib/util";
|
||||
import { validator } from "@felte/validator-zod";
|
||||
import { createForm } from "felte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { z } from "zod";
|
||||
import Combobox from "../base/combobox.svelte";
|
||||
import Modal from "../base/modal.svelte";
|
||||
import TextField from "../base/text_field.svelte";
|
||||
import Textarea from "../base/textarea.svelte";
|
||||
import PhotoPicker from "../trail/photo_picker.svelte";
|
||||
import { show_toast } from "$lib/stores/toast_store";
|
||||
import { convertDMSToDD } from "$lib/models/gpx/utils";
|
||||
|
||||
export let openModal: (() => void) | undefined = undefined;
|
||||
export let closeModal: (() => void) | undefined = undefined;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const { form, errors, handleChange, handleSubmit } = createForm<Waypoint>({
|
||||
const ClientWaypointCreateSchema = WaypointCreateSchema.extend({
|
||||
_photos: z.array(z.instanceof(File)).optional(),
|
||||
});
|
||||
|
||||
const { form, errors, data, setFields } = createForm<
|
||||
z.infer<typeof ClientWaypointCreateSchema>
|
||||
>({
|
||||
initialValues: $waypoint,
|
||||
validationSchema: waypointSchema,
|
||||
onSubmit: async (submittedWaypoint) => {
|
||||
dispatch("save", submittedWaypoint);
|
||||
extend: validator({ schema: ClientWaypointCreateSchema }),
|
||||
onSubmit: async (form) => {
|
||||
dispatch("save", form);
|
||||
|
||||
closeModal!();
|
||||
},
|
||||
transform: (values: unknown) => {
|
||||
const v = values as any;
|
||||
return {
|
||||
...v,
|
||||
lat: parseFloat(v.lat),
|
||||
lon: parseFloat(v.lon),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
$: form.set(util.cloneDeep($waypoint));
|
||||
$: setFields(cloneDeep($waypoint));
|
||||
|
||||
$: filteredIcons =
|
||||
($form.icon?.length ?? 0) > 2
|
||||
($data.icon?.length ?? 0) > 2
|
||||
? icons
|
||||
.filter((i) => i.includes($form.icon ?? ""))
|
||||
.filter((i) => i.includes($data.icon ?? ""))
|
||||
.map((i) => ({ text: i, value: i, icon: i }))
|
||||
: [];
|
||||
|
||||
@@ -48,8 +64,8 @@
|
||||
const lonDir = EXIF.getTag(p, "GPSLongitudeRef");
|
||||
|
||||
if (lat && lon) {
|
||||
$form.lat = convertDMSToDD(lat, latDir);
|
||||
$form.lon = convertDMSToDD(lon, lonDir);
|
||||
setFields("lat", convertDMSToDD(lat, latDir));
|
||||
setFields("lon", convertDMSToDD(lon, lonDir));
|
||||
} else {
|
||||
show_toast({
|
||||
text: "No GPS data in image",
|
||||
@@ -63,7 +79,7 @@
|
||||
|
||||
<Modal
|
||||
id="waypoint-modal"
|
||||
title={$form.id ? $_("edit-waypoint") : $_("add-waypoint")}
|
||||
title={$data.id ? $_("edit-waypoint") : $_("add-waypoint")}
|
||||
let:openModal
|
||||
bind:openModal
|
||||
bind:closeModal
|
||||
@@ -73,50 +89,31 @@
|
||||
id="waypoint-form"
|
||||
slot="content"
|
||||
class="modal-content space-y-4"
|
||||
on:submit={handleSubmit}
|
||||
use:form
|
||||
>
|
||||
<div class="flex gap-4">
|
||||
<div class="basis-2/3">
|
||||
<TextField
|
||||
name="name"
|
||||
label={$_("name")}
|
||||
bind:value={$form.name}
|
||||
error={$errors.name}
|
||||
on:change={handleChange}
|
||||
<TextField name="name" label={$_("name")} error={$errors.name}
|
||||
></TextField>
|
||||
</div>
|
||||
|
||||
<Combobox
|
||||
name="icon"
|
||||
bind:value={$form.icon}
|
||||
icon={$form.icon}
|
||||
icon={$data.icon}
|
||||
items={filteredIcons}
|
||||
label={$_("icon")}
|
||||
on:change={handleChange}
|
||||
></Combobox>
|
||||
</div>
|
||||
|
||||
<Textarea
|
||||
name="description"
|
||||
label={$_("description")}
|
||||
bind:value={$form.description}
|
||||
error={$errors.description}
|
||||
on:change={handleChange}
|
||||
></Textarea>
|
||||
<div class="flex gap-4">
|
||||
<TextField
|
||||
name="lat"
|
||||
label={$_("latitude")}
|
||||
bind:value={$form.lat}
|
||||
error={$errors.lat}
|
||||
on:change={handleChange}
|
||||
<TextField name="lat" label={$_("latitude")} error={$errors.lat}
|
||||
></TextField>
|
||||
<TextField
|
||||
name="lon"
|
||||
label={$_("longitude")}
|
||||
bind:value={$form.lon}
|
||||
error={$errors.lat}
|
||||
on:change={handleChange}
|
||||
<TextField name="lon" label={$_("longitude")} error={$errors.lat}
|
||||
></TextField>
|
||||
</div>
|
||||
<div>
|
||||
@@ -125,10 +122,10 @@
|
||||
</label>
|
||||
<PhotoPicker
|
||||
id="waypoint-photo-input"
|
||||
parent={$form}
|
||||
parent={$data}
|
||||
on:exif={(e) => getCoordinatesFromPhoto(e.detail)}
|
||||
bind:photos={$form.photos}
|
||||
bind:photoFiles={$form._photos}
|
||||
bind:photos={$data.photos}
|
||||
bind:photoFiles={$data._photos}
|
||||
showThumbnailControls={false}
|
||||
showExifControls={true}
|
||||
></PhotoPicker>
|
||||
|
||||
Reference in New Issue
Block a user