app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit fa505923efd6175d5b239f50ec6abbcee97832cf
parent a5e6cff688200e34f2609cc2ca4034f601bf2d50
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Thu, 29 Aug 2024 11:21:20 +0000

Add nav, add layout view, edit layout trellis

Diffstat:
Msrc/lib/components/layout-trellis.svelte | 2+-
Asrc/lib/components/layout-view.svelte | 37+++++++++++++++++++++++++++++++++++++
Asrc/lib/components/nav.svelte | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/routes/(app)/+layout.svelte | 77+++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/routes/(app)/+page.svelte | 6++++++
Msrc/routes/(app)/models/location-gcs/+page.svelte | 4++--
Msrc/routes/(app)/settings/+page.svelte | 628++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mtailwind.config.ts | 6++++++
8 files changed, 504 insertions(+), 350 deletions(-)

diff --git a/src/lib/components/layout-trellis.svelte b/src/lib/components/layout-trellis.svelte @@ -5,6 +5,6 @@ </script> -<div class={`flex flex-col w-full mb-4 pt-20 pb-h_tabs_lg px-4 gap-4 justify-start items-start overflow-y-scroll scroll-hide`}> +<div class={`flex flex-col w-full pt-2 px-4 pb-6 gap-4`}> {@render children()} </div> diff --git a/src/lib/components/layout-view.svelte b/src/lib/components/layout-view.svelte @@ -0,0 +1,37 @@ +<script lang="ts"> + import { app_layout, app_nav_blur, app_nav_visible, app_tabs_blur, app_tabs_visible } from "$lib/stores"; + import { type AppLayoutKey, type PropChildren } from "@radroots/svelte-lib"; + + const styles: Record<AppLayoutKey, string> = { + base: 'pt-2', + lg:'pt-16' + }; + + let { children }: PropChildren = $props(); + + let el: HTMLElement | null; + + let classes_nav = $derived($app_nav_visible ? `pt-h_nav_${$app_layout}` : `${styles[$app_layout]}`) + let classes_tabs = $derived($app_tabs_visible ? `pb-h_tabs_${$app_layout}` : ``) + + const scrollChange = (): void => { + if (Math.max(el?.scrollTop || 0, 0) > 10) app_nav_blur.set(true); + else app_nav_blur.set(false); + + if (Math.max(el?.scrollTop || 0, 0) > 10) app_tabs_blur.set(true); + else app_tabs_blur.set(false); + }; + + $effect(() => { + el?.addEventListener("scroll", scrollChange); + + return () => el?.removeEventListener("scroll", scrollChange); + }); +</script> + +<div + bind:this={el} + class={`absolute top-0 left-0 flex flex-col h-[100vh] w-full overflow-y-scroll scroll-hide ${classes_nav} ${classes_tabs}`} +> + {@render children()} +</div> diff --git a/src/lib/components/nav.svelte b/src/lib/components/nav.svelte @@ -0,0 +1,94 @@ +<script lang="ts"> + import { goto } from "$app/navigation"; + import { app_layout, app_nav, app_nav_blur } from "$lib/stores"; + import { + encode_qp, + fill as Fill, + glyph as Glyph, + } from "@radroots/svelte-lib"; + + let el: HTMLElement | null; + let el_inner: HTMLElement | null; + + let previous_route = $state(``); + let previous_param = $state(``); + let previous_label = $state(``); + + let title_label = $state(``); + + app_nav.subscribe((app_nav) => { + if (app_nav.length) { + const previous = app_nav[app_nav.length - 1]; + if (previous) { + previous_route = previous.route; + if (previous.label) previous_label = previous.label; + if (previous.params) + previous_param = encode_qp(previous.params); + } + } + }); + + const handle_previous = async (): Promise<void> => { + try { + const url = `${previous_route || `/`}${previous_param || ``}`; + await goto(url); + } catch (e) { + console.log(`(error) handle_previous `, e); + } + }; +</script> + +<div + bind:this={el} + class={`z-10 absolute top-0 left-0 flex flex-col w-full justify-start items-start transition-all duration-[250ms] h-nav_${$app_layout} ${$app_nav_blur ? `bg-layer-0-surface-blur/30 backdrop-blur-md` : ``}`} +> + <div + bind:this={el_inner} + class={`relative flex flex-col h-full w-full justify-end items-center bg-transparent`} + > + <div + class={`absolute bottom-[5px] left-0 grid grid-cols-12 flex flex-row h-8 w-full justify-start items-center`} + > + <button + class={`col-span-4 flex flex-row h-full pl-2 justify-start items-center`} + onclick={async () => { + await handle_previous(); + }} + > + <Glyph + basis={{ + key: `caret-left`, + weight: `bold`, + dim: `md+`, + classes: `text-layer-1-glyph-hl group-active:opacity-70 transition-opacity`, + }} + /> + {#if previous_label} + <p + class={`font-sans text-navPrevious text-layer-1-glyph-hl group-active:opacity-60 transition-opacity`} + > + {previous_label} + </p> + {:else} + <Fill /> + {/if} + </button> + <div + class={`col-span-4 flex flex-row h-full justify-center items-center`} + > + {#if title_label} + <p class={`font-sans text-navCurrent text-layer-1-glyph`}> + {title_label} + </p> + {:else} + <Fill /> + {/if} + </div> + <div + class={`col-span-4 flex flex-row h-full justify-end items-center`} + > + <Fill /> + </div> + </div> + </div> +</div> diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte @@ -1,7 +1,8 @@ <script lang="ts"> import { goto } from "$app/navigation"; + import Nav from "$lib/components/nav.svelte"; import Tabs from "$lib/components/tabs.svelte"; - import { app_layout } from "$lib/stores"; + import { app_layout, app_nav_visible, app_tabs_visible } from "$lib/stores"; import { type PropChildren } from "@radroots/svelte-lib"; let { children }: PropChildren = $props(); @@ -9,38 +10,42 @@ </script> {@render children()} -<Tabs -basis={{ - tab_active, - app_layout: $app_layout, - list: [ - { - icon: `house-line`, - callback: async (tab_i) => { - tab_active = tab_i; - await goto("/"); - }, - }, - { - icon: `key`, - callback: async (tab_i) => { - tab_active = tab_i; - }, - }, - { - icon: `network`, - callback: async (tab_i) => { - tab_active = tab_i; - }, - }, - { - icon: `bell-simple`, - callback: async (tab_i) => { - tab_active = tab_i; - await goto("/settings"); - }, - }, - ], -}} -/> - +{#if $app_nav_visible} + <Nav /> +{/if} +{#if $app_tabs_visible} + <Tabs + basis={{ + tab_active, + app_layout: $app_layout, + list: [ + { + icon: `house-line`, + callback: async (tab_i) => { + tab_active = tab_i; + await goto("/"); + }, + }, + { + icon: `key`, + callback: async (tab_i) => { + tab_active = tab_i; + }, + }, + { + icon: `network`, + callback: async (tab_i) => { + tab_active = tab_i; + }, + }, + { + icon: `bell-simple`, + callback: async (tab_i) => { + tab_active = tab_i; + await goto("/settings"); + }, + }, + ], + }} + /> +{/if} diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte @@ -1,7 +1,13 @@ <script lang="ts"> import { goto } from "$app/navigation"; import { cl } from "$lib/client"; + import { app_nav_visible, app_tabs_visible } from "$lib/stores"; import { t } from "@radroots/svelte-lib"; + + $effect(() => { + app_nav_visible.set(false); + app_tabs_visible.set(true); + }); </script> <div class={`flex flex-col w-full pt-16 gap-8 justify-center items-center`}> diff --git a/src/routes/(app)/models/location-gcs/+page.svelte b/src/routes/(app)/models/location-gcs/+page.svelte @@ -124,7 +124,7 @@ {#each locations_all as location} <div class={`flex flex-col justify-center items-center`}> <pre - class={`glyph font-sans font-[400] text-layer-0-glyph`}>{JSON.stringify( + class={`font-sans font-[400] text-layer-0-glyph`}>{JSON.stringify( location, null, 4, @@ -132,7 +132,7 @@ </div> {/each} {:else} - <p class={`glyph font-sans font-[400] text-layer-0-glyph`}> + <p class={`font-sans font-[400] text-layer-0-glyph`}> {"No locations saved"} </p> {/if} diff --git a/src/routes/(app)/settings/+page.svelte b/src/routes/(app)/settings/+page.svelte @@ -2,372 +2,378 @@ import { goto } from "$app/navigation"; import { cl } from "$lib/client"; import LayoutTrellis from "$lib/components/layout-trellis.svelte"; + import LayoutView from "$lib/components/layout-view.svelte"; import { _cf } from "$lib/conf"; - import { app_thc } from "$lib/stores"; - import { toggle_color_mode, trellis } from "@radroots/svelte-lib"; + import { app_nav_visible, app_tabs_visible, app_thc } from "$lib/stores"; + import { + toggle_color_mode, + trellis as Trellis, + } from "@radroots/svelte-lib"; + + $effect(() => { + app_nav_visible.set(true); + app_tabs_visible.set(false); + }); </script> -<svelte:component this={LayoutTrellis}> - <svelte:component - this={trellis} - basis={{ - args: { - layer: 1, - title: { - value: `Appearance`, - }, - list: [ - { - offset: { - mod: { - key: `caret-left`, - }, - }, - touch: { - label: { - left: [ - { - value: "Back", - }, - ], - }, - callback: async () => { - await goto(`/`); - }, - }, +<LayoutView> + <LayoutTrellis> + <Trellis + basis={{ + args: { + layer: 1, + title: { + value: `Appearance`, }, - { - hide_active: true, - touch: { - label: { - left: [ - { - value: `Toggle Color Mode (${toggle_color_mode($app_thc)})`, - classes: `capitalize`, - }, - ], + list: [ + { + offset: { + mod: { + key: `caret-left`, + }, }, - callback: async () => { - await cl.haptics.impact(); - app_thc.set(toggle_color_mode($app_thc)); + touch: { + label: { + left: [ + { + value: "Back", + }, + ], + }, + callback: async () => { + await goto(`/`); + }, }, }, - }, - ], - }, - }} - /> - <svelte:component - this={trellis} - basis={{ - args: { - layer: 1, - title: { - value: `Nostr Keys`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Nostr Key (public)`, - classes: `capitalize`, - }, - ], - }, - end: { - icon: { - key: `caret-right`, + { + hide_active: true, + touch: { + label: { + left: [ + { + value: `Toggle Color Mode (${toggle_color_mode($app_thc)})`, + classes: `capitalize`, + }, + ], + }, + callback: async () => { + await cl.haptics.impact(); + app_thc.set(toggle_color_mode($app_thc)); }, - }, - callback: async () => { - const public_key = await cl.preferences.get( - _cf.pref_key_active, - ); - await cl.dialog.alert( - `Hi! This is your nostr public key ${public_key}`, - ); }, }, + ], + }, + }} + /> + <Trellis + basis={{ + args: { + layer: 1, + title: { + value: `Nostr Keys`, }, - { - touch: { - label: { - left: [ - { - value: `Nostr Key (secret)`, - classes: `capitalize`, + list: [ + { + touch: { + label: { + left: [ + { + value: `Nostr Key (public)`, + classes: `capitalize`, + }, + ], + }, + end: { + icon: { + key: `caret-right`, }, - ], - }, - end: { - icon: { - key: `caret-right`, }, - }, - callback: async () => { - const public_key = await cl.preferences.get( - _cf.pref_key_active, - ); - const secret_key = await cl.keystore.get( - `nostr:key:${public_key}`, - ); - await cl.dialog.alert( - `Hi! This is your nostr secret key ${secret_key}`, - ); + callback: async () => { + const public_key = await cl.preferences.get( + _cf.pref_key_active, + ); + await cl.dialog.alert( + `Hi! This is your nostr public key ${public_key}`, + ); + }, }, }, - }, - ], - }, - }} - /> - <svelte:component - this={trellis} - basis={{ - args: { - layer: 1, - title: { - value: `Configuration`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Reset Device`, - classes: `capitalize`, + { + touch: { + label: { + left: [ + { + value: `Nostr Key (secret)`, + classes: `capitalize`, + }, + ], + }, + end: { + icon: { + key: `caret-right`, }, - ], - }, - end: { - icon: { - key: `caret-right`, }, - }, - callback: async () => { - const confirm = await cl.dialog.confirm( - `Hi! This will delete your saved keys.`, - ); - if (confirm) { + callback: async () => { const public_key = await cl.preferences.get( _cf.pref_key_active, ); - await cl.keystore.remove( + const secret_key = await cl.keystore.get( `nostr:key:${public_key}`, ); - await cl.preferences.remove( - _cf.pref_key_active, + await cl.dialog.alert( + `Hi! This is your nostr secret key ${secret_key}`, ); - await cl.window.splash_show(); - location.reload(); - } + }, }, }, - }, - ], - }, - }} - /> - <svelte:component - this={trellis} - basis={{ - args: { - layer: 1, - title: { - value: `Share`, + ], }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Geolocation Current`, - classes: `capitalize`, + }} + /> + <Trellis + basis={{ + args: { + layer: 1, + title: { + value: `Configuration`, + }, + list: [ + { + touch: { + label: { + left: [ + { + value: `Reset Device`, + classes: `capitalize`, + }, + ], + }, + end: { + icon: { + key: `caret-right`, }, - ], - }, - callback: async () => { - const pos = await cl.geo.current(); - await cl.dialog.alert(JSON.stringify(pos)); + }, + callback: async () => { + const confirm = await cl.dialog.confirm( + `Hi! This will delete your saved keys.`, + ); + if (confirm) { + const public_key = + await cl.preferences.get( + _cf.pref_key_active, + ); + await cl.keystore.remove( + `nostr:key:${public_key}`, + ); + await cl.preferences.remove( + _cf.pref_key_active, + ); + await cl.window.splash_show(); + location.reload(); + } + }, }, }, - }, - ], - }, - }} - /> - <svelte:component - this={trellis} - basis={{ - args: { - layer: 1, - title: { - value: `Haptics`, + ], }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Haptics Touch (less)`, - classes: `capitalize`, - }, - ], - }, - end: { - icon: { - key: `caret-right`, + }} + /> + <Trellis + basis={{ + args: { + layer: 1, + title: { + value: `Share`, + }, + list: [ + { + touch: { + label: { + left: [ + { + value: `Geolocation Current`, + classes: `capitalize`, + }, + ], + }, + callback: async () => { + const pos = await cl.geo.current(); + await cl.dialog.alert(JSON.stringify(pos)); }, - }, - callback: async () => { - await cl.haptics.impact("less"); }, }, + ], + }, + }} + /> + <Trellis + basis={{ + args: { + layer: 1, + title: { + value: `Haptics`, }, - { - touch: { - label: { - left: [ - { - value: `Haptics Touch (more)`, - classes: `capitalize`, + list: [ + { + touch: { + label: { + left: [ + { + value: `Haptics Touch (less)`, + classes: `capitalize`, + }, + ], + }, + end: { + icon: { + key: `caret-right`, }, - ], - }, - end: { - icon: { - key: `caret-right`, }, - }, - callback: async () => { - await cl.haptics.impact("more"); + callback: async () => { + await cl.haptics.impact("less"); + }, }, }, - }, - { - touch: { - label: { - left: [ - { - value: `Haptics Vibrate (500ms)`, - classes: `capitalize`, + { + touch: { + label: { + left: [ + { + value: `Haptics Touch (more)`, + classes: `capitalize`, + }, + ], + }, + end: { + icon: { + key: `caret-right`, }, - ], - }, - end: { - icon: { - key: `caret-right`, }, - }, - callback: async () => { - await cl.haptics.vibrate(500); + callback: async () => { + await cl.haptics.impact("more"); + }, }, }, - }, - { - touch: { - label: { - left: [ - { - value: `Haptics Selection Start`, - classes: `capitalize`, + { + touch: { + label: { + left: [ + { + value: `Haptics Vibrate (500ms)`, + classes: `capitalize`, + }, + ], + }, + end: { + icon: { + key: `caret-right`, }, - ], - }, - end: { - icon: { - key: `caret-right`, }, - }, - callback: async () => { - await cl.haptics.selection_start(); + callback: async () => { + await cl.haptics.vibrate(500); + }, }, }, - }, - { - touch: { - label: { - left: [ - { - value: `Haptics Selection End`, - classes: `capitalize`, + { + touch: { + label: { + left: [ + { + value: `Haptics Selection Start`, + classes: `capitalize`, + }, + ], + }, + end: { + icon: { + key: `caret-right`, }, - ], - }, - end: { - icon: { - key: `caret-right`, }, - }, - callback: async () => { - await cl.haptics.selection_end(); + callback: async () => { + await cl.haptics.selection_start(); + }, }, }, - }, - ], - }, - }} - /> - <svelte:component - this={trellis} - basis={{ - args: { - layer: 1, - title: { - value: `Share`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Share Test #1`, - classes: `capitalize`, + { + touch: { + label: { + left: [ + { + value: `Haptics Selection End`, + classes: `capitalize`, + }, + ], + }, + end: { + icon: { + key: `caret-right`, }, - ], - }, - callback: async () => { - const res = await cl.share.open({ - title: `Radroots Home Page`, - text: `Find farmers around the world.`, - url: `https://radroots.org`, - dialog_title: `This is the dialog title`, - }); - - console.log(`res `, res); + }, + callback: async () => { + await cl.haptics.selection_end(); + }, }, }, + ], + }, + }} + /> + <Trellis + basis={{ + args: { + layer: 1, + title: { + value: `Share`, }, - { - touch: { - label: { - left: [ - { - value: `Share Test #2`, - classes: `capitalize`, - }, - ], + list: [ + { + touch: { + label: { + left: [ + { + value: `Share Test #1`, + classes: `capitalize`, + }, + ], + }, + callback: async () => { + const res = await cl.share.open({ + title: `Radroots Home Page`, + text: `Find farmers around the world.`, + url: `https://radroots.org`, + dialog_title: `This is the dialog title`, + }); + + console.log(`res `, res); + }, }, - callback: async () => { - const res = await cl.share.open({ - title: `Radroots Home Page`, - url: `https://radroots.org`, - dialog_title: `This is the dialog title`, - files: [`file-1.png`, `file-2.png`], - }); + }, + { + touch: { + label: { + left: [ + { + value: `Share Test #2`, + classes: `capitalize`, + }, + ], + }, + callback: async () => { + const res = await cl.share.open({ + title: `Radroots Home Page`, + url: `https://radroots.org`, + dialog_title: `This is the dialog title`, + files: [`file-1.png`, `file-2.png`], + }); - console.log(`res `, res); + console.log(`res `, res); + }, }, }, - }, - ], - }, - }} - /> -</svelte:component> + ], + }, + }} + /> + </LayoutTrellis> +</LayoutView> diff --git a/tailwind.config.ts b/tailwind.config.ts @@ -8,6 +8,8 @@ const heights = { line: `46px`, tabs_base: `64px`, tabs_lg: `88px`, + nav_base: `64px`, + nav_lg: `100px`, }; const widths = { @@ -32,6 +34,10 @@ const config: Config = { fontSize: { line: ["1.05rem", { lineHeight: "1.33rem", fontWeight: 300 }], trellisTitle: ["0.8rem", { lineHeight: "1rem", fontWeight: 200 }], + trellisLine: ["1.05rem", { lineHeight: "1.33rem", fontWeight: 300 }], + trellisLabel: ["0.8rem", { lineHeight: "1rem", fontWeight: 200 }], + navPrevious: ["1.09rem", { lineHeight: "1.33rem", fontWeight: 400 }], + navCurrent: ["1.09rem", { lineHeight: "1.33rem", fontWeight: 500 }], }, height: { ...heights,