web_lib

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

commit f30d573bd8dbd1c73f01cabd3f5f373537b8717f
parent 5657daaa549edca563d6c33a193eb22366fd5ca1
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Thu, 12 Dec 2024 05:19:23 +0000

apps-lib: edit components, elements. add/edit locales, types, utils

Diffstat:
Mapps-lib/src/lib/components/nav_toolbar.svelte | 34+++++++++++++++++++++++++++++++++-
Mapps-lib/src/lib/components/page_header.svelte | 30++++++++++--------------------
Mapps-lib/src/lib/el/input_element.svelte | 1+
Mapps-lib/src/lib/el/textarea_element.svelte | 2+-
Mapps-lib/src/lib/locales/en/common.json | 6+++++-
Mapps-lib/src/lib/locales/en/icu.json | 1+
Mapps-lib/src/lib/locales/en/models.json | 324+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mapps-lib/src/lib/types/client.ts | 2+-
Mapps-lib/src/lib/types/components.ts | 19++++++++++++++++---
Mapps-lib/src/lib/types/el.ts | 27+++++++++++++++++----------
Mapps-lib/src/lib/utils/client.ts | 4++++
11 files changed, 272 insertions(+), 178 deletions(-)

diff --git a/apps-lib/src/lib/components/nav_toolbar.svelte b/apps-lib/src/lib/components/nav_toolbar.svelte @@ -1,11 +1,31 @@ <script lang="ts"> - import { Glyph, LogoCircleSm, ls, route } from "$lib"; + import { + Glyph, + LogoCircleSm, + ls, + PageHeader, + route, + type CallbackPromiseReturn, + type IPageHeader, + type NavigationRoute, + } from "$lib"; + + export let basis: { + callback_route?: CallbackPromiseReturn<NavigationRoute | undefined>; + page_header?: IPageHeader; + }; </script> <div class={`flex flex-row h-12 w-full px-6 pb-6 justify-between items-center`}> <button class={`flex flex-row gap-2 justify-start items-center`} on:click={async () => { + if (basis.callback_route) { + const route_to = await basis.callback_route(); + if (!route_to) return; + await route(route_to); + return; + } await route(`/`); }} > @@ -32,3 +52,15 @@ /> </button> </div> +{#if basis?.page_header} + <PageHeader + basis={{ + callback_route: basis.callback_route, + ...basis?.page_header, + }} + > + <div slot="option" class={`flex flex-row justify-start items-center`}> + <slot name="page_header_option" /> + </div> + </PageHeader> +{/if} diff --git a/apps-lib/src/lib/components/page_header.svelte b/apps-lib/src/lib/components/page_header.svelte @@ -1,25 +1,8 @@ <script lang="ts"> - import { - app_layout, - Fill, - ph_blur, - route, - type NavigationParamTuple, - type NavigationRoute, - } from "$lib"; + import { app_layout, Fill, ph_blur, route, type IPageHeader } from "$lib"; import { fade } from "svelte/transition"; - export let basis: { - label: - | string - | [ - string, - { - route: NavigationRoute; - route_param?: NavigationParamTuple[]; - }, - ]; - }; + export let basis: IPageHeader; </script> {#if $ph_blur} @@ -45,11 +28,18 @@ <button class={`flex flex-row gap-1 justify-center items-center`} on:click={async () => { - if (typeof basis.label !== `string`) + if (typeof basis.label !== `string`) { + if (basis.callback_route) { + const route_to = await basis.callback_route(); + if (!route_to) return; + await route(route_to); + return; + } await route( basis.label[1].route, basis.label[1].route_param || undefined, ); + } }} > <p diff --git a/apps-lib/src/lib/el/input_element.svelte b/apps-lib/src/lib/el/input_element.svelte @@ -47,6 +47,7 @@ if (kv_val && el) el.value = kv_val; else await kv.set(basis?.id, ``); } + if (basis?.on_mount) await basis?.on_mount({ el }); } catch (e) { console.log(`(error) kv_init `, e); } diff --git a/apps-lib/src/lib/el/textarea_element.svelte b/apps-lib/src/lib/el/textarea_element.svelte @@ -41,7 +41,7 @@ if (kv_val && el) el.value = fmt_textarea_value(kv_val); else await kv.set(basis?.id, ``); } - if (basis?.on_mount) await basis?.on_mount(el); + if (basis?.on_mount) await basis?.on_mount({ el }); } catch (e) { console.log(`(error) kv_init `, e); } diff --git a/apps-lib/src/lib/locales/en/common.json b/apps-lib/src/lib/locales/en/common.json @@ -1,5 +1,4 @@ { - "notifications": "Notifications", "accept": "Accept", "activation": "Activation", "active": "Active", @@ -43,6 +42,7 @@ "details": "Details", "device": "Device", "disagree": "Disagree", + "discard": "Discard", "disconnect": "Disconnect", "do_you_want_to_continue_q": "Do you want to continue?", "done": "Done", @@ -68,8 +68,10 @@ "key": "Key", "keypair": "Keypair", "keys": "Keys", + "label": "Label", "land_area": "Land area", "land_plot": "Land plot", + "land_plots": "Land plots", "latitude": "Latitude", "light": "Light", "list": "List", @@ -95,6 +97,7 @@ "no_locations_saved": "No locations saved", "nostr": "Nostr", "not_connected": "Not connected", + "notifications": "Notifications", "npub": "Npub", "nsec": "Nsec", "optional": "Optional", @@ -133,6 +136,7 @@ "reset": "Reset", "return": "Return", "review": "Review", + "save": "Save", "search": "Search", "secret_key": "Secret key", "settings": "Settings", diff --git a/apps-lib/src/lib/locales/en/icu.json b/apps-lib/src/lib/locales/en/icu.json @@ -69,6 +69,7 @@ "the_*_is_required": "The {value} is missing", "the_current_entry_*_will_be_deleted": "The current entry \"{value}\" will be deleted", "the_listing_will_be_created_without_a_*": "The listing will be created without a {value}", + "there_are_changes_to_the_*": "There are changes to the {value}", "there_was_a_failure_while_*": "There was a failure while {value}", "this_*": "This {value}", "toggle_*": "Toggle {value}", diff --git a/apps-lib/src/lib/locales/en/models.json b/apps-lib/src/lib/locales/en/models.json @@ -1,14 +1,38 @@ { "location_gcs": { "fields": { + "lat": { + "label": "Location Latitude" + }, + "lng": { + "label": "Location Latitude" + }, + "geohash": { + "label": "Location Geohash" + }, + "kind": { + "label": "Location Kind" + }, + "label": { + "label": "Location Label" + }, "area": { "label": "Location Area" }, + "elevation": { + "label": "Location Elevation" + }, + "soil": { + "label": "Location Soil" + }, "climate": { "label": "Location Climate" }, - "elevation": { - "label": "Location Elevation" + "gc_id": { + "label": "Location Gc Id" + }, + "gc_name": { + "label": "Location Gc Name" }, "gc_admin1_id": { "label": "Location Gc Admin1 Id" @@ -21,90 +45,78 @@ }, "gc_country_name": { "label": "Location Gc Country Name" - }, - "gc_id": { - "label": "Location Gc Id" - }, - "gc_name": { - "label": "Location Gc Name" - }, - "geohash": { - "label": "Location Geohash" - }, - "kind": { - "label": "Location Kind" - }, - "label": { - "label": "Location Label" - }, - "lat": { - "label": "Location Latitude" - }, - "lng": { - "label": "Location Latitude" - }, - "soil": { - "label": "Location Soil" } }, "schema": { - "geohash.length": "The location geohash must be 9 characters", - "geohash.required": "The location geohash is required", - "kind.required": "The location kind is required", - "lat.max": "The location latitude must be less than 90", - "lat.min": "The location latitude must be greater than -90", "lat.required": "The location latitude is required", - "lng.max": "The location latitude must be less than 180", + "lat.min": "The location latitude must be greater than -90", + "lat.max": "The location latitude must be less than 90", + "lng.required": "The location latitude is required", "lng.min": "The location latitude must be greater than -180", - "lng.required": "The location latitude is required" + "lng.max": "The location latitude must be less than 180", + "geohash.required": "The location geohash is required", + "geohash.length": "The location geohash must be 9 characters", + "kind.required": "The location kind is required", + "label.type": "The location label is invalid", + "area.type": "The location area is invalid", + "area.nonnegative": "The location area nonnegative is incomplete", + "area.finite": "The location area must be a finite number", + "elevation.type": "The location elevation is invalid", + "elevation.int": "The location elevation must be an integer", + "elevation.nonnegative": "The location elevation nonnegative is incomplete", + "soil.type": "The location soil is invalid", + "climate.type": "The location climate is invalid", + "gc_id.type": "The location gc id is invalid", + "gc_name.type": "The location gc name is invalid", + "gc_admin1_id.type": "The location gc admin1 id is invalid", + "gc_admin1_name.type": "The location gc admin1 name is invalid", + "gc_country_id.type": "The location gc country id is invalid", + "gc_country_name.type": "The location gc country name is invalid" } }, "log_error": { "fields": { - "app_system": { - "label": "Log App System" + "error": { + "label": "Log Error" }, - "app_version": { - "label": "Log App Version" + "message": { + "label": "Log Message" + }, + "stack_trace": { + "label": "Log Stack Trace" }, "cause": { "label": "Log Cause" }, - "data": { - "label": "Log Data" - }, - "error": { - "label": "Log Error" + "app_system": { + "label": "Log App System" }, - "message": { - "label": "Log Message" + "app_version": { + "label": "Log App Version" }, "nostr_pubkey": { "label": "Log Nostr Pubkey" }, - "stack_trace": { - "label": "Log Stack Trace" + "data": { + "label": "Log Data" } }, "schema": { - "app_system.required": "The log app system is required", - "app_version.required": "The log app version is required", "error.required": "The log error is required", "message.required": "The log message is required", - "nostr_pubkey.required": "The log nostr pubkey is required" + "stack_trace.type": "The log stack trace is invalid", + "cause.type": "The log cause is invalid", + "app_system.required": "The log app system is required", + "app_version.required": "The log app version is required", + "nostr_pubkey.required": "The log nostr pubkey is required", + "data.type": "The log data is invalid" } }, "media_upload": { "fields": { - "description": { - "label": "Description" - }, "file_path": { "label": "File Path" }, - "label": { - "label": "Label" - }, "mime_type": { "label": "Mime Type" }, @@ -113,179 +125,209 @@ }, "res_path": { "label": "Resource Path" + }, + "label": { + "label": "Label" + }, + "description": { + "label": "Description" } }, "schema": { "file_path.required": "The media file path is required", "mime_type.required": "The media mime type is required", - "res_base.regex": "The media resource endpoint requires an http protocol", "res_base.required": "The media resource endpoint is required", "res_base.url": "The media resource endpoint is incorrectly formatted", - "res_path.required": "The media resource path is required" + "res_base.regex": "The media resource endpoint requires an http protocol", + "res_path.required": "The media resource path is required", + "label.type": "The media label is invalid", + "description.type": "The media description is invalid" } }, "nostr_profile": { "fields": { - "about": { - "label": "Profile About" + "public_key": { + "label": "Profile Public Key" }, - "banner": { - "label": "Profile Banner" + "name": { + "label": "Profile Name" }, "display_name": { "label": "Profile Display Name" }, - "lud06": { - "label": "Profile Lud-06" + "about": { + "label": "Profile About" }, - "lud16": { - "label": "Profile Lud-16" + "website": { + "label": "Profile Website" }, - "name": { - "label": "Profile Name" + "picture": { + "label": "Profile Picture" + }, + "banner": { + "label": "Profile Banner" }, "nip05": { "label": "Profile Nip-05" }, - "picture": { - "label": "Profile Picture" - }, - "public_key": { - "label": "Profile Public Key" + "lud06": { + "label": "Profile Lud-06" }, - "website": { - "label": "Profile Website" + "lud16": { + "label": "Profile Lud-16" } }, "schema": { + "public_key.required": "The profile public key is required", + "public_key.length": "The profile public key must be 64 characters", + "name.type": "The profile name is invalid", + "display_name.type": "The profile display name is invalid", + "about.type": "The profile about is invalid", + "website.type": "The profile website is invalid", + "website.url": "The profile website url is incomplete", + "picture.type": "The profile picture is invalid", + "picture.url": "The profile picture url is incomplete", + "banner.type": "The profile banner is invalid", "banner.url": "The profile banner url is incomplete", + "nip05.type": "The profile nip-05 is invalid", "nip05.email": "The profile nip-05 is incorrectly formatted", - "picture.url": "The profile picture url is incomplete", - "public_key.length": "The profile public key must be 64 characters", - "public_key.required": "The profile public key is required", - "website.url": "The profile website url is incomplete" + "lud06.type": "The profile lud-06 is invalid", + "lud16.type": "The profile lud-16 is invalid" } }, "nostr_relay": { "fields": { - "contact": { - "label": "Administrator Contact" - }, - "data": { - "label": "Additional Information" + "url": { + "label": "Relay Endpoint" }, - "description": { - "label": "Relay Description" + "relay_id": { + "label": "Relay Id" }, "name": { "label": "Relay Name" }, + "description": { + "label": "Relay Description" + }, "pubkey": { "label": "Administrator" }, - "relay_id": { - "label": "Relay Id" - }, - "software": { - "label": "Software" + "contact": { + "label": "Administrator Contact" }, "supported_nips": { "label": "Supported Nips" }, - "url": { - "label": "Relay Endpoint" + "software": { + "label": "Software" }, "version": { "label": "Software Version" + }, + "data": { + "label": "Additional Information" } }, "schema": { - "url.regex": "The relay relay endpoint requires a websocket protocol", "url.required": "The relay relay endpoint is required", - "url.url": "The relay relay endpoint is incorrectly formatted" + "url.url": "The relay relay endpoint is incorrectly formatted", + "url.regex": "The relay relay endpoint requires a websocket protocol", + "relay_id.type": "The relay relay id is invalid", + "name.type": "The relay name is invalid", + "description.type": "The relay description is invalid", + "pubkey.type": "The relay Administrator is invalid", + "contact.type": "The relay Administrator contact is invalid", + "supported_nips.type": "The relay supported nips is invalid", + "software.type": "The relay software is invalid", + "version.type": "The relay Software version is invalid", + "data.type": "The relay Additional information is invalid" } }, "trade_product": { "fields": { - "category": { - "label": "Product Category" - }, "key": { "label": "Product Kind" }, - "lot": { - "label": "Product Lot" - }, - "notes": { - "label": "Notes" - }, - "price_amt": { - "label": "Price Amount" - }, - "price_currency": { - "label": "Price Currency" + "category": { + "label": "Product Category" }, - "price_qty_amt": { - "label": "Price Quantity" + "title": { + "label": "Product Title" }, - "price_qty_unit": { - "label": "Price Quantity Unit" + "summary": { + "label": "Product Description" }, "process": { "label": "Processing Method" }, + "lot": { + "label": "Product Lot" + }, "profile": { "label": "Flavor Profile" }, + "year": { + "label": "Production Year" + }, "qty_amt": { "label": "Quantity Amount" }, - "qty_avail": { - "label": "Quantity Available" + "qty_unit": { + "label": "Quantity Unit" }, "qty_label": { "label": "Quantity Name" }, - "qty_unit": { - "label": "Quantity Unit" + "qty_avail": { + "label": "Quantity Available" }, - "summary": { - "label": "Product Description" + "price_amt": { + "label": "Price Amount" }, - "title": { - "label": "Product Title" + "price_currency": { + "label": "Price Currency" }, - "year": { - "label": "Production Year" + "price_qty_amt": { + "label": "Price Quantity" + }, + "price_qty_unit": { + "label": "Price Quantity Unit" + }, + "notes": { + "label": "Notes" } }, "schema": { - "category.required": "The product category is required", "key.required": "The product kind is required", - "lot.max": "The product lot must be less than 120 characters", - "lot.min": "The product lot must be more than 1 character", - "lot.required": "The product lot is required", - "price_amt.positive": "The product price amount must be positive", - "price_amt.required": "The product price amount is required", - "price_currency.length": "The product price currency must be 3 characters", - "price_currency.required": "The product price currency is required", - "price_qty_amt.int": "The product price quantity must be an integer", - "price_qty_amt.positive": "The product price quantity must be positive", - "price_qty_amt.required": "The product price quantity is required", - "price_qty_unit.required": "The product price quantity unit is required", + "category.required": "The product category is required", + "title.required": "The product title is required", + "summary.required": "The product description is required", "process.required": "The product processing method is required", + "lot.required": "The product lot is required", + "lot.min": "The product lot must be more than 1 character", + "lot.max": "The product lot must be less than 120 characters", "profile.required": "The product flavor profile is required", + "year.required": "The product production year is required", + "year.int": "The product production year must be an integer", + "year.positive": "The product production year must be positive", + "qty_amt.required": "The product quantity amount is required", "qty_amt.int": "The product quantity amount must be an integer", "qty_amt.positive": "The product quantity amount must be positive", - "qty_amt.required": "The product quantity amount is required", + "qty_unit.required": "The product quantity unit is required", + "qty_label.type": "The product quantity name is invalid", + "qty_avail.type": "The product quantity available is invalid", "qty_avail.int": "The product quantity available must be an integer", "qty_avail.positive": "The product quantity available must be positive", - "qty_unit.required": "The product quantity unit is required", - "summary.required": "The product description is required", - "title.required": "The product title is required", - "year.int": "The product production year must be an integer", - "year.positive": "The product production year must be positive", - "year.required": "The product production year is required" + "price_amt.required": "The product price amount is required", + "price_amt.positive": "The product price amount must be positive", + "price_amt.finite": "The product price amount must be a finite number", + "price_currency.required": "The product price currency is required", + "price_currency.length": "The product price currency must be 3 characters", + "price_qty_amt.required": "The product price quantity is required", + "price_qty_amt.int": "The product price quantity must be an integer", + "price_qty_amt.positive": "The product price quantity must be positive", + "price_qty_unit.required": "The product price quantity unit is required", + "notes.type": "The product notes is invalid" } } } \ No newline at end of file diff --git a/apps-lib/src/lib/types/client.ts b/apps-lib/src/lib/types/client.ts @@ -30,7 +30,7 @@ export type GeometryGlyphDimension = //export type CallbackPromiseGenericReturn<T1, T2> = (value: T1) => Promise<T2>; export type CallbackPromiseGeneric<T> = (value: T) => Promise<void>; -//export type CallbackPromiseReturn<T> = () => Promise<T>; +export type CallbackPromiseReturn<T> = () => Promise<T>; export type CallbackPromise = () => Promise<void>; export type IRoute = { diff --git a/apps-lib/src/lib/types/components.ts b/apps-lib/src/lib/types/components.ts @@ -1,4 +1,4 @@ -import type { CallbackPromise, CallbackPromiseGeneric, GlyphKey, GlyphWeight, ICb, ICbG, ICbOpt, IClOpt, IDisabledOpt, IGl, IGlOpt, IGlyph, IIdOpt, IIdWrapOpt, IInputElement, ILabel, ILabelFieldsOpt, ILabelOpt, ILabelOptFieldsOpt, ILabelValue, ILoadingOpt, ILyOpt, ILyOptTs, ISelectElement, ITextAreaElement, NavigationParamTuple, NavigationRoute } from "$lib"; +import type { CallbackPromise, CallbackPromiseGeneric, CallbackPromiseReturn, GlyphKey, GlyphWeight, ICb, ICbG, ICbOpt, IClOpt, IDisabledOpt, 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 & { @@ -118,4 +118,17 @@ export type INavBasis = { export type IDisplayLine = IIdWrapOpt & IClOpt & ILabelValue & ILyOpt & { style?: IEntryStyle -} -\ No newline at end of file +} + +export type IPageHeader = { + callback_route?: CallbackPromiseReturn<NavigationRoute | undefined>; + label: + | string + | [ + string, + { + route: NavigationRoute; + route_param?: NavigationParamTuple[]; + }, + ]; +}; +\ No newline at end of file diff --git a/apps-lib/src/lib/types/el.ts b/apps-lib/src/lib/types/el.ts @@ -121,6 +121,13 @@ export type GlyphKey = | export type GlyphWeight = `light` | `regular` | `fill` | `bold`; // `thin` `duotone` +export type ElementCallbackValue = CallbackPromiseGeneric<{ value: string; pass: boolean; }>; +export type ElementCallbackValueKeydown<T extends HTMLElement> = CallbackPromiseGeneric<{ key: string; key_s: boolean; el: T }>; +export type ElementCallbackValueBlur<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>; +export type ElementCallbackValueFocus<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>; +export type ElementCallbackMount<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>; + + export type IGlyph = ICbOpt & IIdOpt & { layer?: ThemeLayer; classes?: string; @@ -169,11 +176,11 @@ export type IInputElement = IId & IClOpt & ILyOptTs & { validate?: RegExp; sync?: boolean; field?: IFormField; - callback?: CallbackPromiseGeneric<{ value: string; pass: boolean; }>; - callback_keydown?: CallbackPromiseGeneric<{ key: string; key_s: boolean; el: HTMLInputElement }>; - callback_blur?: CallbackPromiseGeneric<{ el: HTMLInputElement }>; - callback_focus?: CallbackPromiseGeneric<{ el: HTMLInputElement }>; - on_mount?: CallbackPromiseGeneric<HTMLInputElement>; + callback?: ElementCallbackValue, + callback_keydown?: ElementCallbackValueKeydown<HTMLInputElement>, + callback_blur?: ElementCallbackValueBlur<HTMLInputElement>; + callback_focus?: ElementCallbackValueBlur<HTMLInputElement>; + on_mount?: ElementCallbackMount<HTMLInputElement>; }; export type ITextAreaElement = IId & IClOpt & ILyOptTs & { @@ -183,9 +190,9 @@ export type ITextAreaElement = IId & IClOpt & ILyOptTs & { validate?: RegExp; sync?: true; field?: IFormField; - callback?: CallbackPromiseGeneric<{ value: string; pass: boolean; }>; - callback_keydown?: CallbackPromiseGeneric<{ key: string; key_s: boolean; el: HTMLTextAreaElement }>; - callback_blur?: CallbackPromiseGeneric<{ el: HTMLTextAreaElement }>; - callback_focus?: CallbackPromiseGeneric<{ el: HTMLTextAreaElement }>; - on_mount?: CallbackPromiseGeneric<HTMLTextAreaElement>; + callback?: ElementCallbackValue, + callback_keydown?: ElementCallbackValueKeydown<HTMLTextAreaElement>, + callback_blur?: ElementCallbackValueBlur<HTMLTextAreaElement>; + callback_focus?: ElementCallbackValueBlur<HTMLTextAreaElement>; + on_mount?: ElementCallbackMount<HTMLTextAreaElement>; }; \ No newline at end of file diff --git a/apps-lib/src/lib/utils/client.ts b/apps-lib/src/lib/utils/client.ts @@ -276,6 +276,10 @@ export const fmt_geol_longitude = (lng: number, fmt_opt: GeolocationLatitudeFmtO } }; +export const fmt_geol_point = (lat: number, lng: number, precision: number = 4): [string, string] => { + return [fmt_geol_latitude(lat, `d`, precision), fmt_geol_longitude(lng, `d`, precision)]; +}; + export const exe_iter = async (callback: CallbackPromise, num: number = 1, delay: number = 400): Promise<void> => { try { const iter_fn = (count: number) => {