web_lib

Common web application libraries
git clone https://radroots.dev/git/web_lib.git
Log | Files | Refs | LICENSE

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:
Mapps-lib/src/lib/components/form/entry-line-idb.svelte | 4++--
Aapps-lib/src/lib/components/lib/input-idb.svelte | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapps-lib/src/lib/components/lib/input-value.svelte | 34+++++++++++-----------------------
Mapps-lib/src/lib/components/lib/input.svelte | 108+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mapps-lib/src/lib/index.ts | 1+
Mapps-lib/src/lib/types/component.ts | 2+-
Mapps-lib/src/lib/types/interface.ts | 11++++++++++-
Mapps-lib/src/lib/types/lib.ts | 3++-
Mapps-lib/src/lib/util/lib.ts | 4++--
Mapps-lib/src/lib/util/service/nostr-sync.ts | 4++--
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 = {