commit 9014496d467d9e3f6a2b22ec5b1e459c7efd68ee
parent 3894e0fb26f6bac138c4c6a674ca9975e5583685
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Wed, 9 Apr 2025 01:46:49 +0000
apps-lib: refactor input components, add/edit components, types, utils
Diffstat:
10 files changed, 210 insertions(+), 81 deletions(-)
diff --git a/apps-lib/src/lib/components/form/entry-line-idb.svelte b/apps-lib/src/lib/components/form/entry-line-idb.svelte
@@ -6,7 +6,7 @@
LoadSymbol,
type IEntryLineIdb,
} from "$root";
- import { parse_layer, type LoadingDimension } from "@radroots/util";
+ import { fmt_cl, parse_layer, type LoadingDimension } from "@radroots/util";
let {
basis,
@@ -35,7 +35,7 @@
<Input
basis={{
...basis.el,
- classes: `h-entry_line ${basis.el.classes}`,
+ classes: `h-entry_line ${fmt_cl(basis.el.classes)}`,
}}
/>
{#if basis.loading}
diff --git a/apps-lib/src/lib/components/lib/input-idb.svelte b/apps-lib/src/lib/components/lib/input-idb.svelte
@@ -0,0 +1,120 @@
+<script lang="ts">
+ import { browser } from "$app/environment";
+ import { handle_err, idb, type IInput } from "$root";
+ import { fmt_cl, parse_layer, value_constrain } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let {
+ basis,
+ el = $bindable(null),
+ value = $bindable(``),
+ }: {
+ basis: IInput<string>;
+ el?: HTMLInputElement | null;
+ value?: string;
+ } = $props();
+
+ const id = $derived(basis?.id ? basis.id : null);
+ const layer = $derived(
+ typeof basis?.layer === `boolean` ? 0 : parse_layer(basis?.layer),
+ );
+ const classes_layer = $derived(
+ typeof basis?.layer === `boolean` || typeof basis?.layer === `undefined`
+ ? ``
+ : `bg-layer-${layer}-surface text-layer-${layer}-glyph_d placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`,
+ );
+
+ let value_local = $state(value);
+
+ const sync_from_idb = async (): Promise<void> => {
+ if (!browser || !idb || !id) return;
+ try {
+ const kv_val = await idb.get(id);
+ if (
+ kv_val !== null &&
+ kv_val !== undefined &&
+ kv_val !== value_local
+ ) {
+ value_local = kv_val;
+ } else if (kv_val === null || kv_val === undefined) {
+ value_local = ``;
+ await idb.set(id, ``);
+ }
+ } catch (e) {
+ handle_err(e, `sync_from_idb`);
+ }
+ };
+
+ const sync_to_idb = async (): Promise<void> => {
+ if (!browser || !idb || !id) return;
+ try {
+ await idb.set(id, value_local || ``);
+ } catch (e) {
+ handle_err(e, `input_idb_sync`);
+ }
+ };
+
+ onMount(async () => {
+ await sync_from_idb();
+ if (basis?.callback_mount && el) {
+ try {
+ await basis.callback_mount({ el });
+ } catch (e) {
+ handle_err(e, `callback_mount`);
+ }
+ }
+ });
+
+ $effect(() => {
+ if (id && basis?.sync && browser && idb) {
+ (async () => {
+ await sync_to_idb();
+ })();
+ }
+ });
+
+ const handle_on_input = async (): Promise<void> => {
+ try {
+ let updatedVal = value_local;
+ let pass = true;
+ if (basis?.field) {
+ updatedVal = value_constrain(basis.field.charset, updatedVal);
+ if (updatedVal !== value_local) {
+ value_local = updatedVal;
+ }
+ pass = basis.field.validate.test(updatedVal);
+ }
+ if (basis?.callback) {
+ await basis.callback({ value: updatedVal, pass });
+ }
+ } catch (e) {
+ handle_err(e, `handle_on_input`);
+ }
+ };
+</script>
+
+<input
+ bind:this={el}
+ bind:value={value_local}
+ disabled={!!basis.disabled}
+ oninput={async () => await handle_on_input()}
+ onblur={async ({ currentTarget: el }) => {
+ if (basis.callback_blur) await basis.callback_blur({ el });
+ }}
+ onfocus={async ({ currentTarget: el }) => {
+ if (id && basis.sync && browser && idb) await sync_from_idb();
+ if (basis.callback_focus) await basis.callback_focus({ el });
+ }}
+ onkeydown={async (ev) => {
+ if (basis?.callback_keydown)
+ await basis.callback_keydown({
+ key: ev.key,
+ key_s: ev.key === `Enter`,
+ el: ev.currentTarget,
+ });
+ }}
+ {id}
+ type="text"
+ class={`${fmt_cl(basis?.classes)} el-input ${classes_layer} el-re`}
+ placeholder={basis?.placeholder || ``}
+/>
diff --git a/apps-lib/src/lib/components/lib/input-value.svelte b/apps-lib/src/lib/components/lib/input-value.svelte
@@ -9,7 +9,7 @@
}: {
basis: IInputValue<string>;
el?: HTMLInputElement | null;
- value?: string;
+ value: string;
} = $props();
const layer = $derived(
@@ -24,39 +24,27 @@
: `bg-layer-${layer}-surface text-layer-${layer}-glyph placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`,
);
- const handle_on_input = async (el: HTMLInputElement): Promise<void> => {
+ const handle_on_input = async (): Promise<void> => {
try {
+ let val_cur = value;
let pass = true;
- let val = el?.value;
- if (basis?.field && el) {
- val = value_constrain(basis?.field.charset, val);
- el.value = val;
- if (
- !basis?.field.validate.test(val) &&
- basis?.field_constrain
- ) {
- //@todo set styles
- }
- pass = basis?.field.validate.test(val);
+ if (basis?.field) {
+ val_cur = value_constrain(basis.field.charset, val_cur);
+ if (val_cur !== value) value = val_cur;
+ pass = basis.field.validate.test(val_cur);
}
- if (basis?.callback) await basis?.callback({ value: val, pass });
+ if (basis?.callback) await basis.callback({ value: val_cur, pass });
} catch (e) {
- console.log(`(error) handle_on_input `, e);
+ console.error(`(error) handle_on_input`, e);
}
};
-
- $effect(() => {
- console.log(`value `, value);
- });
</script>
<input
bind:this={el}
bind:value
disabled={!!basis.disabled}
- oninput={async ({ currentTarget: el }) => {
- await handle_on_input(el);
- }}
+ oninput={handle_on_input}
onblur={async ({ currentTarget: el }) => {
if (basis.callback_blur) await basis.callback_blur({ el });
}}
@@ -65,7 +53,7 @@
}}
onkeydown={async (ev) => {
if (basis?.callback_keydown)
- await basis?.callback_keydown({
+ await basis.callback_keydown({
key: ev.key,
key_s: ev.key === `Enter`,
el: ev.currentTarget,
diff --git a/apps-lib/src/lib/components/lib/input.svelte b/apps-lib/src/lib/components/lib/input.svelte
@@ -1,6 +1,6 @@
<script lang="ts">
import { browser } from "$app/environment";
- import { idb, type IInput } from "$root";
+ import { handle_err, idb, type IInput } from "$root";
import { fmt_cl, parse_layer, value_constrain } from "@radroots/util";
import { onMount } from "svelte";
@@ -14,90 +14,100 @@
value?: string;
} = $props();
- onMount(async () => {
- try {
- await kv_init();
- } catch (e) {
- } finally {
- }
- });
-
const id = $derived(basis?.id ? basis.id : null);
-
const layer = $derived(
- typeof basis?.layer === `boolean`
- ? parse_layer(0)
- : parse_layer(basis?.layer),
+ typeof basis?.layer === `boolean` ? 0 : parse_layer(basis?.layer),
);
-
const classes_layer = $derived(
typeof basis?.layer === `boolean` || typeof basis?.layer === `undefined`
? ``
: `bg-layer-${layer}-surface text-layer-${layer}-glyph_d placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`,
);
- $effect(() => {
- if (id && basis?.sync) {
- (async () => {
- if (browser) await idb.set(id, value || ``);
- })();
- }
- });
+ let value_local = $state(value);
- const kv_init = async (): Promise<void> => {
+ const sync_from_idb = async (): Promise<void> => {
+ if (!browser || !idb || !id) return;
try {
- if (!id) return;
- if (basis?.sync && browser) {
- const kv_val = await idb.get(id);
- if (kv_val && el) el.value = kv_val;
- else await idb.set(id, ``);
+ const kv_val = await idb.get(id);
+ if (
+ kv_val !== null &&
+ kv_val !== undefined &&
+ kv_val !== value_local
+ ) {
+ value_local = kv_val;
+ } else if (kv_val === null || kv_val === undefined) {
+ value_local = ``;
+ await idb.set(id, ``);
}
- if (basis?.callback_mount && el)
- await basis?.callback_mount({ el });
} catch (e) {
- console.log(`(error) kv_init `, e);
+ handle_err(e, `sync_from_idb`);
}
};
- const handle_on_input = async (el: HTMLInputElement): Promise<void> => {
+ const sync_to_idb = async (): Promise<void> => {
+ if (!browser || !idb || !id) return;
try {
+ await idb.set(id, value_local || ``);
+ } catch (e) {
+ handle_err(e, `input_idb_sync`);
+ }
+ };
+
+ onMount(async () => {
+ await sync_from_idb();
+ if (basis?.callback_mount && el) {
+ try {
+ await basis.callback_mount({ el });
+ } catch (e) {
+ handle_err(e, `callback_mount`);
+ }
+ }
+ });
+
+ $effect(() => {
+ if (id && basis?.sync && browser && idb) {
+ (async () => {
+ await sync_to_idb();
+ })();
+ }
+ });
+
+ const handle_on_input = async (): Promise<void> => {
+ try {
+ let val_cur = value_local;
let pass = true;
- let val = el?.value;
- if (basis?.field && el) {
- val = value_constrain(basis?.field.charset, val);
- el.value = val;
- if (
- !basis?.field.validate.test(val) &&
- basis?.field_constrain
- ) {
- //@todo set styles
+ if (basis?.field) {
+ val_cur = value_constrain(basis.field.charset, val_cur);
+ if (val_cur !== value_local) {
+ value_local = val_cur;
}
- pass = basis?.field.validate.test(val);
+ pass = basis.field.validate.test(val_cur);
+ }
+ if (basis?.callback) {
+ await basis.callback({ value: val_cur, pass });
}
- if (basis?.sync && id && browser) await idb.set(id, val);
- if (basis?.callback) await basis?.callback({ value: val, pass });
} catch (e) {
- console.log(`(error) handle_on_input `, e);
+ handle_err(e, `handle_on_input`);
}
};
</script>
<input
bind:this={el}
- bind:value
+ bind:value={value_local}
disabled={!!basis.disabled}
- oninput={async ({ currentTarget: el }) => {
- await handle_on_input(el);
- }}
+ oninput={handle_on_input}
onblur={async ({ currentTarget: el }) => {
if (basis.callback_blur) await basis.callback_blur({ el });
}}
onfocus={async ({ currentTarget: el }) => {
+ if (id && basis.sync && browser && idb) await sync_from_idb();
if (basis.callback_focus) await basis.callback_focus({ el });
}}
onkeydown={async (ev) => {
if (basis?.callback_keydown)
- await basis?.callback_keydown({
+ await basis.callback_keydown({
key: ev.key,
key_s: ev.key === `Enter`,
el: ev.currentTarget,
diff --git a/apps-lib/src/lib/index.ts b/apps-lib/src/lib/index.ts
@@ -47,6 +47,7 @@ export { default as Empty } from "./components/lib/empty.svelte"
export { default as Fade } from "./components/lib/fade.svelte"
export { default as ImageBlob } from "./components/lib/image-blob.svelte"
export { default as ImagePath } from "./components/lib/image-path.svelte"
+export { default as InputIdb } from "./components/lib/input-idb.svelte"
export { default as InputValue } from "./components/lib/input-value.svelte"
export { default as Input } from "./components/lib/input.svelte"
export { default as LabelSwap } from "./components/lib/label-swap.svelte"
diff --git a/apps-lib/src/lib/types/component.ts b/apps-lib/src/lib/types/component.ts
@@ -35,7 +35,7 @@ export type IInput<T extends string> = IIdGOpt<T> & IClOpt & ILyOpt & IDisabledO
callback_mount?: ElementCallbackMount<HTMLInputElement>;
};
-export type IInputValue<T extends string> = Omit<IInput<T>, `id` | `sync`>;
+export type IInputValue<T extends string> = Omit<IInput<T>, `sync`>;
export type ISelectOption<T extends string> = IDisabledOpt & {
value: T;
diff --git a/apps-lib/src/lib/types/interface.ts b/apps-lib/src/lib/types/interface.ts
@@ -1,4 +1,4 @@
-import type { IGlyph, IInput, IInputValue, ITextArea } from "$root";
+import type { IGlyph, IInput, IInputValue, ISelect, ITextArea } from "$root";
import type { CallbackPromise, CallbackPromiseGeneric, EntryStyle, GlyphKey, LayerGlyphBasisKind, LoadingBlades, LoadingDimension, SvelteTransitionConfig, ThemeLayer } from "@radroots/util";
export type IDisabled = {
@@ -162,6 +162,15 @@ export type IEntryLineIdb = ILoadingOpt & {
};
};
+export type IEntryLineSelectIdb = ILoadingOpt & {
+ wrap?: IEntryWrap;
+ el_input: IInput<string>;
+ el_sel: ISelect;
+ /*notify_inline?: {
+ glyph: GlyphKey | IGlyph;
+ };*/
+};
+
export type IEntryMultiLine = {
wrap?: IEntryWrap;
el: ITextArea;
diff --git a/apps-lib/src/lib/types/lib.ts b/apps-lib/src/lib/types/lib.ts
@@ -10,7 +10,8 @@ export type NavigationRouteParamLat = `lat`;
export type NavigationRouteParamLng = `lng`;
export type NavigationRouteParamNostrPublicKey = `key_nostr`;
export type NavigationRouteParamKey = NavigationRouteParamId | NavigationRouteParamField | NavigationRouteParamRef | NavigationRouteParamLat | NavigationRouteParamLng | NavigationRouteParamNostrPublicKey;
-export type NavigationPreviousParam<T extends string> = { route: T, label?: string; params?: NavigationParamTuple<NavigationRouteParamKey>[] }
+export type NavigationRouteParamTuple = NavigationParamTuple<NavigationRouteParamKey>;
+export type NavigationPreviousParam<T extends string> = { route: T, label?: string; params?: NavigationRouteParamTuple[] }
export type LcGuiAlertCallback = CallbackPromiseFull<string, boolean>;
export type LcGuiConfirmCallback = CallbackPromiseFull<string | { message: string; ok?: string; cancel?: string }, boolean>;
diff --git a/apps-lib/src/lib/util/lib.ts b/apps-lib/src/lib/util/lib.ts
@@ -1,7 +1,7 @@
import { browser } from "$app/environment";
import { goto } from "$app/navigation";
import { page } from "$app/state";
-import { win_h, win_w, type CallbackRoute } from "$root";
+import { win_h, win_w, type CallbackRoute, type NavigationRouteParamKey } from "$root";
import type { ColorMode, ThemeKey } from "@radroots/theme";
import { encode_route, fmt_geometry_point_coords, fmt_price, parse_currency_marker, type GeometryPoint, type IErrorCatchCallback } from "@radroots/util";
import { get } from "svelte/store";
@@ -48,7 +48,7 @@ export const callback_route = async <T extends string>(callback_route: CallbackR
if (`route` in callback_route) {
if (typeof callback_route.route === `string`) return void await goto(callback_route.route);
else return void await goto(
- encode_route<string>(
+ encode_route<string, NavigationRouteParamKey>(
callback_route.route[0],
callback_route.route[1],
),
diff --git a/apps-lib/src/lib/util/service/nostr-sync.ts b/apps-lib/src/lib/util/service/nostr-sync.ts
@@ -1,11 +1,11 @@
import { get_store, handle_err, ndk, ndk_user } from "$root";
import type { NDKEvent, NDKUser } from "@nostr-dev-kit/ndk";
import type NDKSvelte from "@nostr-dev-kit/ndk-svelte";
-import { ndk_event_metadata, type NostrMetadata } from "@radroots/nostr-util";
+import { ndk_event_metadata, type INostrMetadata } from "@radroots/nostr-util";
import { err_msg, type ErrorMessage } from "@radroots/util";
export type INostrSyncServiceMetadata = {
- metadata: NostrMetadata;
+ metadata: INostrMetadata;
};
export type INostrSyncService = {