web_lib

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

commit a990c14294ebfb6672f6d8b48a5e8f97854d9e58
parent febf3469932e19d3898042beea887a23b4e914dd
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Sun, 27 Oct 2024 18:50:11 +0000

apps-lib: edit entry select component add select element and update basis type. edit layout trellis line add conditional glyph to notify. edit input element add callback blur and edit styles. edit select element add handlers and kv sync. add number utils. add/edit locales, types, utils

Diffstat:
Mapps-lib/src/lib/components/entry_select.svelte | 46++++------------------------------------------
Mapps-lib/src/lib/components/layout_trellis_line.svelte | 26+++++++++++++++++++-------
Mapps-lib/src/lib/el/input_element.svelte | 5++++-
Dapps-lib/src/lib/el/select_el.svelte | 52----------------------------------------------------
Aapps-lib/src/lib/el/select_element.svelte | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapps-lib/src/lib/index.ts | 3++-
Mapps-lib/src/lib/locales/en/common.json | 1+
Mapps-lib/src/lib/locales/en/icu.json | 4+++-
Mapps-lib/src/lib/locales/en/model.json | 2+-
Mapps-lib/src/lib/types/components.ts | 14++++++++++----
Mapps-lib/src/lib/types/el.ts | 4++++
Mapps-lib/src/lib/utils/client.ts | 10+++++++---
Aapps-lib/src/lib/utils/numbers.ts | 14++++++++++++++
13 files changed, 158 insertions(+), 112 deletions(-)

