input-pwa.svelte (3592B)
1 <script lang="ts"> 2 import { browser } from "$app/environment"; 3 import { idb_kv } from "$lib/utils/keyval"; 4 import { 5 fmt_cl, 6 type IInput, 7 parse_layer, 8 value_constrain, 9 } from "@radroots/apps-lib"; 10 import { handle_err } from "@radroots/utils"; 11 import { onMount } from "svelte"; 12 13 let { 14 basis, 15 el = $bindable(null), 16 value = $bindable(``), 17 }: { 18 basis: IInput<string>; 19 el?: HTMLInputElement | null; 20 value?: string; 21 } = $props(); 22 23 const id = $derived(basis?.id ? basis.id : null); 24 const layer = $derived( 25 typeof basis?.layer === `boolean` ? 0 : parse_layer(basis?.layer), 26 ); 27 const classes_layer = $derived( 28 typeof basis?.layer === `boolean` || typeof basis?.layer === `undefined` 29 ? `` 30 : `bg-ly${layer} text-ly${layer}-gl_d placeholder:text-ly${layer}-gl_pl caret-ly${layer}-gl`, 31 ); 32 33 const sync_from_idb = async (): Promise<void> => { 34 if (!browser || !id || !idb_kv) return; 35 try { 36 const kv_val = await idb_kv.get<string | undefined>(id); 37 if (kv_val !== null && kv_val !== undefined && kv_val !== value) { 38 value = kv_val; 39 } else if (kv_val === null || kv_val === undefined) { 40 value = ``; 41 await idb_kv.set(id, ``); 42 } 43 } catch (e) { 44 handle_err(e, `sync_from_idb`); 45 } 46 }; 47 48 const sync_to_idb = async (): Promise<void> => { 49 if (!browser || !id || !idb_kv) return; 50 try { 51 await idb_kv.set(id, value || ``); 52 } catch (e) { 53 handle_err(e, `input_idb_sync`); 54 } 55 }; 56 57 onMount(async () => { 58 await sync_from_idb(); 59 if (basis?.callback_mount && el) { 60 try { 61 await basis.callback_mount({ el }); 62 } catch (e) { 63 handle_err(e, `callback_mount`); 64 } 65 } 66 }); 67 68 $effect(() => { 69 if (id && basis?.sync && browser) { 70 (async () => { 71 await sync_to_idb(); 72 })(); 73 } 74 }); 75 76 const handle_on_input = async (): Promise<void> => { 77 try { 78 let val_cur = value; 79 let pass = true; 80 if (basis?.field) { 81 val_cur = value_constrain(basis.field?.charset, val_cur); 82 if (val_cur !== value) { 83 value = val_cur; 84 } 85 pass = basis.field?.validate.test(val_cur); 86 } 87 if (basis?.callback) { 88 await basis.callback({ value: val_cur, pass }); 89 } 90 } catch (e) { 91 handle_err(e, `handle_on_input`); 92 } 93 }; 94 </script> 95 96 <input 97 bind:this={el} 98 bind:value 99 disabled={!!basis.disabled} 100 oninput={handle_on_input} 101 onblur={async ({ currentTarget: el }) => { 102 if (basis.callback_blur) await basis.callback_blur({ el }); 103 }} 104 onfocus={async ({ currentTarget: el }) => { 105 if (id && basis.sync && browser) await sync_from_idb(); 106 if (basis.callback_focus) await basis.callback_focus({ el }); 107 }} 108 onkeydown={async (ev) => { 109 if (basis?.callback_keydown) 110 await basis.callback_keydown({ 111 key: ev.key, 112 key_s: ev.key === `Enter`, 113 el: ev.currentTarget, 114 }); 115 }} 116 {id} 117 type="text" 118 class={`${fmt_cl(basis?.classes)} el-input ${classes_layer} el-re`} 119 placeholder={basis?.placeholder || ``} 120 />