web_lib

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

commit 10d5f1a3cbf25e0dd30c6e7925bf384333594ca3
parent 72e0d0d9573803afcba182a6f18560c5c0eb78fc
Author: triesap <triesap@radroots.dev>
Date:   Thu, 20 Nov 2025 14:21:33 +0000

apps-lib-pwa: migrate and refactor home view from `@radroots/apps-lib`

Diffstat:
Aapps-lib-pwa/src/lib/components/button/button-simple.svelte | 22++++++++++++++++++++++
Aapps-lib-pwa/src/lib/components/layout/layout-page.svelte | 18++++++++++++++++++
Aapps-lib-pwa/src/lib/components/layout/layout-view.svelte | 48++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib-pwa/src/lib/components/lib/logo-circle-sm.svelte | 14++++++++++++++
Aapps-lib-pwa/src/lib/components/lib/logo-letters.svelte | 3+++
Aapps-lib-pwa/src/lib/components/navigation/navigation-tabs.svelte | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib-pwa/src/lib/components/navigation/page-header.svelte | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib-pwa/src/lib/components/navigation/page-toolbar.svelte | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapps-lib-pwa/src/lib/index.ts | 7+++++++
Aapps-lib-pwa/src/lib/types/components.ts | 33+++++++++++++++++++++++++++++++++
Aapps-lib-pwa/src/lib/types/components/lib.ts | 33+++++++++++++++++++++++++++++++++
Aapps-lib-pwa/src/lib/types/views.ts | 9+++++++++
Aapps-lib-pwa/src/lib/views/root/home.svelte | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 455 insertions(+), 0 deletions(-)