diff --git a/apps-lib/src/lib/components/entry_select.svelte b/apps-lib/src/lib/components/entry_select.svelte @@ -1,18 +1,8 @@ -<!-- svelte-ignore a11y-no-noninteractive-tabindex --> <script lang="ts"> - import { - fmt_cl, - Glyph, - type IEntrySelect, - kv, - Loading, - parse_layer, - } from "$lib"; - import { onMount } from "svelte"; + import { Glyph, type IEntrySelect, Loading, parse_layer } from "$lib"; + import SelectElement from "$lib/el/select_element.svelte"; import EntryWrap from "./entry_wrap.svelte"; - let el: HTMLSelectElement | null; - export let value: string; export let basis: IEntrySelect; $: basis = basis; @@ -21,13 +11,6 @@ typeof basis?.wrap.layer === `boolean` ? parse_layer(0) : parse_layer(basis?.wrap.layer, 1); - - onMount(async () => { - try { - if (basis?.el.sync && basis?.el.id) - await kv.set(basis?.el.id, basis?.el.options[0].value); - } catch (e) {} - }); </script> <EntryWrap basis={basis?.wrap}> @@ -36,30 +19,9 @@ <Loading basis={{ dim: `sm`, blades: 8 }} /> </div> {:else} - <select - bind:this={el} - bind:value - id={basis?.el.id || null} - class={`${fmt_cl(basis?.el.classes)} z-10 el-select entry-line-fluid text-layer-${layer}-glyph`} - on:change={async ({ currentTarget: el }) => { - const val = el.value; - if (basis?.el.sync && basis?.el.id) - await kv.set(basis?.el.id, val); - if (basis?.el.callback) await basis?.el.callback(val); - }} - > - {#each basis?.el.options as opt} - <option - value={opt.value} - disabled={!!opt.disabled} - selected={!!opt.selected} - > - {opt.label || opt.value} - </option> - {/each} - </select> + <SelectElement bind:value basis={basis.el} /> {/if} - {#if !basis?.el.hide_arrows} + {#if !basis?.hide_arrows} <div class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-4 justify-end items-center`} > diff --git a/apps-lib/src/lib/components/layout_trellis_line.svelte b/apps-lib/src/lib/components/layout_trellis_line.svelte @@ -3,22 +3,28 @@ fmt_cl, type ICb, type IClOpt, - type ILabel, + type IGlOpt, type ILabelOpt, } from "$lib"; + import Glyph from "$lib/el/glyph.svelte"; import { fade } from "svelte/transition"; export let basis: | (ILabelOpt & IClOpt & { - notify?: IClOpt & ICb & ILabel; + notify?: IClOpt & + ICb & + ILabelOpt & + IGlOpt & { + glyph_last?: boolean; + }; }) | undefined = undefined; $: basis = basis; </script> <div - class={`${fmt_cl(basis?.classes)} flex flex-col w-trellis_line gap-1 justify-start items-center`} + class={`${fmt_cl(basis?.classes)} flex flex-col w-trellis_line gap-[6px] justify-start items-center`} > {#if (basis?.label && `value` in basis?.label) || basis?.notify} <div @@ -35,21 +41,27 @@ <div in:fade={{ duration: 200 }} out:fade={{ delay: 50, duration: 200 }} - class={`${fmt_cl(basis?.notify.classes)} flex flex-row justify-start items-center transition-all`} + class={`${fmt_cl(basis?.notify.classes)} flex flex-row justify-start items-center el-re`} > <button - class={`flex flex-row justify-center items-center`} + class={`flex flex-row justify-center items-center text-layer-2-glyph/80`} on:click={async () => { await basis?.notify?.callback(); }} > - {#if `value` in basis?.notify.label} + {#if `glyph` in basis?.notify && basis?.notify?.glyph && !basis.notify.glyph_last} + <Glyph basis={basis.notify.glyph} /> + {/if} + {#if `label` in basis?.notify && basis?.notify?.label && `value` in basis?.notify?.label} <p - class={`${fmt_cl(basis?.notify.label.classes)} font-sans font-[500] uppercase text-layer-2-glyph/80 text-xs`} + class={`${fmt_cl(basis?.notify.label.classes)} font-sans font-[500] uppercase text-xs`} > {basis?.notify.label.value} </p> {/if} + {#if `glyph` in basis?.notify && basis?.notify?.glyph && basis.notify.glyph_last} + <Glyph basis={basis.notify.glyph} /> + {/if} </button> </div> {/if} diff --git a/apps-lib/src/lib/el/input_element.svelte b/apps-lib/src/lib/el/input_element.svelte @@ -74,11 +74,14 @@ bind:this={el} {id} type="text" - class={`${fmt_cl(basis?.classes)} el-input text-layer-${layer}-glyph placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`} + class={`${fmt_cl(basis?.classes)} el-input text-layer-${layer}-glyph placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph el-re`} placeholder={basis?.placeholder || ""} on:input={async ({ currentTarget: el }) => { await handle_on_input(el); }} + on:blur={async ({ currentTarget: el }) => { + if (basis.callback_blur) await basis.callback_blur({ el }); + }} on:keydown={async (ev) => { if (basis?.callback_keydown) await basis?.callback_keydown({ diff --git a/apps-lib/src/lib/el/select_el.svelte b/apps-lib/src/lib/el/select_el.svelte @@ -1,52 +0,0 @@ -<script lang="ts"> - import { fmt_cl, parse_layer, type ISelectElement } from "$lib"; - - export let value: string; - export let basis: ISelectElement; - $: basis = basis; - - let el_wrap: HTMLDivElement | null = null; - let el_select: HTMLSelectElement | null = null; - $: layer = - typeof basis?.layer === `boolean` - ? parse_layer(0) - : parse_layer(basis.layer, 0); - $: classes_layer = - typeof basis?.layer === `boolean` ? `` : `text-layer-${layer}-glyph`; -</script> - -<select - class={`${fmt_cl(basis.classes)} z-10 el-select ${classes_layer}`} - bind:this={el_select} - bind:value - on:change={async (e) => { - const opt = basis.options - .map((i) => i.entries) - .reduce((_, j) => j, []) - .find((k) => k.value === e.currentTarget?.value); - if (basis.callback && opt) await basis.callback(opt); - if (el_select) el_select.value = value; - }} -> - {#each basis.options as opt_g} - {#if opt_g.group} - <optgroup> - {#each opt_g.entries as opt} - <option - label={opt_g.group === true - ? `-`.repeat(21) - : opt_g.group || ``} - > - {opt.label} - </option> - {/each} - </optgroup> - {:else} - {#each opt_g.entries as opt} - <option value={opt.value} disabled={!!opt.disabled}> - {opt.label} - </option> - {/each} - {/if} - {/each} -</select> diff --git a/apps-lib/src/lib/el/select_element.svelte b/apps-lib/src/lib/el/select_element.svelte @@ -0,0 +1,89 @@ +<script lang="ts"> + import { fmt_cl, kv, parse_layer, type ISelectElement } from "$lib"; + import { onMount } from "svelte"; + + export let value: string; + export let basis: ISelectElement; + $: basis = basis; + + let el: HTMLSelectElement | null = null; + $: layer = + typeof basis?.layer === `boolean` + ? parse_layer(0) + : parse_layer(basis.layer, 0); + $: classes_layer = + typeof basis?.layer === `boolean` ? `` : `text-layer-${layer}-glyph`; + + onMount(async () => { + try { + await kv_init(); + } catch (e) { + console.log(`e select mount`, e); + } + }); + const kv_init = async (): Promise<void> => { + try { + if (basis?.id) { + if (basis?.sync_init) + await kv.set( + basis?.id, + typeof basis?.sync_init === `string` + ? basis?.sync_init + : ``, + ); + if (basis?.sync) { + const kv_val = await kv.get(basis?.id); + if (kv_val && el) el.value = kv_val; + else await kv.set(basis?.id, ``); + } + } + } catch (e) { + console.log(`(error) kv_init `, e); + } + }; + + const handle_on_change = async (el: HTMLSelectElement): Promise<void> => { + try { + const opt = basis.options + .map((i) => i.entries) + .reduce((_, j) => j, []) + .find((k) => k.value === el?.value); + if (el) el.value = value; + if (basis?.sync) await kv.set(basis?.id, value); + if (basis.callback && opt) await basis.callback(opt); + } catch (e) { + console.log(`(error) handle_on_change `, e); + } + }; +</script> + +<select + class={`${fmt_cl(basis.classes)} z-10 el-select ${classes_layer}`} + bind:this={el} + bind:value + on:change={async ({ currentTarget: el }) => { + handle_on_change(el); + }} +> + {#each basis.options as opt_g} + {#if opt_g.group} + <optgroup> + {#each opt_g.entries as opt} + <option + label={opt_g.group === true + ? `-`.repeat(21) + : opt_g.group || ``} + > + {opt.label} + </option> + {/each} + </optgroup> + {:else} + {#each opt_g.entries as opt} + <option value={opt.value} disabled={!!opt.disabled}> + {opt.label} + </option> + {/each} + {/if} + {/each} +</select> diff --git a/apps-lib/src/lib/index.ts b/apps-lib/src/lib/index.ts @@ -6,6 +6,7 @@ export * from "./types/conf"; export * from "./types/el"; export * from "./types/nostr"; export { default as Blur } from "./el/blur.svelte"; +export { default as SelectElement } from "./el/select_element.svelte"; export { default as Toast } from "./el/toast.svelte"; export { default as GlyphCircle } from "./el/glyph_circle.svelte"; export { default as TextareaElement } from "./el/textarea_element.svelte"; @@ -19,13 +20,13 @@ export { default as Loading } from "./el/loading.svelte"; export { default as InputElement } from "./el/input_element.svelte"; export { default as Fill } from "./el/fill.svelte"; export { default as FillWhite } from "./el/fill_white.svelte"; -export { default as SelectEl } from "./el/select_el.svelte"; export * from "./stores/ndk"; export * from "./stores/client"; export * from "./utils/routes"; export * from "./utils/dom"; export * from "./utils/styles"; export * from "./utils/carousel"; +export * from "./utils/numbers"; export * from "./utils/client"; export * from "./utils/conf"; export * from "./utils/time"; diff --git a/apps-lib/src/lib/locales/en/common.json b/apps-lib/src/lib/locales/en/common.json @@ -87,6 +87,7 @@ "publish": "Publish", "quantity": "Quantity", "quit": "Quit", + "reading": "Reading", "relay": "Relay", "relays": "Relays", "reset": "Reset", diff --git a/apps-lib/src/lib/locales/en/icu.json b/apps-lib/src/lib/locales/en/icu.json @@ -9,6 +9,7 @@ "*_month": "{value} month", "*_name": "{value} name", "*_summary": "{value} summary", + "*_the": "{value} the", "*_title": "{value} title", "*_your_device": "{value} your device", "add_*": "Add {value}", @@ -28,9 +29,10 @@ "enter_*": "Enter {value}", "enter_a_*": "Enter a {value}", "enter_new_*": "Enter new {value}", - "enter_number_of_*_per_order": "Enter number of {value} per order", + "enter_*_per_order": "Enter {value} per order", "enter_the_*": "Enter the {value}", "error_loading_*": "Error loading {value}", + "failure_*": "Failure {value}", "failure_saving_*_to_the_database": "Failure saving {value} to the database", "go_*": "Go {value}", "invalid_*": "Invalid {value}", diff --git a/apps-lib/src/lib/locales/en/model.json b/apps-lib/src/lib/locales/en/model.json @@ -49,7 +49,7 @@ "qty_avail": "Quantity available", "qty_label": "Quantity label", "qty_unit": "Quantity unit", - "summary": "Listing summary", + "summary": "Product description", "title": "Listing title", "year": "Year" } diff --git a/apps-lib/src/lib/types/components.ts b/apps-lib/src/lib/types/components.ts @@ -1,4 +1,5 @@ -import type { CallbackPromise, CallbackPromiseGeneric, GlyphKey, GlyphWeight, ICb, ICbG, ICbGOpt, ICbOpt, IClOpt, IGl, IGlOpt, IGlyph, IIdOpt, IIdWrapOpt, IInputElement, ILabel, ILabelFieldsOpt, ILabelOpt, ILabelOptFieldsOpt, ILabelValue, ILoadingOpt, ILyOpt, ILyOptTs, ITextAreaElement, NavigationParamTuple, NavigationRoute } from "$lib"; +import type { CallbackPromise, CallbackPromiseGeneric, GlyphKey, GlyphWeight, ICb, ICbG, ICbOpt, IClOpt, IGl, IGlOpt, IGlyph, IIdOpt, IIdWrapOpt, IInputElement, ILabel, ILabelFieldsOpt, ILabelOpt, ILabelOptFieldsOpt, ILabelValue, ILoadingOpt, ILyOpt, ILyOptTs, ISelectElement, ITextAreaElement, NavigationParamTuple, NavigationRoute } from "$lib"; +import type { TransitionConfig } from "svelte/transition"; export type ITabsBasisList = IClOpt & { icon: GlyphKey; @@ -26,6 +27,10 @@ export type IEntryStyle = `guide` | `line`; export type IEntryWrap = IClOpt & IIdOpt & ILyOptTs & { style?: IEntryStyle; style_a?: true; + fade?: { + in?: TransitionConfig; + out?: TransitionConfig; + }; } export type IEntryLine = ILoadingOpt & { @@ -55,13 +60,14 @@ export type IEntrySelectOption = { export type IEntrySelect = ILoadingOpt & { wrap: IEntryWrap; - el: IIdOpt & IClOpt & ICbGOpt<string> & { + el: ISelectElement; + hide_arrows?: boolean; + /*IIdOpt & IClOpt & ICbGOpt<string> & { label?: string; hidden?: boolean; - hide_arrows?: boolean; sync?: boolean; options: IEntrySelectOption[]; - } + }*/ }; diff --git a/apps-lib/src/lib/types/el.ts b/apps-lib/src/lib/types/el.ts @@ -4,6 +4,7 @@ import type { ThemeLayer } from "@radroots/theme"; export type GlyphKeyCurrency = `dollar` | `eur`; export type GlyphKey = | + `image-square` | `image-broken` | `funnel` | `users-three` | @@ -142,6 +143,8 @@ export type ISelectOption<T extends string> = { export type ISelectElement = IIdOpt & IClOpt & ILyOptTs & ICbGOpt<ISelectOption<string>> & { + sync?: true; + sync_init?: true | string; options: { group?: string | true; entries: ISelectOption<string>[] }[]; }; @@ -158,6 +161,7 @@ export type IInputElement = IId & IClOpt & ILyOptTs & { };*/ callback?: CallbackPromiseGeneric<{ value: string; pass: boolean; }>; callback_keydown?: CallbackPromiseGeneric<{ key: string; el: HTMLInputElement }>; + callback_blur?: CallbackPromiseGeneric<{ el: HTMLInputElement }>; on_mount?: CallbackPromiseGeneric<HTMLInputElement>; }; diff --git a/apps-lib/src/lib/utils/client.ts b/apps-lib/src/lib/utils/client.ts @@ -177,9 +177,13 @@ export const value_constrain = (regex_charset: RegExp, value: string): string => export const value_constrain_textarea = (regex_charset: RegExp, value: string): string => { return value .replace(/\u00A0/g, ` `) - .split("") - .filter((char) => regex_charset.test(char)) - .join("") + .split(/[\n]/) + .map(line => line + .split("") + .filter((char) => regex_charset.test(char)) + .join("") + ) + .join("\n") .replace(/ /g, `\u00A0`); }; diff --git a/apps-lib/src/lib/utils/numbers.ts b/apps-lib/src/lib/utils/numbers.ts @@ -0,0 +1,13 @@ +import { locale } from "$lib/locales/i18n"; +import { get as get_store } from "svelte/store"; + +export const price_locale_fmt = (currency: string, value: string): string => { + const value_num = parseFloat(value); + const res = new Intl.NumberFormat(get_store(locale), { + style: 'currency', + currency, + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }).format(value_num); + return res; +}; +\ No newline at end of file