web_lib

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

commit d5c9d86649f174c96f37f559fa24316915334a99
parent af35fb75a82f87f59f29f60d82e526e257696147
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Thu, 26 Sep 2024 20:25:13 +0000

apps-lib: edit nav component option features, edit app config component, add toast ui, edit locales, stores, types, utils

Diffstat:
Mapps-lib/src/lib/components/app_config.svelte | 48++++++++++++++++++++++++++++++++++++++++++++++--
Mapps-lib/src/lib/components/nav.svelte | 53+++++++++++------------------------------------------
Aapps-lib/src/lib/components/nav_option.svelte | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapps-lib/src/lib/index.ts | 7+++++--
Mapps-lib/src/lib/locales/en/common.json | 7+++++++
Mapps-lib/src/lib/locales/en/icu.json | 2++
Mapps-lib/src/lib/stores/client.ts | 3++-
Mapps-lib/src/lib/types/client.ts | 9+++++++++
Mapps-lib/src/lib/types/components.ts | 11+++++++----
Mapps-lib/src/lib/types/ui.ts | 2+-
Aapps-lib/src/lib/ui/toast.svelte | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapps-lib/src/lib/utils/client.ts | 21+++++++++++++++------
Mapps-lib/src/lib/utils/routes.ts | 2++
13 files changed, 241 insertions(+), 58 deletions(-)