diff --git a/apps-lib-pwa/src/lib/components/button/button-simple.svelte b/apps-lib-pwa/src/lib/components/button/button-simple.svelte @@ -0,0 +1,22 @@ +<script lang="ts"> + import type { IButtonSimple } from "$lib/types/components/lib"; + import { parse_layer } from "@radroots/apps-lib"; + + let { basis }: { basis: IButtonSimple } = $props(); + + const layer = $derived(parse_layer(basis.layer || 1)); +</script> + +<button + class={`group flex flex-row h-line_button w-full justify-center items-center rounded-touch bg-ly${layer} ly${layer}-active-surface ly${layer}-active-ring`} + onclick={async (ev) => { + if (!basis.allow_propogation) ev.stopPropagation(); + await basis.callback(); + }} +> + <p + class={`font-sans font-[600] text-xl text-ly0-gl capitalize tracking-wider opacity-active`} + > + {basis.label} + </p> +</button> diff --git a/apps-lib-pwa/src/lib/components/layout/layout-page.svelte b/apps-lib-pwa/src/lib/components/layout/layout-page.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { type IBasisOpt, type IClOpt, fmt_cl } from "@radroots/apps-lib"; + import type { Snippet } from "svelte"; + + let { + basis = undefined, + children, + }: { + basis?: IBasisOpt<IClOpt>; + children: Snippet; + } = $props(); +</script> + +<div + class={`${fmt_cl(basis?.classes)} flex flex-col w-full pt-4 px-4 pb-24 gap-4 justify-center items-center`} +> + {@render children()} +</div> diff --git a/apps-lib-pwa/src/lib/components/layout/layout-view.svelte b/apps-lib-pwa/src/lib/components/layout/layout-view.svelte @@ -0,0 +1,48 @@ +<script lang="ts"> + import { nav_blur, ph_blur, tabs_blur } from "$lib/stores/app"; + import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/apps-lib"; + import { onDestroy, onMount, type Snippet } from "svelte"; + + let { + basis = undefined, + el = $bindable(null), + children, + }: { + el?: HTMLDivElement | null; + basis?: IBasisOpt<IClOpt & { fade?: boolean }>; + children: Snippet; + } = $props(); + + onMount(async () => { + try { + el?.addEventListener("scroll", scrollChange); + } catch (e) { + } finally { + } + }); + + onDestroy(async () => { + try { + el?.removeEventListener("scroll", scrollChange); + } catch (e) { + } finally { + } + }); + + const scrollChange = (): void => { + if (Math.max(el?.scrollTop || 0, 0) > 10) nav_blur.set(true); + else nav_blur.set(false); + if (Math.max(el?.scrollTop || 0, 0) > 10) tabs_blur.set(true); + else tabs_blur.set(false); + if (Math.max(el?.scrollTop || 0, 0) > 30) ph_blur.set(true); + else ph_blur.set(false); + }; +</script> + +<div + bind:this={el} + class={`${fmt_cl(basis?.classes)} absolute top-0 left-0 flex flex-col h-[100vh] w-full justify-start items-center scroll-hide overflow-auto`} + class:fade-in={basis?.fade} +> + {@render children()} +</div> diff --git a/apps-lib-pwa/src/lib/components/lib/logo-circle-sm.svelte b/apps-lib-pwa/src/lib/components/lib/logo-circle-sm.svelte @@ -0,0 +1,14 @@ +<div + class={`relative flex flex-row h-12 w-12 justify-center items-center bg-ly2 rounded-full`} +> + <p + class={`font-sans font-[900] text-[1.5rem] text-ly0-gl -tracking-[0.4rem] -translate-x-[2px]`} + > + {"ยป`,"} + </p> + <p + class={`font-sans font-[900] text-[1.5rem] text-ly0-gl translate-x-[3px]`} + > + {"-"} + </p> +</div> diff --git a/apps-lib-pwa/src/lib/components/lib/logo-letters.svelte b/apps-lib-pwa/src/lib/components/lib/logo-letters.svelte @@ -0,0 +1,3 @@ +<p class={`font-sansd italic font-[700] text-[1.7rem] text-ly0-gl lowercase`}> + {`radroots`} +</p> diff --git a/apps-lib-pwa/src/lib/components/navigation/navigation-tabs.svelte b/apps-lib-pwa/src/lib/components/navigation/navigation-tabs.svelte @@ -0,0 +1,100 @@ +<script lang="ts"> + import { goto } from "$app/navigation"; + import { page } from "$app/state"; + import { app_lo } from "$lib/stores/app"; + import { Flex, Glyph } from "@radroots/apps-lib"; +</script> + +<div + class={`fixed bottom-0 left-0 h-nav_tabs_${$app_lo} flex flex-row w-full pt-2 justify-center items-start`} +> + <div class={`flex flex-row justify-between gap-10 items-center`}> + <div + class={`grid grid-cols-4 flex flex-row h-[3.1rem] px-6 gap-6 justify-start items-center bg-ly1 rounded-full backdrop-blur-lg`} + > + <button + class={`col-span-1 flex flex-row justify-center items-center`} + onclick={async () => { + await goto(`/`); + }} + > + <Glyph + basis={{ + classes: `text-[26px] text-ly0-gl/80 rotate-90`, + key: `columns`, + weight: page.url.pathname === `/` ? `fill` : `bold`, + }} + /> + </button> + <button + class={`relative col-span-1 flex flex-row justify-center items-center`} + onclick={async () => { + await goto(`/search`); + }} + > + <Glyph + basis={{ + classes: `text-[24px] text-ly0-gl/80`, + key: `magnifying-glass`, + weight: page.url.pathname.includes(`search`) + ? `fill` + : `bold`, + }} + /> + </button> + <button + class={`relative col-span-1 flex flex-row justify-center items-center`} + onclick={async () => { + goto(`/profile`); + }} + > + <Glyph + basis={{ + classes: `text-[24px] text-ly0-gl/80`, + key: `user`, + weight: page.url.pathname.includes(`profile`) + ? `fill` + : `bold`, + }} + /> + </button> + <button + class={`relative col-span-1 flex flex-row h-full justify-center items-center`} + onclick={async () => { + await goto(`/notifications`); + }} + > + <Glyph + basis={{ + classes: `text-[24px] text-ly0-gl/80`, + key: `bell`, + weight: page.url.pathname.includes(`notifications`) + ? `fill` + : `bold`, + }} + /> + <div + class={`absolute top-2 -right-1 flex flex-row justify-start items-center`} + > + <div + class={`flex flex-row h-2 w-2 justify-start items-center bg-yellow-400 rounded-full`} + > + <Flex /> + </div> + </div> + </button> + </div> + <button + class={`flex flex-row h-[3.1rem] w-[3.1rem] justify-center items-center bg-ly1 rounded-full backdrop-blur-lg`} + onclick={async () => {}} + > + <Glyph + basis={{ + classes: `text-[22px] text-ly0-gl/80`, + + key: `plus`, + }} + /> + </button> + </div> +</div> diff --git a/apps-lib-pwa/src/lib/components/navigation/page-header.svelte b/apps-lib-pwa/src/lib/components/navigation/page-header.svelte @@ -0,0 +1,55 @@ +<script lang="ts"> + import { app_lo, ph_blur } from "$lib/stores/app"; + import type { IPageHeader } from "$lib/types/components"; + import { callback_route, Flex } from "@radroots/apps-lib"; + import type { Snippet } from "svelte"; + import { fade } from "svelte/transition"; + + let { + basis, + children, + }: { + basis: IPageHeader<string>; + children?: Snippet; + } = $props(); +</script> + +{#if $ph_blur} + <div + in:fade={{ duration: 50 }} + out:fade={{ delay: 50, duration: 200 }} + class={`z-20 fixed top-0 left-0 flex flex-row h-nav_page_header_${$app_lo} w-full justify-center items-center bg-ly0-blur/30 backdrop-blur-lg`} + > + <Flex /> + </div> +{/if} +<div + class={`z-20 sticky top-0 flex flex-row min-h-nav_page_header_${$app_lo} h-nav_page_header_${$app_lo} w-full px-6 justify-between items-center`} +> + <div class={`flex flex-row justify-start items-center`}> + <button + class={`flex flex-row justify-center items-center`} + onclick={async () => { + if (basis.callback_route) + await callback_route(basis.callback_route); + }} + > + <p + class={`font-sansd font-[700] text-2xl text-ly0-gl capitalize max-w-lo_${$app_lo} truncate`} + > + {basis.label || ``} + </p> + </button> + </div> + {#if children} + {#if !$ph_blur} + <div + in:fade={{ duration: 50 }} + out:fade={{ delay: 50, duration: 200 }} + class={`flex flex-row justify-center items-center`} + > + {@render children()} + </div> + {/if} + {/if} +</div> diff --git a/apps-lib-pwa/src/lib/components/navigation/page-toolbar.svelte b/apps-lib-pwa/src/lib/components/navigation/page-toolbar.svelte @@ -0,0 +1,57 @@ +<script lang="ts"> + import { goto } from "$app/navigation"; + import { app_lo } from "$lib/stores/app"; + import type { IPageToolbar } from "$lib/types/components"; + import { type IBasisOpt, Glyph } from "@radroots/apps-lib"; + import type { Snippet } from "svelte"; + import LogoCircleSm from "../lib/logo-circle-sm.svelte"; + import LogoLetters from "../lib/logo-letters.svelte"; + import PageHeader from "./page-header.svelte"; + + let { + basis = undefined, + header_option, + }: { + basis?: IBasisOpt<IPageToolbar<string>>; + header_option?: Snippet; + } = $props(); +</script> + +<div + class={`flex flex-row min-h-nav_page_toolbar_${$app_lo} h-nav_page_toolbar_${$app_lo} w-full px-6 justify-between items-end`} +> + <div class={`flex flex-row w-full justify-between items-center`}> + <button + class={`flex flex-row gap-2 justify-start items-center`} + onclick={async () => { + if (basis?.callback) await basis.callback(); + else await goto(`/`); + }} + > + <LogoCircleSm /> + <LogoLetters /> + </button> + <button + class={`flex flex-row justify-center items-center`} + onclick={async () => { + await goto(`/settings`); + }} + > + <Glyph + basis={{ + classes: `text-ly0-gl`, + dim: `lg`, + + key: `gear`, + }} + /> + </button> + </div> +</div> +{#if basis?.header} + <PageHeader basis={basis.header}> + {#if header_option} + {@render header_option()} + {/if} + </PageHeader> +{/if} diff --git a/apps-lib-pwa/src/lib/index.ts b/apps-lib-pwa/src/lib/index.ts @@ -1,10 +1,17 @@ export { default as ButtonLayoutPair } from "./components/button/button-layout-pair.svelte"; export { default as ButtonLayout } from "./components/button/button-layout.svelte"; +export { default as ButtonSimple } from "./components/button/button-simple.svelte"; export { default as EntryLine } from "./components/form/entry-line.svelte"; export { default as EntryWrap } from "./components/form/entry-wrap.svelte"; +export { default as LayoutPage } from "./components/layout/layout-page.svelte"; +export { default as LayoutView } from "./components/layout/layout-view.svelte"; export { default as LayoutWindow } from "./components/layout/layout-window.svelte"; export { default as Css } from "./components/lib/css.svelte"; export { default as InputValue } from "./components/lib/input-value.svelte"; export { default as LoadSymbol } from "./components/lib/load-symbol.svelte"; export { default as LogoCircle } from "./components/lib/logo-circle.svelte"; export { default as SelectMenu } from "./components/lib/select-menu.svelte"; +export { default as NavigationTabs } from "./components/navigation/navigation-tabs.svelte"; +export { default as PageHeader } from "./components/navigation/page-header.svelte"; +export { default as PageToolbar } from "./components/navigation/page-toolbar.svelte"; +export { default as Home } from "./views/root/home.svelte"; diff --git a/apps-lib-pwa/src/lib/types/components.ts b/apps-lib-pwa/src/lib/types/components.ts @@ -0,0 +1,33 @@ +import type { CallbackRoute, GeometryScreenPositionHorizontal, ICb, ICbOpt, IDisabledOpt, IGlyph, IGlyphKey, ILoadingOpt, ILyOpt } from "@radroots/apps-lib"; +import type { CallbackPromise } from "@radroots/utils"; + +export type IButtonSimple = ILyOpt & { + label: string; + callback: CallbackPromise; + allow_propogation?: boolean; +}; + +export type IPageHeader<T extends string> = { + label: string; + callback_route?: CallbackRoute<T>; +}; + +export type IPageToolbar<T extends string> = ICbOpt & { + header?: IPageHeader<T>; +}; + +export type IMapMarkerArea = { + show_display?: boolean; + no_drag?: boolean; +} + +export type IGlyphCircle = { + classes_wrap: string; + glyph: IGlyph +}; + +export type IFloatPage = { + posx: Omit<GeometryScreenPositionHorizontal, "center">; +}; + +export type IButtonNavRound = ICb & IDisabledOpt & ILoadingOpt & IGlyphKey; diff --git a/apps-lib-pwa/src/lib/types/components/lib.ts b/apps-lib-pwa/src/lib/types/components/lib.ts @@ -0,0 +1,33 @@ +import type { CallbackRoute, GeometryScreenPositionHorizontal, ICb, ICbOpt, IDisabledOpt, IGlyph, IGlyphKey, ILoadingOpt, ILyOpt } from "@radroots/apps-lib"; +import type { CallbackPromise } from "@radroots/utils"; + +export type IButtonSimple = ILyOpt & { + label: string; + callback: CallbackPromise; + allow_propogation?: boolean; +}; + +export type IPageHeader<T extends string> = { + label: string; + callback_route?: CallbackRoute<T>; +}; + +export type IPageToolbar<T extends string> = ICbOpt & { + header?: IPageHeader<T>; +}; + +export type IMapMarkerArea = { + show_display?: boolean; + no_drag?: boolean; +} + +export type IGlyphCircle = { + classes_wrap: string; + glyph: IGlyph +}; + +export type IFloatPage = { + posx: Omit<GeometryScreenPositionHorizontal, "center">; +}; + +export type IButtonNavRound = ICb & IDisabledOpt & ILoadingOpt & IGlyphKey; diff --git a/apps-lib-pwa/src/lib/types/views.ts b/apps-lib-pwa/src/lib/types/views.ts @@ -0,0 +1,9 @@ +import type { CallbackPromise } from "@radroots/utils"; + +export type IViewBasis<T extends object> = { + kv_init_prevent?: boolean; + on_mount?: CallbackPromise; + on_destroy?: CallbackPromise; +} & T; + +export type IViewHomeData = {}; diff --git a/apps-lib-pwa/src/lib/views/root/home.svelte b/apps-lib-pwa/src/lib/views/root/home.svelte @@ -0,0 +1,56 @@ +<script lang="ts"> + import ButtonSimple from "$lib/components/button/button-simple.svelte"; + import LayoutPage from "$lib/components/layout/layout-page.svelte"; + import LayoutView from "$lib/components/layout/layout-view.svelte"; + import NavigationTabs from "$lib/components/navigation/navigation-tabs.svelte"; + import PageToolbar from "$lib/components/navigation/page-toolbar.svelte"; + import type { IViewBasis, IViewHomeData } from "$lib/types/views"; + import { get_context, idb_kv_init_page } from "@radroots/apps-lib"; + + import { handle_err, type CallbackPromise } from "@radroots/utils"; + import { onMount } from "svelte"; + + const { ls } = get_context(`lib`); + + let { + basis, + }: { + basis: IViewBasis<{ + data?: IViewHomeData; + on_handle_farms: CallbackPromise; + on_handle_products: CallbackPromise; + }>; + } = $props(); + + onMount(async () => { + try { + if (!basis.kv_init_prevent) await idb_kv_init_page(); + } catch (e) { + handle_err(e, `on_mount`); + } + }); +</script> + +{#if basis.data} + {@const { data: basis_data } = basis} + <LayoutView> + <PageToolbar + basis={{ + header: { + label: `${$ls(`common.general`)}`, + }, + }} + /> + <LayoutPage> + <ButtonSimple + basis={{ + label: `${$ls(`common.farms`)}`, + callback: async () => { + await basis.on_handle_farms(); + }, + }} + /> + </LayoutPage> + </LayoutView> + <NavigationTabs /> +{/if}