diff --git a/apps-lib/src/lib/components/app_config.svelte b/apps-lib/src/lib/components/app_config.svelte @@ -1,12 +1,52 @@ +<script lang="ts" context="module"> + const toast_ms = 1500; + + export const init_toast = (): void => { + app_toast.set(false); + }; + + export const show_toast = async (opts: { + args: IToast | string; + callback?: CallbackPromise; + }): Promise<void> => { + try { + const basis: IToast = + typeof opts.args === `string` + ? { + layer: 1, + label: { + value: opts.args, + }, + } + : opts.args; + app_toast.set(basis); + await sleep(toast_ms); + init_toast(); + if (opts.callback) await opts.callback(); + } catch (e) { + console.log(`(error) show_toast `, e); + } + }; +</script> + <script lang="ts"> - import { app_config, app_layout, app_win } from "$lib"; + import { + app_config, + app_layout, + app_toast, + app_win, + sleep, + type CallbackPromise, + type IToast, + } from "$lib"; + import Toast from "$lib/ui/toast.svelte"; import { onMount } from "svelte"; onMount(async () => { try { app_win.set([window.innerHeight, window.innerWidth]); - app_config.set(true); + app_toast.set(false); } catch (e) { console.log(`(layout mount) `, e); } finally { @@ -17,3 +57,7 @@ if (win_h > 800) app_layout.set("lg"); }); </script> + +{#if $app_toast} + <Toast basis={$app_toast} /> +{/if} diff --git a/apps-lib/src/lib/components/nav.svelte b/apps-lib/src/lib/components/nav.svelte @@ -11,8 +11,8 @@ nav_visible, type INavBasis, } from "$lib"; - import Loading from "$lib/ui/loading.svelte"; import { onDestroy, onMount } from "svelte"; + import NavOption from "./nav_option.svelte"; export let basis: INavBasis; $: basis = basis; @@ -20,9 +20,12 @@ let el: HTMLElement | null; let el_inner: HTMLElement | null; + let nav_prev_label = ``; + onMount(async () => { try { nav_visible.set(true); + if ($nav_prev.length) nav_prev_label = $nav_prev[0].label || ``; } catch (e) { } finally { } @@ -52,7 +55,6 @@ class={`group col-span-4 flex flex-row h-full pl-2 justify-start items-center`} on:click={async () => { if (basis.prev.callback) await basis.prev.callback(); - console.log(`basis.prev.route `, basis.prev.route); let route_to = typeof basis.prev.route === `string` ? basis.prev.route @@ -60,18 +62,14 @@ basis.prev.route[0], basis.prev.route[1], ); - console.log(`route_to `, route_to); if ($nav_prev.length) { - const nav_prev_param = $nav_prev.pop(); - console.log(`nav_prev_param `, nav_prev_param); - if (nav_prev_param) + const nav_prev_li = $nav_prev.pop(); + if (nav_prev_li) route_to = encode_qp_route( - nav_prev_param.route, - nav_prev_param.params, + nav_prev_li.route, + nav_prev_li.params, ); } - - console.log(`route_to `, route_to); await goto(route_to); }} > @@ -83,11 +81,11 @@ classes: `text-layer-1-glyph-hl group-active:opacity-70 transition-opacity`, }} /> - {#if basis.prev.label} + {#if nav_prev_label || basis.prev.label} <p class={`font-sans text-navPrevious text-layer-1-glyph-hl group-active:opacity-60 transition-opacity`} > - {basis.prev.label} + {nav_prev_label || basis.prev.label} </p> {:else} <Fill /> @@ -118,36 +116,7 @@ class={`col-span-4 flex flex-row h-full justify-end items-center`} > {#if basis.option} - {#if basis.option.loading} - <div - class={`flex flex-row pr-4 justify-center items-center`} - > - <Loading /> - </div> - {:else} - <button - class={`group col-span-4 flex flex-row h-full pr-6 gap-2 justify-end items-center`} - on:click={async () => { - await basis.option?.callback(); - }} - > - {#if `glyph` in basis.option && basis.option.glyph} - <Glyph - basis={{ - classes: `group-active:opacity-70 ${basis.option.glyph.classes}`, - ...basis.option.glyph, - }} - /> - {/if} - {#if `label` in basis.option && basis.option.label} - <p - class={`${fmt_cl(basis.option.label.classes)} font-sans text-navPrevious text-layer-1-glyph-hl group-active:opacity-60 transition-opacity`} - > - {basis.option.label.value} - </p> - {/if} - </button> - {/if} + <NavOption basis={basis.option} /> {:else} <Fill /> {/if} diff --git a/apps-lib/src/lib/components/nav_option.svelte b/apps-lib/src/lib/components/nav_option.svelte @@ -0,0 +1,63 @@ +<!-- svelte-ignore a11y-label-has-associated-control --> +<script lang="ts"> + import { fmt_cl, Glyph, Loading, type INavBasisOption } from "$lib"; + + let el_swap: HTMLLabelElement | null = null; + + export let basis: INavBasisOption; + $: basis = basis; + + $: classes_swap = ``; + + let layer = 1; +</script> + +{#if basis.loading} + <div class={`flex flex-row pr-4 justify-center items-center`}> + <Loading /> + </div> +{:else} + <button + class={`group col-span-4 flex flex-row h-full pr-6 gap-2 justify-end items-center`} + on:click={async () => { + await basis.callback([classes_swap ? false : true, el_swap]); + if (classes_swap) classes_swap = ``; + else classes_swap = ` swap-active`; + }} + > + {#if `glyph` in basis && basis.glyph} + <Glyph + basis={{ + classes: `group-active:opacity-70 ${basis.glyph.classes}`, + ...basis.glyph, + }} + /> + {/if} + {#if `label` in basis && basis.label} + {#if basis.label.swap} + <label bind:this={el_swap} class={`swap${classes_swap}`}> + <div class="swap-off"> + <p + class={`${fmt_cl(basis.label.classes)} font-sans text-navPrevious text-layer-${layer}-glyph-hl -translate-y-[1px] transition-all`} + > + {basis.label.value} + </p> + </div> + <div class="swap-on"> + <p + class={`${fmt_cl(basis.label.classes)} font-sans text-navPrevious text-layer-${layer}-glyph-hl group-active:opacity-60 -translate-y-[1px] transition-all`} + > + {basis.label.swap} + </p> + </div> + </label> + {:else} + <p + class={`${fmt_cl(basis.label.classes)} font-sans text-navPrevious text-layer-1-glyph-hl group-active:opacity-60 transition-opacity`} + > + {basis.label.value} + </p> + {/if} + {/if} + </button> +{/if} diff --git a/apps-lib/src/lib/index.ts b/apps-lib/src/lib/index.ts @@ -1,5 +1,4 @@ -export * from "./locales/i18n" -export { default as AppConfig } from "./components/app_config.svelte" +export { default as AppConfig, show_toast } from "./components/app_config.svelte" export { default as ButtonLoading } from "./components/button_loading.svelte" export { default as Envelope } from "./components/envelope.svelte" export { default as EnvelopeLower } from "./components/envelope_lower.svelte" @@ -12,6 +11,7 @@ export { default as LayoutView } from "./components/layout_view.svelte" export { default as LayoutWindow } from "./components/layout_window.svelte" export { default as LoadingView } from "./components/loading_view.svelte" export { default as Nav } from "./components/nav.svelte" +export { default as NavOption } from "./components/nav_option.svelte" export { default as NotifyGlyph } from "./components/notify_glyph.svelte" export { default as Tabs } from "./components/tabs.svelte" export { default as Trellis } from "./components/trellis.svelte" @@ -23,6 +23,7 @@ export { default as TrellisRowDisplayValue } from "./components/trellis_row_disp export { default as TrellisRowLabel } from "./components/trellis_row_label.svelte" export { default as TrellisTitle } from "./components/trellis_title.svelte" export { default as TrellisTouch } from "./components/trellis_touch.svelte" +export * from "./locales/i18n" export * from "./stores/client" export * from "./stores/ndk" export * from "./types/client" @@ -36,9 +37,11 @@ export { default as Glyph } from "./ui/glyph.svelte" export { default as InputElement } from "./ui/input_element.svelte" export { default as Loading } from "./ui/loading.svelte" export { default as SelectElement } from "./ui/select_element.svelte" +export { default as Toast } from "./ui/toast.svelte" export * from "./utils/client" export * from "./utils/dom" export * from "./utils/geo" export * from "./utils/ndk" export * from "./utils/routes" export * from "./utils/time" + diff --git a/apps-lib/src/lib/locales/en/common.json b/apps-lib/src/lib/locales/en/common.json @@ -1,4 +1,11 @@ { + "done": "Done", + "unlock": "Unlock", + "nsec": "Nsec", + "npub": "Npub", + "hex": "Hex", + "public_key": "Public key", + "keys": "Keys", "settings": "Settings", "products": "Products", "locations": "Locations", diff --git a/apps-lib/src/lib/locales/en/icu.json b/apps-lib/src/lib/locales/en/icu.json @@ -1,4 +1,6 @@ { + "your": "Your {value}", + "nostr": "Nostr {value}", "invalid_entry": "Invalid {value} entry", "enter_new": "Enter new {value}", "enter": "Enter {value}", diff --git a/apps-lib/src/lib/stores/client.ts b/apps-lib/src/lib/stores/client.ts @@ -1,4 +1,4 @@ -import { type AppLayoutKey, type NavigationPreviousParam } from "$lib"; +import { type AppLayoutKey, type IToast, type NavigationPreviousParam } from "$lib"; import { writable } from "svelte/store"; import { queryParam, queryParameters } from "sveltekit-search-params"; @@ -18,6 +18,7 @@ export const app_config = writable<boolean>(false); export const app_render = writable<boolean>(false); export const app_win = writable<[number, number]>([0, 0]); export const app_notify = writable<string>(``); +export const app_toast = writable<IToast | false>(false); export const nav_visible = writable<boolean>(false); export const nav_blur = writable<boolean>(false); diff --git a/apps-lib/src/lib/types/client.ts b/apps-lib/src/lib/types/client.ts @@ -137,3 +137,11 @@ export type ILableFields = IGlOpt & { export type LabelFieldKind = `link` | `on` | `shade`; +export type IToastKind = `simple`; + +export type IToast = IClOpt & + ILabel & IGlOpt & { + styles?: IToastKind[]; + layer?: ThemeLayer; + position?: GeometryScreenPosition; + }; +\ No newline at end of file diff --git a/apps-lib/src/lib/types/components.ts b/apps-lib/src/lib/types/components.ts @@ -1,5 +1,5 @@ import type { NavigationRoute } from "$lib/utils/routes"; -import type { CallbackPromise, CallbackPromiseGeneric, ICb, ICbGOpt, ICbOpt, IClOpt, IGl, IGlOpt, ILabel, ILabelFieldsOpt, ILabelOpt, ILabelOptFieldsOpt, ILyOpt, ILyOptTs, NavigationParamTuple } from "./client"; +import type { CallbackPromise, CallbackPromiseGeneric, ICb, ICbG, ICbGOpt, ICbOpt, IClOpt, IGl, IGlOpt, ILabel, ILabelFieldsOpt, ILabelOpt, ILabelOptFieldsOpt, ILyOpt, ILyOptTs, NavigationParamTuple } from "./client"; import type { GlyphKey, GlyphWeight, IGlyph } from "./ui"; export type ITabsBasisList = { @@ -80,14 +80,17 @@ export type IEnvelopeTitledBasis = { } }; +export type INavBasisOption = ICbG< + [boolean, HTMLLabelElement | null] +> & IGlOpt & ILabelOpt & { + loading?: boolean; +}; export type INavBasis = { prev: ICbOpt & { label?: string; route: NavigationRoute | [NavigationRoute, NavigationParamTuple[]]; }; title?: ICbOpt & ILabel; - option?: ICb & IGlOpt & ILabelOpt & { - loading?: boolean; - }; + option?: INavBasisOption; }; diff --git a/apps-lib/src/lib/types/ui.ts b/apps-lib/src/lib/types/ui.ts @@ -132,5 +132,5 @@ export type IInputElement = IClOpt & ILyOptTs & { };*/ callback?: CallbackPromiseGeneric<{ val: string; pass: boolean; }>; callback_keydown?: CallbackPromiseGeneric<{ key: string; }>; - on_mount?: CallbackPromiseGeneric<HTMLElement>; + on_mount?: CallbackPromiseGeneric<HTMLInputElement>; }; \ No newline at end of file diff --git a/apps-lib/src/lib/ui/toast.svelte b/apps-lib/src/lib/ui/toast.svelte @@ -0,0 +1,71 @@ +<script lang="ts"> + import { + app_layout, + fmt_cl, + get_layout, + parse_layer, + type AppLayoutKey, + type IToast, + type IToastKind, + } from "$lib"; + import Glyph from "./glyph.svelte"; + + export const layout_toast_map: Map<AppLayoutKey, string> = new Map([ + [`base`, `pt-8`], + [`lg`, `pt-16`], + /*[ + `mobile-xl`, `pt-12` + ]*/ + ]); + + const lm: Map<IToastKind, { inner: string; outer: string }> = new Map([ + [ + `simple`, + { + inner: `justify-center`, + outer: `min-h-cover1 w-full px-4 rounded-2xl shadow-sm`, + }, + ], + ]); + + export let basis: IToast; + $: basis = basis; + + let styles: IToastKind[] = basis.styles || [`simple`]; + $: styles = styles; + let layer = parse_layer(basis.layer || 1); + $: layer = layer; + $: layout = get_layout($app_layout); +</script> + +<div + class={`${fmt_cl(layout_toast_map.get(layout))} z-[1000] h-[100vh] toast w-full ${basis.position || `top-center`} `} +> + <div class={`flex flex-row w-full h-max justify-center pb-2`}> + <div + class={`${fmt_cl(basis.classes)} relative grid grid-cols-12 h-max items-center justify-center ${styles.includes(`simple`) ? `bg-layer-${layer}-surface` : ``} ${fmt_cl(styles.map((style) => fmt_cl(lm.get(style)?.outer)).join(` `))}`} + > + <div + class={`absolute top-0 left-4 flex flex-row h-full items-center`} + > + <Glyph + basis={{ + key: `info`, + weight: `regular`, + dim: `md`, + ...basis.glyph, + }} + /> + </div> + <div + class={`col-span-12 flex flex-row pl-1 ${fmt_cl(styles.map((style) => fmt_cl(lm.get(style)?.inner)).join(` `))}`} + > + <p + class={`font-sans font-[500] truncate text-layer-${layer}-glyph -translate-y-[1px]`} + > + {basis.label.value} + </p> + </div> + </div> + </div> +</div> diff --git a/apps-lib/src/lib/utils/client.ts b/apps-lib/src/lib/utils/client.ts @@ -1,6 +1,6 @@ import { goto } from "$app/navigation"; import type { GlyphKey, NavigationRoute } from "$lib"; -import type { AnchorRoute, CallbackPromiseGeneric, LabelFieldKind, NavigationParamTuple, NavigationRouteParamKey } from "$lib/types/client"; +import type { AnchorRoute, AppLayoutKey, CallbackPromiseGeneric, LabelFieldKind, NavigationParamTuple, NavigationRouteParamKey } from "$lib/types/client"; import type { ColorMode, ThemeKey, ThemeLayer } from "@radroots/theme"; export const sleep = async (ms: number): Promise<void> => { @@ -46,7 +46,7 @@ export const encode_qp = (params_list?: NavigationParamTuple[]): string => { }; export const encode_qp_route = (route: NavigationRoute, params_list?: NavigationParamTuple[]): string => { - return `${route}/${encode_qp(params_list)}` + return `${route}/${encode_qp(params_list)}`.replaceAll(`//`, `/`) }; export const decode_qp = (query_param: string): AnchorRoute => { @@ -92,10 +92,10 @@ export const int_step = (num: number, op: `+` | `-`, bounds?: number): number => return Math.max(int_num - 1, bounds || 0); }; -export const clipboard_copy = async (text: string, callback: CallbackPromiseGeneric<string>): Promise<void> => { +export const clipboard_copy = async (text: string, callback?: CallbackPromiseGeneric<string>): Promise<void> => { try { navigator.clipboard.writeText(text).then(async () => { - await callback(text); + if (callback) await callback(text); }); } catch (e) { console.log(`(error) clipboard_copy `, e); @@ -113,4 +113,14 @@ export const route = async (route: NavigationRoute, params_list?: NavigationPara } catch (e) { console.log(`(error) route `, e); } -} -\ No newline at end of file +} + +export const get_layout = (val: string | false): AppLayoutKey => { + switch (val) { + case `base`: + case `lg`: + return val; + default: + return `base`; + }; +}; diff --git a/apps-lib/src/lib/utils/routes.ts b/apps-lib/src/lib/utils/routes.ts @@ -4,6 +4,7 @@ export type NavigationRoute = | "/models/location-gcs/view-map" | "/models/nostr-profile" | "/models/nostr-profile/edit/field" + | "/models/nostr-profile/view" | "/models/trade-product" | "/models/trade-product/add" | "/models/trade-product/add/preview" @@ -24,6 +25,7 @@ export function parse_route(route: string): NavigationRoute { case "/models/location-gcs/view-map": case "/models/nostr-profile": case "/models/nostr-profile/edit/field": + case "/models/nostr-profile/view": case "/models/trade-product": case "/models/trade-product/add": case "/models/trade-product/add/preview":