commit 39bbd067ee7b042efed196359fee679593a54b7c
parent 09be0dd9513e61e8ed3966426b510b4242c31e2f
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Mon, 10 Feb 2025 10:20:52 +0000
apps-lib: refactor library for app/0.0.1
Diffstat:
205 files changed, 6981 insertions(+), 5789 deletions(-)
diff --git a/apps-lib/.gitignore b/apps-lib/.gitignore
@@ -18,20 +18,30 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
-# local env files
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
# turbo
.turbo
-.dev*
+
+# Output
+.output
+/build
+dist
+
+# local env files
+.env*
+!.env.example
+
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
+
+# local
.tmp*
+.backup*
+.dev*
.vscode
notes*.txt
notes*.md
-justfile
-dist
git-diff.txt
+justfile
diff --git a/apps-lib/README.md b/apps-lib/README.md
@@ -1 +1 @@
-# svelte-lib
+# lib-app
diff --git a/apps-lib/package.json b/apps-lib/package.json
@@ -1,60 +1,61 @@
{
- "name": "@radroots/svelte-lib",
- "version": "0.0.0",
- "private": true,
- "license": "GPLv3",
- "type": "module",
- "files": [
- "dist",
- "!dist/**/*.test.*",
- "!dist/**/*.spec.*"
- ],
- "exports": {
- ".": {
- "types": "./dist/index.d.ts",
- "svelte": "./dist/index.js"
- }
- },
- "svelte": "./dist/index.js",
- "types": "./dist/index.d.ts",
- "scripts": {
- "dev": "svelte-package -w",
- "prebuild": "just build",
- "build": "npm run check && svelte-package",
- "preview": "vite preview",
- "package": "svelte-kit sync && svelte-package && publint",
- "prepublishOnly": "npm run package",
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
- },
- "peerDependencies": {
- "@sveltejs/kit": "^2.0.0",
- "svelte": "^4.2.19"
- },
- "devDependencies": {
- "@sveltejs/kit": "^2.0.0",
- "@sveltejs/package": "^2.0.0",
- "@types/luxon": "^3.4.2",
- "publint": "^0.1.9",
- "svelte": "^4.2.19",
- "svelte-check": "^3.8.6",
- "svelte-preprocess": "^5.1.0",
- "tslib": "^2.4.1",
- "typescript": "^5.3.3",
- "vite": "^5.0.11"
- },
- "dependencies": {
- "@nostr-dev-kit/ndk": "^2.10.7",
- "@nostr-dev-kit/ndk-cache-dexie": "^2.5.8",
- "@nostr-dev-kit/ndk-svelte": "^2.3.2",
- "@radroots/theme": "workspace:*",
- "@radroots/svelte-maplibre": "workspace:*",
- "@sveltekit-i18n/base": "^1.3.7",
- "@sveltekit-i18n/parser-icu": "^1.0.8",
- "luxon": "^3.5.0",
- "sveltekit-search-params": "^3.0.0"
- },
- "publishConfig": {
- "access": "public"
- }
+ "name": "@radroots/lib-app",
+ "version": "0.0.0",
+ "private": true,
+ "license": "GPLv3",
+ "scripts": {
+ "dev": "svelte-package -w",
+ "prebuild": "npm run check",
+ "build": "svelte-package",
+ "preview": "vite preview",
+ "prepare": "svelte-kit sync || echo ''",
+ "prepack": "svelte-kit sync && svelte-package && publint",
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
+ },
+ "files": [
+ "dist",
+ "!dist/**/*.test.*",
+ "!dist/**/*.spec.*"
+ ],
+ "sideEffects": [
+ "**/*.css"
+ ],
+ "svelte": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "svelte": "./dist/index.js"
+ }
+ },
+ "peerDependencies": {
+ "@sveltejs/kit": "^2.16.0",
+ "svelte": "^5.0.0"
+ },
+ "devDependencies": {
+ "@sveltejs/adapter-auto": "^4.0.0",
+ "@sveltejs/kit": "^2.16.0",
+ "@sveltejs/package": "^2.0.0",
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
+ "@types/node": "^22.5.0",
+ "publint": "^0.3.2",
+ "svelte": "^5.0.0",
+ "svelte-check": "^4.0.0",
+ "typescript": "^5.0.0",
+ "vite": "^6.0.0"
+ },
+ "dependencies": {
+ "@nostr-dev-kit/ndk": "^2.11.0",
+ "@nostr-dev-kit/ndk-cache-dexie": "^2.5.9",
+ "@nostr-dev-kit/ndk-svelte": "^2.4.0",
+ "@radroots/theme": "workspace:*",
+ "@radroots/util": "workspace:*",
+ "@radroots/svelte-maplibre": "workspace:*",
+ "@sveltekit-i18n/base": "^1.3.7",
+ "@sveltekit-i18n/parser-icu": "^1.0.8",
+ "luxon": "^3.5.0",
+ "sveltekit-search-params": "^3.0.0"
+ }
}
\ No newline at end of file
diff --git a/apps-lib/src/lib/global.d.ts b/apps-lib/src/global.d.ts
diff --git a/apps-lib/src/lib/component/button/button-arrow.svelte b/apps-lib/src/lib/component/button/button-arrow.svelte
@@ -1,58 +0,0 @@
-<script lang="ts">
- import { Glyph, type CallbackPromise } from "$lib";
- import { onDestroy, onMount } from "svelte";
-
- export let basis: {
- label: string;
- callback: CallbackPromise;
- };
-
- let visible_action = false;
-
- onMount(async () => {
- try {
- document.addEventListener("click", () => toggle(false));
- } catch (e) {
- } finally {
- }
- });
-
- onDestroy(() => {
- document.removeEventListener("click", () => toggle(false));
- });
-
- const toggle = (toggle_force?: boolean): void => {
- visible_action =
- typeof toggle_force === `boolean` ? toggle_force : !visible_action;
- };
-</script>
-
-<div class={`relative inline-block`}>
- <button
- class={`flex flex-row w-auto h-10 gap-1 justify-center items-center el-re`}
- on:click|stopPropagation={async () => {
- toggle();
- }}
- >
- <button
- class={`${visible_action ? `fade-in` : `hidden`} absolute top-0 left-0 flex flex-row h-full justify-start items-center el-re`}
- on:click|stopPropagation={async () => {
- await basis.callback();
- }}
- >
- <Glyph
- basis={{
- classes: `text-layer-0-glyph`,
- dim: `sm`,
- weight: `bold`,
- key: `arrow-left`,
- }}
- />
- </button>
- <p
- class={`font-circ font-[700] text-layer-0-glyph text-[1.6rem] tracking-tight ${visible_action ? `translate-x-8` : ``} duration-[350ms] el-re`}
- >
- {basis.label || ``}
- </p>
- </button>
-</div>
diff --git a/apps-lib/src/lib/component/button/button-layout-pair.svelte b/apps-lib/src/lib/component/button/button-layout-pair.svelte
@@ -1,55 +0,0 @@
-<script lang="ts">
- import {
- app_layout,
- ButtonLayout,
- Empty,
- type CallbackPromise,
- type IDisabledOpt,
- } from "$lib";
-
- export let basis: {
- continue: IDisabledOpt & {
- label: string;
- callback: CallbackPromise;
- };
- back?: IDisabledOpt & {
- visible: boolean;
- label?: string;
- callback: CallbackPromise;
- };
- };
-</script>
-
-<div class={`flex flex-col justify-center items-center`}>
- <ButtonLayout
- basis={{
- disabled: basis.continue.disabled,
- label: basis.continue.label,
- callback: basis.continue.callback,
- }}
- ></ButtonLayout>
- {#if basis.back}
- <div class={`flex flex-col justify-center items-center el-re`}>
- {#if basis.back.visible}
- <button
- class={`group flex flex-row h-12 w-${$app_layout} justify-center items-center fade-in`}
- on:click|stopPropagation={async () => {
- if (!basis.back?.disabled) await basis.back?.callback();
- }}
- >
- <p
- class={`font-sans font-[600] tracking-wide text-layer-1-glyph-shade ${basis.back?.disabled ? `` : `group-active:text-layer-1-glyph/40`} el-re`}
- >
- {basis.back.label || ``}
- </p>
- </button>
- {:else}
- <div
- class={`flex flex-row h-4 w-full justify-start items-center`}
- >
- <Empty></Empty>
- </div>
- {/if}
- </div>
- {/if}
-</div>
diff --git a/apps-lib/src/lib/component/button/button-layout.svelte b/apps-lib/src/lib/component/button/button-layout.svelte
@@ -1,39 +0,0 @@
-<script lang="ts">
- import {
- app_layout,
- fmt_cl,
- parse_layer,
- type CallbackPromise,
- type IClOpt,
- type IDisabledOpt,
- type ILyOpt,
- } from "$lib";
-
- export let basis: ILyOpt &
- IClOpt &
- IDisabledOpt & {
- classes_inner?: string;
- hide_active?: boolean;
- label: string;
- callback: CallbackPromise;
- };
-
- $: layer = parse_layer(basis.layer, 1);
-
- $: classes_active = !basis.hide_active
- ? `layer-1-active-surface layer-1-active-raise-less layer-1-active-ring-less`
- : ``;
-</script>
-
-<button
- class={`${fmt_cl(basis.classes)} group flex flex-row h-touch_guide w-${$app_layout} justify-center items-center bg-layer-${layer}-surface rounded-touch ${basis.disabled ? `opacity-60` : classes_active} el-re`}
- on:click|stopPropagation={async () => {
- if (!basis.disabled) await basis.callback();
- }}
->
- <p
- class={`${fmt_cl(basis.classes_inner)} font-sans font-[600] tracking-wide text-layer-${layer}-glyph-shade ${basis.disabled ? `` : `group-active:text-layer-${layer}-glyph/40 `} el-re`}
- >
- {basis.label || ``}
- </p>
-</button>
diff --git a/apps-lib/src/lib/component/carousel/carousel-item.svelte b/apps-lib/src/lib/component/carousel/carousel-item.svelte
@@ -1,6 +0,0 @@
-<script lang="ts">
-</script>
-
-<div class={`flex flex-col flex-shrink-0 w-[100vw] justify-start items-center`}>
- <slot />
-</div>
diff --git a/apps-lib/src/lib/component/carousel/carousel.svelte b/apps-lib/src/lib/component/carousel/carousel.svelte
@@ -1,12 +0,0 @@
-<script lang="ts">
- import { casl_index } from "$lib";
-</script>
-
-<div class={`relative flex flex-col w-full`}>
- <div
- class={`flex transition-transform duration-500`}
- style={`transform: translateX(-${Math.max($casl_index, 0) * 100}vw)`}
- >
- <slot />
- </div>
-</div>
diff --git a/apps-lib/src/lib/component/entry/entry-line.svelte b/apps-lib/src/lib/component/entry/entry-line.svelte
@@ -1,56 +0,0 @@
-<script lang="ts">
- import {
- EntryWrap,
- Glyph,
- Input,
- LoadSymbol,
- parse_layer,
- type IEntryLine,
- type LoadingDimension,
- } from "$lib";
-
- export let basis: IEntryLine;
-
- $: layer =
- typeof basis.wrap?.layer === `boolean`
- ? parse_layer(0)
- : parse_layer(basis.wrap?.layer);
- $: classes_layer =
- typeof basis.wrap?.layer === `boolean`
- ? ``
- : `text-layer-${layer}-glyph`;
- let loading_dim: LoadingDimension = `sm`;
- $: loading_dim = basis.wrap?.style === `guide` ? `md` : `sm`;
-</script>
-
-<EntryWrap basis={basis?.wrap}>
- <Input basis={basis.el}></Input>
- {#if basis.loading}
- <div
- class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-4 justify-end items-center fade-in el-re`}
- >
- <LoadSymbol
- basis={{
- dim: loading_dim,
- }}
- />
- </div>
- {:else if basis.notify_inline}
- {#if `glyph` in basis.notify_inline}
- <div
- class={`z-5 absolute el-re right-0 top-0 flex flex-row h-full pr-3 justify-end items-center translate-x-[34px] fade-in`}
- >
- <Glyph
- basis={typeof basis.notify_inline.glyph === `string`
- ? {
- key: basis.notify_inline.glyph,
- dim: `xs+`,
- weight: `bold`,
- classes: `${classes_layer}`,
- }
- : basis.notify_inline.glyph}
- ></Glyph>
- </div>
- {/if}
- {/if}
-</EntryWrap>
diff --git a/apps-lib/src/lib/component/entry/entry-wrap.svelte b/apps-lib/src/lib/component/entry/entry-wrap.svelte
@@ -1,21 +0,0 @@
-<script lang="ts">
- import { fmt_cl, parse_layer, type IEntryWrap } from "$lib";
-
- export let basis: IEntryWrap | undefined = undefined;
-
- $: layer =
- typeof basis?.layer === `boolean`
- ? parse_layer(0)
- : parse_layer(basis?.layer);
- $: classes_layer =
- typeof basis?.layer === `boolean`
- ? `bg-transparent`
- : `bg-layer-${layer}-surface ${basis?.style_a ? `active:bg-layer-${layer}-surface_a` : ``}`; //text-layer-${layer}-glyph_d
-</script>
-
-<button
- id={basis?.id || null}
- class={`${fmt_cl(basis?.classes)} relative entry-line-wrap ${!basis.no_pad ? ` pl-6 pr-4` : ``} h-entry_${basis?.style ? basis.style : `line`} rounded-touch ${classes_layer} el-re`}
->
- <slot />
-</button>
diff --git a/apps-lib/src/lib/component/float/float-page-button.svelte b/apps-lib/src/lib/component/float/float-page-button.svelte
@@ -1,37 +0,0 @@
-<script lang="ts">
- import {
- Glyph,
- LoadSymbol,
- type GeometryScreenPositionHorizontal,
- type ICb,
- type IGlyphKey,
- type ILoadingOpt,
- } from "$lib";
-
- export let basis: ICb &
- ILoadingOpt &
- IGlyphKey & {
- posx: Omit<GeometryScreenPositionHorizontal, "center">;
- };
-</script>
-
-<div class={`absolute top-16 ${basis.posx}-6 flex flex-row`}>
- <button
- class={`flex flex-row h-12 w-12 justify-center items-center bg-layer-1-surface rounded-full el-re`}
- on:click={basis.callback}
- >
- {#if basis.loading}
- <LoadSymbol />
- {:else}
- <Glyph
- basis={{
- classes: `text-layer-0-glyph`,
- dim: `sm+`,
- weight: `bold`,
- key: basis.glyph,
- }}
- ></Glyph>
- {/if}
- </button>
-</div>
-<div class="hidden left-6 right-6"></div>
diff --git a/apps-lib/src/lib/component/float/float-tabs.svelte b/apps-lib/src/lib/component/float/float-tabs.svelte
@@ -1,101 +0,0 @@
-<script lang="ts">
- import { goto } from "$app/navigation";
- import { page } from "$app/stores";
- import { Empty, Glyph } from "$lib";
-</script>
-
-<div
- class={`absolute bottom-0 left-0 h-24 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-layer-1-surface rounded-full backdrop-blur-lg`}
- >
- <button
- class={`col-span-1 flex flex-row justify-center items-center`}
- on:click={async () => {
- await goto(`/`);
- }}
- >
- <Glyph
- basis={{
- classes: `text-[26px] text-layer-0-glyph/80 rotate-90`,
- weight: $page.url.pathname !== `/` ? `bold` : `fill`,
- key: `columns`,
- }}
- ></Glyph>
- </button>
- <button
- class={`relative col-span-1 flex flex-row justify-center items-center`}
- on:click={async () => {
- await goto(`/search`);
- }}
- >
- <Glyph
- basis={{
- classes: `text-[24px] text-layer-0-glyph/80`,
- weight: $page.url.pathname.startsWith(`/search`)
- ? `fill`
- : `bold`,
- key: `magnifying-glass`,
- }}
- ></Glyph>
- </button>
- <button
- class={`relative col-span-1 flex flex-row justify-center items-center`}
- on:click={async () => {
- goto(`/settings/profile`);
- }}
- >
- <Glyph
- basis={{
- classes: `text-[24px] text-layer-0-glyph/80`,
- weight: $page.url.pathname.startsWith(
- `/settings/profile`,
- )
- ? `fill`
- : `bold`,
- key: `user`,
- }}
- ></Glyph>
- </button>
- <button
- class={`relative col-span-1 flex flex-row h-full justify-center items-center`}
- on:click={async () => {
- await goto(`/notifications`);
- }}
- >
- <Glyph
- basis={{
- classes: `text-[24px] text-layer-0-glyph/80`,
- weight: $page.url.pathname.startsWith(`/notifications`)
- ? `fill`
- : `bold`,
- key: `bell`,
- }}
- ></Glyph>
- <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`}
- >
- <Empty></Empty>
- </div>
- </div>
- </button>
- </div>
- <button
- class={`flex flex-row h-[3.1rem] w-[3.1rem] justify-center items-center bg-layer-1-surface rounded-full backdrop-blur-lg`}
- on:click={async () => {}}
- >
- <Glyph
- basis={{
- classes: `text-[22px] text-layer-0-glyph/80`,
- weight: `bold`,
- key: `plus`,
- }}
- ></Glyph>
- </button>
- </div>
-</div>
diff --git a/apps-lib/src/lib/component/glyph/glyph-button-simple.svelte b/apps-lib/src/lib/component/glyph/glyph-button-simple.svelte
@@ -1,40 +0,0 @@
-<script lang="ts">
- import {
- fmt_cl,
- Glyph,
- type CallbackPromise,
- type GlyphKey,
- type IClOpt,
- } from "$lib";
-
- export let basis: IClOpt & {
- kind?: `primary` | `neutral`;
- label: string;
- callback: CallbackPromise;
- glyph?: GlyphKey;
- };
-
- $: classes_kind =
- basis.kind === `neutral`
- ? `text-layer-0-glyph-shade`
- : `text-layer-0-glyph-hl`;
-</script>
-
-<button
- class={`${fmt_cl(basis.classes)} group flex flex-row justify-center items-center`}
- on:click={basis.callback}
->
- {#if basis.glyph}
- <Glyph
- basis={{
- classes: `${classes_kind}`,
- dim: `sm+`,
- weight: `bold`,
- key: basis.glyph,
- }}
- />
- {/if}
- <p class={`font-sans font-[600] text-guide ${classes_kind} opacity-active`}>
- {basis.label}
- </p>
-</button>
diff --git a/apps-lib/src/lib/component/glyph/glyph-button.svelte b/apps-lib/src/lib/component/glyph/glyph-button.svelte
@@ -1,21 +0,0 @@
-<script lang="ts">
- import { fmt_cl, glyph_style_map, type IGlyph } from "$lib";
-
- export let basis: IGlyph;
- $: basis = basis;
-
- $: weight =
- !basis?.weight || basis?.weight === `regular` ? `` : `-${basis.weight}`;
- $: styles = basis?.dim
- ? glyph_style_map.get(basis.dim)
- : glyph_style_map.get(`sm`);
-</script>
-
-<button
- class={`${fmt_cl(basis.classes)} flex flex-col justify-center items-center text-[${styles?.gl_1}px] el-re`}
- on:click={async () => {
- if (basis.callback) await basis.callback();
- }}
->
- <i class={`ph${weight} ph-${basis.key}`}></i>
-</button>
diff --git a/apps-lib/src/lib/component/glyph/glyph-circle.svelte b/apps-lib/src/lib/component/glyph/glyph-circle.svelte
@@ -1,22 +0,0 @@
-<script lang="ts">
- import {
- type IGlyphCircle,
- GlyphButton,
- fmt_cl,
- glyph_style_map,
- } from "$lib";
-
- export let basis: IGlyphCircle;
-
- $: styles = basis?.glyph?.dim
- ? glyph_style_map.get(basis?.glyph?.dim)
- : glyph_style_map.get(`sm`);
-</script>
-
-{#if styles?.dim_1}
- <div
- class={`${fmt_cl(basis?.classes_wrap)} flex flex-col h-[${styles?.dim_1}px] w-[${styles?.dim_1}px] justify-center items-center rounded-full el-re`}
- >
- <GlyphButton basis={basis?.glyph}></GlyphButton>
- </div>
-{/if}
diff --git a/apps-lib/src/lib/component/glyph/glyph-title-select-label.svelte b/apps-lib/src/lib/component/glyph/glyph-title-select-label.svelte
@@ -1,32 +0,0 @@
-<script lang="ts">
- import { ascii } from "$lib";
-
- export let basis: {
- label: string;
- };
-</script>
-
-<div class={`flex flex-row justify-start items-center`}>
- <p
- class={`pr-[13px] font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}
- >
- {`(${basis.label}`}
- </p>
- <div
- class={`relative flex flex-row justify-start items-center -translate-x-[10px] -translate-y-[1px]`}
- >
- <p
- class={`absolute font-sansd text-trellis_ti text-layer-0-glyph-label uppercase scale-y-[70%] scale-x-[80%] -translate-y-[1px]`}
- >
- {`${ascii.up}`}
- </p>
- <p
- class={`absolute font-sansd text-trellis_ti text-layer-0-glyph-label uppercase scale-y-[70%] scale-x-[80%] translate-y-[2px]`}
- >
- {`${ascii.down}`}
- </p>
- </div>
- <p class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}>
- {`)`}
- </p>
-</div>
diff --git a/apps-lib/src/lib/component/label/label-display.svelte b/apps-lib/src/lib/component/label/label-display.svelte
@@ -1,28 +0,0 @@
-<script lang="ts">
- import { fmt_cl, parse_layer, type ILabelDisplay } from "$lib";
-
- export let basis: ILabelDisplay;
- $: basis = basis;
-
- $: layer =
- typeof basis?.layer === `boolean`
- ? false
- : parse_layer(basis?.layer, 1);
- $: classes_layer =
- typeof layer === `boolean`
- ? `bg-transparent`
- : `bg-layer-${layer}-surface`;
- $: clases_style =
- basis.style === `guide` ? `h-entry_guide rounded-touch` : ``;
-</script>
-
-<div
- id={basis.id_wrap || null}
- class={`${fmt_cl(basis.classes)} relative el-re entry-line-wrap px-2 ${classes_layer} ${clases_style}`}
->
- <p
- class={`${fmt_cl(basis.label.classes)} font-sans font-[400] text-layer-0-glyph`}
- >
- {basis.label.value}
- </p>
-</div>
diff --git a/apps-lib/src/lib/component/lib/fade.svelte b/apps-lib/src/lib/component/lib/fade.svelte
@@ -1,11 +0,0 @@
-<script lang="ts">
- import type { IBasisOpt } from "$lib/types/component";
- import type { IClOpt } from "$lib/types/interface";
- import { fmt_cl } from "$lib/util/app";
-
- export let basis: IBasisOpt<IClOpt> = undefined;
-</script>
-
-<div class={`${fmt_cl(basis?.classes)} flex`}>
- <slot />
-</div>
diff --git a/apps-lib/src/lib/component/lib/load-screen.svelte b/apps-lib/src/lib/component/lib/load-screen.svelte
@@ -1,12 +0,0 @@
-<script lang="ts">
- import { LoadSymbol } from "$lib";
- import { fade } from "svelte/transition";
-</script>
-
-<div
- in:fade={{ duration: 200 }}
- out:fade={{ delay: 50, duration: 200 }}
- class={`z-50 absolute top-0 left-0 flex flex-row h-[100vh] w-full justify-center items-center bg-layer-0-surface`}
->
- <LoadSymbol basis={{ dim: `lg` }} />
-</div>
diff --git a/apps-lib/src/lib/component/lib/logo-circle.svelte b/apps-lib/src/lib/component/lib/logo-circle.svelte
@@ -1,41 +0,0 @@
-<script lang="ts">
- let array = [` • `, `radroots`, ` • `, `radroots`];
-</script>
-
-<div
- class={`relative flex flex-col h-[196px] w-full justify-center items-center`}
->
- <div
- class={`relative flex flex-row h-36 w-36 justify-center items-center bg-layer-2-surface rounded-full`}
- >
- <p
- class={`font-sans font-[900] text-6xl text-layer-0-glyph -tracking-[0.4rem] -translate-x-[6px]`}
- >
- {"»`,"}
- </p>
- <p
- class={`font-sans font-[900] text-6xl text-layer-0-glyph translate-x-[8px]`}
- >
- {"-"}
- </p>
- </div>
- {#each array as char, index}
- <div
- class={`char font-sans text-layer-0-glyph/60 text-[0.8rem] text-center uppercase`}
- style="--angle: {`${(1 / array.length) * index + 0.18}turn`}"
- >
- {char}
- </div>
- {/each}
-</div>
-
-<style>
- .char {
- width: 1em;
- height: 100%;
- position: absolute;
- top: 0;
- left: 50%;
- transform: translateX(-50%) rotate(var(--angle, 0deg));
- }
-</style>
diff --git a/apps-lib/src/lib/component/lib/splash-screen.svelte b/apps-lib/src/lib/component/lib/splash-screen.svelte
@@ -1,12 +0,0 @@
-<script lang="ts">
- import { LogoCircle } from "$lib";
- import { fade } from "svelte/transition";
-</script>
-
-<div
- in:fade={{ duration: 200 }}
- out:fade={{ delay: 50, duration: 200 }}
- class={`z-50 absolute top-0 left-0 flex flex-row h-[100vh] w-full justify-center items-center bg-layer-0-surface`}
->
- <LogoCircle />
-</div>
diff --git a/apps-lib/src/lib/component/lib/view.svelte b/apps-lib/src/lib/component/lib/view.svelte
@@ -1,14 +0,0 @@
-<script lang="ts">
- import { fade } from "svelte/transition";
-
- export let key: string;
-</script>
-
-<div
- in:fade={{ duration: 200 }}
- out:fade={{ delay: 50, duration: 200 }}
- data-view={key}
- class={`hidden flex flex-col h-full w-full justify-start items-center`}
->
- <slot />
-</div>
diff --git a/apps-lib/src/lib/component/map/map-marker-dot.svelte b/apps-lib/src/lib/component/map/map-marker-dot.svelte
@@ -1,15 +0,0 @@
-<script lang="ts">
- import { Empty } from "$lib";
-</script>
-
-<div class="flex flex-row p-1">
- <div
- class={`z-20 flex flex-row h-map_circle w-map_circle justify-center items-center bg-white rounded-full shadow-lg`}
- >
- <div
- class={`z-10 flex flex-row h-map_circle_inner w-map_circle_inner justify-center items-center bg-blue-400 rounded-full`}
- >
- <Empty></Empty>
- </div>
- </div>
-</div>
diff --git a/apps-lib/src/lib/component/map/map-point-display.svelte b/apps-lib/src/lib/component/map/map-point-display.svelte
@@ -1,28 +0,0 @@
-<script lang="ts">
- import {
- app_thc,
- cfg_map,
- fmt_cl,
- type GeolocationPoint,
- type IClOpt,
- MapMarkerDot,
- } from "$lib";
- import { MapLibre, Marker } from "@radroots/svelte-maplibre";
-
- export let basis: IClOpt & {
- point: GeolocationPoint;
- zoom?: number;
- };
-</script>
-
-<MapLibre
- center={basis.point}
- zoom={basis.zoom || 4}
- class={`${fmt_cl(basis.classes)} relative aspect-1 w-full`}
- style={cfg_map.styles.base[$app_thc]}
- attributionControl={false}
->
- <Marker bind:lngLat={basis.point} draggable={false}>
- <MapMarkerDot />
- </Marker>
-</MapLibre>
diff --git a/apps-lib/src/lib/component/map/map-point-select.svelte b/apps-lib/src/lib/component/map/map-point-select.svelte
@@ -1,70 +0,0 @@
-<script lang="ts">
- import {
- app_thc,
- cfg_map,
- Empty,
- handle_err,
- MapPopupPointGeolocation,
- type GeocoderReverseResult,
- type GeolocationPoint,
- type IClOpt,
- type ILcGeocodeCallback,
- } from "$lib";
- import { MapLibre, Marker, Popup } from "@radroots/svelte-maplibre";
-
- let map_center: GeolocationPoint = {
- ...cfg_map.coords.default,
- };
- export let map_point: GeolocationPoint = {
- ...cfg_map.coords.default,
- };
- export let map_point_geoc_r: GeocoderReverseResult | undefined = undefined;
-
- export let basis: IClOpt & {
- lc_geocode: ILcGeocodeCallback;
- };
-
- $: if (map_point && map_center.lat === 0 && map_center.lng === 0) {
- map_center = map_point;
- (async () => {
- try {
- map_point_geoc_r = await basis.lc_geocode(map_center);
- } catch (e) {
- await handle_err(e, `map-point-select-lc-geocode-init`);
- }
- })();
- }
-</script>
-
-<MapLibre
- center={map_center}
- zoom={10}
- class={`relative h-full w-full`}
- style={cfg_map.styles.base[$app_thc]}
- attributionControl={false}
->
- <Marker
- bind:lngLat={map_point}
- draggable
- class={`flex flex-row h-map_circle w-map_circle justify-center items-center bg-white rounded-full shadow-lg`}
- on:dragend={async () => {
- if (!map_point) return;
- const geoc_r = await basis.lc_geocode(map_point);
- if (geoc_r) map_point_geoc_r = geoc_r;
- }}
- >
- <div
- class={`flex flex-row h-map_circle_inner w-map_circle_inner justify-center items-center bg-blue-400 rounded-full`}
- >
- <Empty />
- </div>
- <Popup open={true} offset={[0, -10]}>
- <MapPopupPointGeolocation
- basis={{
- point: map_point,
- geoc_r: map_point_geoc_r,
- }}
- />
- </Popup>
- </Marker>
-</MapLibre>
diff --git a/apps-lib/src/lib/component/map/map-popup-point-geolocation.svelte b/apps-lib/src/lib/component/map/map-popup-point-geolocation.svelte
@@ -1,74 +0,0 @@
-<script lang="ts">
- import {
- geol_lat_fmt,
- geol_lng_fmt,
- Glyph,
- type GeocoderReverseResult,
- type GeolocationPoint,
- type IClOpt,
- } from "$lib";
- import Fade from "../lib/fade.svelte";
-
- export let basis: IClOpt & {
- point?: GeolocationPoint;
- geoc_r?: GeocoderReverseResult;
- };
-</script>
-
-<div
- class={`flex flex-col w-fit px-5 py-[0.7rem] justify-start items-start bg-layer-1-surface rounded-2xl shadow-lg`}
->
- <div class={`flex flex-col w-full gap-1 justify-start items-start`}>
- {#if basis.geoc_r}
- <Fade
- basis={{
- classes: `flex-col w-full gap-1 justify-start items-start`,
- }}
- >
- <div class={`flex flex-row gap-1 justify-start items-center`}>
- <p
- class={`font-sans font-[500] text-[0.9rem] text-layer-2-glyph`}
- >
- {basis.geoc_r.name}
- </p>
- <Glyph
- basis={{
- classes: `text-layer-2-glyph -translate-y-[2px]`,
- dim: `xs`,
- weight: `bold`,
- key: `map-pin-simple`,
- }}
- ></Glyph>
- </div>
- <div
- class={`flex flex-row w-full gap-1 justify-start items-center`}
- >
- <p
- class={`font-sans font-[500] text-[0.9rem] tracking-tight text-layer-2-glyph`}
- >
- {`${basis.geoc_r.admin1_name},`}
- </p>
- <p
- class={`font-sans font-[500] text-[0.9rem] tracking-tight text-layer-2-glyph`}
- >
- {`${basis.geoc_r.country_name}`}
- </p>
- </div>
- </Fade>
- {/if}
- {#if basis.point}
- <div class={`flex flex-col w-full justify-start items-start`}>
- <p
- class={`font-sans font-[400] text-[0.9rem] text-layer-0-glyph`}
- >
- {`${geol_lat_fmt(basis.point.lat, `dms`)}`}
- </p>
- <p
- class={`font-sans font-[400] text-[0.9rem] text-layer-0-glyph`}
- >
- {`${geol_lng_fmt(basis.point.lng, `dms`)}`}
- </p>
- </div>
- {/if}
- </div>
-</div>
diff --git a/apps-lib/src/lib/component/nav/nav-option.svelte b/apps-lib/src/lib/component/nav/nav-option.svelte
@@ -1,82 +0,0 @@
-<!-- svelte-ignore a11y-label-has-associated-control -->
-<script lang="ts">
- import {
- fmt_cl,
- Glyph,
- LoadSymbol,
- parse_layer,
- type INavBasisOption,
- } from "$lib";
-
- let el_swap: HTMLLabelElement | null = null;
-
- export let basis: INavBasisOption;
- $: basis = basis;
- $: layer = parse_layer(1);
- $: classes_disabled = basis.disabled ? `opacity-40` : ``;
-</script>
-
-{#if basis?.loading}
- <div class={`flex flex-row pr-4 justify-center items-center`}>
- <LoadSymbol />
- </div>
-{:else}
- <button
- class={`${fmt_cl(basis?.classes)} group col-span-4 flex flex-row h-full justify-end items-center ${classes_disabled}`}
- on:click={async () => {
- if (!basis.disabled) await basis?.callback(el_swap);
- }}
- >
- {#if `glyph` in basis && basis?.glyph}
- <Glyph
- basis={{
- classes: `group-active:opacity-70 ${basis?.glyph?.classes}`,
- ...basis?.glyph,
- }}
- ></Glyph>
- {/if}
- {#if `label` in basis && basis?.label}
- <div
- class={`flex flex-row justify-start items-center ${basis?.label && `glyph` in basis?.label && basis.label?.glyph ? `pr-2` : `pr-4`}`}
- >
- {#if `swap` in basis?.label}
- <label
- bind:this={el_swap}
- class={`swap${basis?.label?.swap?.toggle ? ` swap-active` : ``}`}
- >
- <div class="swap-on">
- <p
- class={`${fmt_cl(basis?.label?.swap?.on.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-70`)} font-sans -translate-y-[1px] el-re`}
- >
- {basis?.label?.swap?.on.value}
- </p>
- </div>
- <div class="swap-off">
- <p
- class={`${fmt_cl(basis?.label?.swap?.off.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-70`)} font-sans -translate-y-[1px] el-re`}
- >
- {basis?.label?.swap?.off.value}
- </p>
- </div>
- </label>
- {:else if `value` in basis?.label}
- <p
- class={`${fmt_cl(basis?.label.classes)} font-sans text-nav_prev text-layer-1-glyph-hl group-active:opacity-70 el-re`}
- >
- {basis?.label.value}
- </p>
- {/if}
- {#if `glyph` in basis?.label}
- <Glyph
- basis={{
- key: basis?.label?.glyph?.key,
- classes: `text-layer-1-glyph-hl group-active:opacity-70 ${basis?.label?.glyph?.classes}`,
- weight: `bold`,
- dim: `md`,
- }}
- ></Glyph>
- {/if}
- </div>
- {/if}
- </button>
-{/if}
diff --git a/apps-lib/src/lib/component/nav/nav.svelte b/apps-lib/src/lib/component/nav/nav.svelte
@@ -1,167 +0,0 @@
-<script lang="ts">
- import { goto } from "$app/navigation";
- import {
- app_layout,
- ButtonArrow,
- Empty,
- encode_qp_route,
- fmt_cl,
- Glyph,
- LoadSymbol,
- nav_blur,
- nav_prev,
- nav_visible,
- NavOption,
- type INavBasis,
- } from "$lib";
- import { onDestroy, onMount } from "svelte";
-
- export let basis: INavBasis;
- $: basis = basis;
-
- let el: HTMLElement | null;
- let el_inner: HTMLElement | null;
-
- let nav_prev_label = ``;
-
- $: classes_nav_blur = $nav_blur ? `bg-white/40 backdrop-blur-sm` : ``;
-
- onMount(async () => {
- try {
- nav_visible.set(true);
- if ($nav_prev.length)
- nav_prev_label = $nav_prev[$nav_prev.length - 1].label || ``;
- } catch (e) {
- } finally {
- }
- });
-
- onDestroy(async () => {
- try {
- nav_visible.set(false);
- } catch (e) {
- } finally {
- }
- });
-
- const callback_prev = async (): Promise<void> => {
- try {
- if (basis.prev.prevent_route) {
- await basis.prev.prevent_route.callback();
- return;
- } else if (basis.prev.callback) await basis.prev.callback();
- let route_to =
- typeof basis.prev.route === `string`
- ? basis.prev.route
- : encode_qp_route(basis.prev.route[0], basis.prev.route[1]);
- if ($nav_prev.length) {
- const nav_prev_li = $nav_prev[$nav_prev.length - 1];
- $nav_prev = [...$nav_prev.slice(0, -1)];
- if (nav_prev_li)
- route_to = encode_qp_route(
- nav_prev_li.route,
- nav_prev_li.params,
- );
- }
- await goto(route_to);
- } catch (e) {
- console.log(`(error) callback_prev `, e);
- }
- };
-</script>
-
-<div
- bind:this={el}
- class={`z-10 absolute top-0 left-0 flex flex-col w-full justify-start items-start h-nav_${$app_layout} ${classes_nav_blur} duration-[250ms] el-re`}
->
- <div
- bind:this={el_inner}
- class={`relative flex flex-col h-full w-full justify-end items-center`}
- >
- <div
- class={`absolute bottom-[6px] left-0 grid grid-cols-12 flex flex-row h-8 w-full justify-start items-center`}
- >
- <div
- class={`col-span-4 flex flex-row w-full justify-start items-center`}
- >
- {#if basis.prev.loading}
- <div
- class={`flex flex-row pl-4 justify-center items-center`}
- >
- <LoadSymbol />
- </div>
- {:else if basis.prev.kind === `arrow`}
- <div
- class={`flex flex-row w-full pl-8 justify-start items-center`}
- >
- <ButtonArrow
- basis={{
- label: nav_prev_label || basis.prev.label,
- callback: async () => {
- await callback_prev();
- },
- }}
- />
- </div>
- {:else}
- <button
- class={`group flex flex-row h-full pl-2 justify-start items-center`}
- on:click={async () => {
- await callback_prev();
- }}
- >
- <Glyph
- basis={{
- key: `caret-left`,
- weight: `bold`,
- dim: `md+`,
- classes: `text-layer-1-glyph-hl group-active:opacity-70 transition-opacity`,
- }}
- ></Glyph>
- {#if nav_prev_label || basis.prev.label}
- <p
- class={`font-sans text-nav_prev text-layer-1-glyph-hl group-active:opacity-60 transition-opacity`}
- >
- {nav_prev_label || basis.prev.label}
- </p>
- {:else}
- <Empty></Empty>
- {/if}
- </button>
- {/if}
- </div>
- <div
- class={`col-span-4 flex flex-row h-full justify-center items-center`}
- >
- {#if basis.title}
- <button
- class={`flex flex-row justify-center items-center`}
- on:click={async () => {
- if (basis.title.callback)
- await basis.title.callback();
- }}
- >
- {#if `value` in basis.title.label}
- <p
- class={`${fmt_cl(basis.title.label.classes)} font-sans text-nav_curr text-layer-1-glyph`}
- >
- {basis.title.label.value}
- </p>
- {/if}
- </button>
- {:else}
- <Empty></Empty>
- {/if}
- </div>
- <div
- class={`col-span-4 flex flex-row h-full justify-end items-center`}
- >
- {#if basis.option}
- <NavOption basis={basis.option} />
- {:else}
- <Empty></Empty>
- {/if}
- </div>
- </div>
- </div>
-</div>
diff --git a/apps-lib/src/lib/component/page/page-header.svelte b/apps-lib/src/lib/component/page/page-header.svelte
@@ -1,41 +0,0 @@
-<script lang="ts">
- import {
- app_layout,
- callback_route,
- Empty,
- ph_blur,
- type IPageHeader,
- } from "$lib";
- import { fade } from "svelte/transition";
-
- export let basis: IPageHeader;
-</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_${$app_layout} w-full justify-center items-center bg-layer-0-surface-blur/30 backdrop-blur-lg`}
- >
- <Empty></Empty>
- </div>
-{/if}
-<div
- class={`z-20 sticky top-0 flex flex-row w-full pb-4 px-6 justify-between items-center`}
->
- <div class={`flex flex-row justify-start items-center`}>
- <button
- class={`flex flex-row gap-1 justify-center items-center`}
- on:click={async () => {
- await callback_route(basis.callback_route);
- }}
- >
- <p
- class={`font-sansd font-[700] text-2xl text-layer-0-glyph capitalize`}
- >
- {basis.label || ``}
- </p>
- </button>
- </div>
- <slot name="header-option" />
-</div>
diff --git a/apps-lib/src/lib/component/page/page-toolbar.svelte b/apps-lib/src/lib/component/page/page-toolbar.svelte
@@ -1,55 +0,0 @@
-<script lang="ts">
- import { goto } from "$app/navigation";
- import {
- Empty,
- Glyph,
- LogoCircleSm,
- PageHeader,
- type IBasisOpt,
- type IPageToolbar,
- } from "$lib";
-
- export let basis: IBasisOpt<IPageToolbar> = undefined;
-</script>
-
-<div class={`flex flex-row h-12 w-full px-6 justify-between items-center`}>
- <button
- class={`flex flex-row gap-2 justify-start items-center`}
- on:click={async () => {
- if (basis?.callback) await basis.callback();
- else await goto(`/`);
- }}
- >
- <LogoCircleSm />
- <p
- class={`font-sansd italic font-[700] text-[1.7rem] text-layer-0-glyph lowercase`}
- >
- {`radroots`}
- </p>
- </button>
- <button
- class={`flex flex-row justify-center items-center`}
- on:click={async () => {
- await goto(`/settings`);
- }}
- >
- <Glyph
- basis={{
- classes: `text-layer-0-glyph`,
- dim: `lg`,
- weight: `bold`,
- key: `gear`,
- }}
- ></Glyph>
- </button>
-</div>
-{#if basis?.header}
- <div class={`flex flex-row h-5 w-full justify-center items-center`}>
- <Empty></Empty>
- </div>
- <PageHeader basis={basis.header}>
- <div slot="header-option">
- <slot name="header-option" />
- </div>
- </PageHeader>
-{/if}
diff --git a/apps-lib/src/lib/component/trellis/trellis-default-label.svelte b/apps-lib/src/lib/component/trellis/trellis-default-label.svelte
@@ -1,30 +0,0 @@
-<script lang="ts">
- import { fmt_cl, type ITrellisDefaultLabel } from "$lib";
- import type { ThemeLayer } from "@radroots/theme";
-
- export let classes = ``;
- export let layer: ThemeLayer;
- export let labels: ITrellisDefaultLabel[];
-</script>
-
-<div class={`${fmt_cl(classes)} flex flex-row`}>
- <p class={`font-sans text-trellis_ti text-layer-${layer}-glyph-shade`}>
- {#each labels as label}
- <span class={`${fmt_cl(label.classes)} font-sans text-trellis_ti`}>
- {#if `callback` in label}
- <button
- class={``}
- on:click|preventDefault={async () => {
- if (`callback` in label && label.callback)
- await label.callback();
- }}
- >
- {label.label}
- </button>
- {:else}
- {label.label}
- {/if}
- </span>
- {/each}
- </p>
-</div>
diff --git a/apps-lib/src/lib/component/trellis/trellis-end.svelte b/apps-lib/src/lib/component/trellis/trellis-end.svelte
@@ -1,30 +0,0 @@
-<script lang="ts">
- import { Glyph, type ITrellisBasisTouchEnd } from "$lib";
- import type { ThemeLayer } from "@radroots/theme";
-
- export let basis: ITrellisBasisTouchEnd;
- export let layer: ThemeLayer;
- export let hide_active: boolean;
-</script>
-
-<div
- class={`absolute top-0 right-0 h-full w-max flex flex-row justify-center items-center`}
->
- <button
- class={`flex pr-3`}
- on:click|preventDefault={async (ev) => {
- if (basis.callback) await basis.callback(ev);
- }}
- >
- {#if basis.glyph}
- <Glyph
- basis={{
- classes: `text-layer-${layer}-glyph-shade ${hide_active ? `` : `group-active:text-layer-${layer}-glyph_a`} translate-y-[1px] opacity-70`,
- dim: `xs+`,
- weight: `bold`,
- ...basis.glyph,
- }}
- ></Glyph>
- {/if}
- </button>
-</div>
diff --git a/apps-lib/src/lib/component/trellis/trellis-input.svelte b/apps-lib/src/lib/component/trellis/trellis-input.svelte
@@ -1,82 +0,0 @@
-<script lang="ts">
- import {
- fmt_cl,
- fmt_trellis,
- Glyph,
- Input,
- LoadSymbol,
- type ITrellisBasisInput,
- } from "$lib";
- import type { ThemeLayer } from "@radroots/theme";
-
- export let basis: ITrellisBasisInput;
- export let layer: ThemeLayer;
- export let hide_border_t: boolean;
- export let hide_border_b: boolean;
-</script>
-
-<div class={`flex flex-row flex-grow h-full w-full`}>
- <div
- class={`${fmt_trellis(hide_border_b, hide_border_t)} flex flex-row h-line w-full justify-start items-center border-t-line border-layer-${layer}-surface-edge overflow-hidden`}
- >
- {#if basis.line_label && basis.line_label.value}
- <div
- class={`${fmt_cl(basis.line_label.classes)} flex flex-row h-full justify-start items-center overflow-x-hidden`}
- >
- <p class={`font-sans text-layer-${layer}-glyph_b`}>
- {basis.line_label.value}
- </p>
- </div>
- {/if}
- <div
- class={`relative flex flex-row flex-grow h-full pr-12 justify-start items-center`}
- >
- <Input
- basis={{
- ...basis.basis,
- layer: layer,
- }}
- ></Input>
- {#if basis.action}
- {#if basis.action.visible}
- <div
- class={`absolute top-0 right-0 flex flex-row h-full w-12 pr-4 justify-end items-center fade-in`}
- >
- {#if basis.action.loading}
- <div class={`flex flex-row fade-in`}>
- <LoadSymbol
- basis={{
- dim: `glyph-send-button`,
- blades: 8,
- classes: `text-layer-${layer}-glyph el-re`,
- }}
- />
- </div>
- {:else}
- <button
- class={`group fade-in-long`}
- on:click|preventDefault={async () => {
- if (basis.action.callback)
- await basis.action.callback();
- }}
- >
- <Glyph
- basis={basis.action.glyph
- ? {
- dim: `md-`,
- ...basis.action.glyph,
- }
- : {
- key: `plus`,
- classes: `text-layer-${layer}-glyph`,
- dim: `md-`,
- }}
- ></Glyph>
- </button>
- {/if}
- </div>
- {/if}
- {/if}
- </div>
- </div>
-</div>
diff --git a/apps-lib/src/lib/component/trellis/trellis-line.svelte b/apps-lib/src/lib/component/trellis/trellis-line.svelte
@@ -1,40 +0,0 @@
-<script lang="ts">
- import { fmt_trellis, LoadSymbol, type CallbackPromiseGeneric } from "$lib";
- import type { ThemeLayer } from "@radroots/theme";
-
- export let loading: boolean = false;
- export let layer: ThemeLayer;
- export let callback: CallbackPromiseGeneric<MouseEvent>;
- export let hide_border_t: boolean;
- export let hide_border_b: boolean;
-</script>
-
-<button
- class={`flex flex-row flex-grow overflow-x-hidden`}
- on:click={async (ev) => {
- if (callback) await callback(ev);
- }}
->
- <div
- class={`${fmt_trellis(hide_border_b, hide_border_t)} flex flex-row h-full w-full justify-center items-center border-t-line border-layer-${layer}-surface-edge el-re`}
- >
- {#if loading}
- <div
- class={`flex flex-row h-full w-full justify-center items-center`}
- >
- <LoadSymbol basis={{ dim: `sm` }} />
- </div>
- {:else}
- <div
- class={`relative group flex flex-row h-line w-full pr-[2px] justify-between items-center el-re`}
- >
- <div
- class={`flex flex-row h-full w-trellis_display justify-between items-center`}
- >
- <slot />
- </div>
- <slot name="end" />
- </div>
- {/if}
- </div>
-</button>
diff --git a/apps-lib/src/lib/component/trellis/trellis-offset.svelte b/apps-lib/src/lib/component/trellis/trellis-offset.svelte
@@ -1,70 +0,0 @@
-<script lang="ts">
- import {
- Empty,
- fmt_cl,
- Glyph,
- GlyphCircle,
- LoadSymbol,
- type ITrellisBasisOffset,
- type ITrellisBasisOffsetMod,
- } from "$lib";
- import type { ThemeLayer } from "@radroots/theme";
-
- export let basis: ITrellisBasisOffset | undefined = undefined;
- export let layer: ThemeLayer;
-
- let mod: ITrellisBasisOffsetMod = `sm`;
- $: mod = basis && basis.mod ? basis.mod : `sm`;
-</script>
-
-<div class={`flex flex-row h-full`}>
- {#if mod === `sm`}
- <div class={`${fmt_cl(``)} flex flex-row h-full w-[22px]`}>
- <Empty></Empty>
- </div>
- {:else if mod === `glyph`}
- <div class={`flex flex-row pr-[2px]`}>
- <div class={`${fmt_cl(``)} flex flex-row h-full w-trellisOffset`}>
- <Empty></Empty>
- </div>
- </div>
- {:else if typeof mod === `object`}
- <div
- class={`flex flex-row h-full min-w-[20px] w-trellisOffset justify-center items-center pr-3`}
- >
- <button
- class={`fade-in pl-2 translate-x-[3px] translate-y-[1px]`}
- on:click|preventDefault={async (ev) => {
- if (mod.loading) return;
- else if (typeof basis !== `boolean` && basis?.callback)
- await basis.callback(ev);
- }}
- >
- {#if mod.loading}
- <LoadSymbol basis={{ blades: 8, dim: `xs` }} />
- {:else if `glyph` in mod}
- <Glyph
- basis={{
- classes: mod.glyph.classes
- ? mod.glyph.classes
- : `text-layer-${layer}-glyph`,
- ...mod.glyph,
- }}
- ></Glyph>
- {:else if `glyph_circle` in mod}
- <GlyphCircle
- basis={{
- classes_wrap: mod.glyph_circle?.classes_wrap,
- glyph: {
- classes: mod.glyph_circle?.glyph?.classes
- ? mod.glyph_circle?.glyph?.classes
- : `text-layer-${layer}-glyph`,
- ...mod.glyph_circle?.glyph,
- },
- }}
- ></GlyphCircle>
- {/if}
- </button>
- </div>
- {/if}
-</div>
diff --git a/apps-lib/src/lib/component/trellis/trellis-row-display-value.svelte b/apps-lib/src/lib/component/trellis/trellis-row-display-value.svelte
@@ -1,41 +0,0 @@
-<script lang="ts">
- import {
- fmt_cl,
- get_label_classes_kind,
- Glyph,
- type ITrellisKindDisplayValue,
- } from "$lib";
- import type { ThemeLayer } from "@radroots/theme";
-
- export let basis: ITrellisKindDisplayValue;
- export let layer: ThemeLayer;
- export let hide_active: boolean;
-</script>
-
-<button
- class={`z-10 flex flex-grow justify-end`}
- on:click|stopPropagation={async (ev) => {
- if (basis.callback) await basis.callback(ev);
- }}
->
- {#if `icon` in basis}
- <Glyph
- basis={{
- classes:
- basis.icon.classes ||
- `${get_label_classes_kind(layer, `shade`, hide_active)}`,
- key: basis.icon.key,
- weight: `bold`,
- dim: `sm`,
- }}
- ></Glyph>
- {:else if basis.label}
- {#if `value` in basis.label}
- <p
- class={`${fmt_cl(basis.label.classes)} font-sans text-line_d_e line-clamp-1 text-layer-0-glyph-label el-re`}
- >
- {basis.label.value}
- </p>
- {/if}
- {/if}
-</button>
diff --git a/apps-lib/src/lib/component/trellis/trellis-row-label.svelte b/apps-lib/src/lib/component/trellis/trellis-row-label.svelte
@@ -1,61 +0,0 @@
-<script lang="ts">
- import {
- GlyphButton,
- fmt_cl,
- get_label_classes_kind,
- type ILabelTupFields,
- } from "$lib";
- import type { ThemeLayer } from "@radroots/theme";
-
- export let basis: ILabelTupFields;
- export let layer: ThemeLayer;
- export let hide_active: boolean;
-</script>
-
-<div class={`flex flex-row h-full items-center justify-between`}>
- {#if basis.left && basis.left.length}
- <div class={`flex flex-row h-full items-center truncate`}>
- {#each basis.left as title_l}
- <div
- class={`${fmt_cl(title_l.classes_wrap)} flex flex-row h-full items-center ${get_label_classes_kind(layer, undefined, hide_active)} ${title_l.hide_truncate ? `` : `truncate`}`}
- >
- {#if `glyph` in title_l}
- <div
- class={`flex flex-row justify-start items-center pr-2`}
- >
- <GlyphButton basis={{ ...title_l.glyph }}
- ></GlyphButton>
- </div>
- {:else if `value` in title_l}
- <p
- class={`${fmt_cl(title_l.classes)} font-sans text-line_d ${title_l.hide_truncate ? `` : `truncate`} el-re`}
- >
- {title_l.value || ``}
- </p>
- {/if}
- </div>
- {/each}
- </div>
- {/if}
- {#if basis.right && basis.right.length}
- <div
- class={`flex flex-row h-full w-content items-center justify-end pr-4`}
- >
- {#each basis.right.reverse() as title_r}
- <div
- class={`${fmt_cl(title_r.classes_wrap)} flex flex-row h-full gap-1 items-center ${title_r.hide_truncate ? `` : `truncate`}`}
- >
- {#if `glyph` in title_r}
- <GlyphButton basis={{ ...title_r.glyph }}></GlyphButton>
- {:else if `value` in title_r}
- <p
- class={`${fmt_cl(title_r.classes)} font-sans text-line_d text-layer-${layer}-glyph_d ${title_r.hide_truncate ? `` : `truncate`} el-re`}
- >
- {title_r.value || ``}
- </p>
- {/if}
- </div>
- {/each}
- </div>
- {/if}
-</div>
diff --git a/apps-lib/src/lib/component/trellis/trellis-select.svelte b/apps-lib/src/lib/component/trellis/trellis-select.svelte
@@ -1,57 +0,0 @@
-<script lang="ts">
- import {
- LoadSymbol,
- SelectMenu,
- TrellisEnd,
- TrellisLine,
- TrellisRowDisplayValue,
- TrellisRowLabel,
- type ITrellisBasisSelect,
- } from "$lib";
- import type { ThemeLayer } from "@radroots/theme";
-
- export let basis: ITrellisBasisSelect;
- export let layer: ThemeLayer;
- export let hide_border_t: boolean;
- export let hide_border_b: boolean;
- export let hide_active: boolean;
-
- $: value = basis.el.value;
- $: loading = typeof basis?.loading === `boolean` ? basis.loading : false;
-</script>
-
-<TrellisLine
- {layer}
- {loading}
- {hide_border_b}
- {hide_border_t}
- callback={basis.callback}
->
- <TrellisRowLabel basis={basis.label} {layer} {hide_active} />
- {#if basis.display}
- <div class={`flex flex-row pr-3 justify-center items-end`}>
- <SelectMenu {value} basis={basis.el}>
- <svelte:fragment slot="element">
- {#if basis.display.loading}
- <div
- class={`flex flex-row h-full w-full justify-end items-center`}
- >
- <LoadSymbol basis={{ dim: `sm` }} />
- </div>
- {:else}
- <TrellisRowDisplayValue
- basis={{ ...basis.display }}
- {layer}
- {hide_active}
- />
- {/if}
- </svelte:fragment>
- </SelectMenu>
- </div>
- {/if}
- <div slot="end">
- {#if basis.end && !basis.display.loading}
- <TrellisEnd basis={basis.end} {layer} {hide_active} />
- {/if}
- </div>
-</TrellisLine>
diff --git a/apps-lib/src/lib/component/trellis/trellis-title.svelte b/apps-lib/src/lib/component/trellis/trellis-title.svelte
@@ -1,62 +0,0 @@
-<script lang="ts">
- import { Empty, fmt_cl, Glyph, type ITrellisTitle, LabelSwap } from "$lib";
- import type { ThemeLayer } from "@radroots/theme";
-
- export let basis: ITrellisTitle;
- export let layer: ThemeLayer = 0;
-
- $: mod = basis && basis.mod ? basis.mod : `sm`;
-</script>
-
-<div
- class={`${fmt_cl(basis.classes)} flex flex-row h-[24px] w-full pl-[2px] gap-1 items-center`}
->
- <button
- class={`flex flex-row h-full w-max items-center gap-1 ${mod === `glyph` ? `pl-[36px]` : mod === `sm` ? `pl-[16px]` : ``}`}
- on:click|preventDefault={async () => {
- if (basis && basis.callback) await basis.callback();
- }}
- >
- {#if basis.value === true}
- <Empty></Empty>
- {:else}
- <p
- class={`font-sans text-trellis_ti text-layer-${layer}-glyph-label uppercase`}
- >
- {basis.value || ``}
- </p>
- {/if}
- </button>
- {#if basis.link}
- <button
- class={`${fmt_cl(basis.link.classes)} group flex flex-row h-full w-max items-center`}
- on:click|preventDefault={async () => {
- if (basis.link && basis.link.callback)
- await basis.link.callback();
- }}
- >
- {#if basis.link.label}
- {#if `swap` in basis.link.label}
- <LabelSwap basis={basis.link.label} />
- {:else if `value` in basis.link.label}
- <p
- class={`${fmt_cl(basis.link.label.classes)} font-sans text-trellis_ti uppercase fade-in`}
- >
- {basis.link.label.value || ``}
- </p>
- {/if}
- {/if}
- {#if basis.link.glyph}
- <div class={`flex flex-row w-max`}>
- <Glyph
- basis={{
- ...basis.link.glyph,
- dim: `xs-`,
- classes: `${fmt_cl(basis.link.glyph.classes)} fade-in`,
- }}
- ></Glyph>
- </div>
- {/if}
- </button>
- {/if}
-</div>
diff --git a/apps-lib/src/lib/component/trellis/trellis-touch.svelte b/apps-lib/src/lib/component/trellis/trellis-touch.svelte
@@ -1,34 +0,0 @@
-<script lang="ts">
- import {
- TrellisEnd,
- TrellisLine,
- TrellisRowDisplayValue,
- TrellisRowLabel,
- type ITrellisBasisTouch,
- } from "$lib";
- import type { ThemeLayer } from "@radroots/theme";
-
- export let basis: ITrellisBasisTouch;
- export let layer: ThemeLayer;
- export let hide_border_t: boolean;
- export let hide_border_b: boolean;
- export let hide_active: boolean;
-</script>
-
-<TrellisLine {layer} {hide_border_b} {hide_border_t} callback={basis.callback}>
- <TrellisRowLabel basis={basis.label} {layer} {hide_active} />
- {#if basis.display}
- <TrellisRowDisplayValue
- basis={{
- ...basis.display,
- }}
- {layer}
- {hide_active}
- />
- {/if}
- <svelte:fragment slot="end">
- {#if basis.end}
- <TrellisEnd basis={basis.end} {layer} {hide_active} />
- {/if}
- </svelte:fragment>
-</TrellisLine>
diff --git a/apps-lib/src/lib/component/trellis/trellis.svelte b/apps-lib/src/lib/component/trellis/trellis.svelte
@@ -1,124 +0,0 @@
-<script lang="ts">
- import {
- app_layout,
- fmt_cl,
- lls,
- parse_layer,
- TrellisDefaultLabel,
- TrellisInput,
- TrellisOffset,
- TrellisSelect,
- TrellisTitle,
- TrellisTouch,
- type ITrellis,
- } from "$lib";
-
- export let basis: { args: ITrellis };
- $: ({ args } = basis);
- $: hide_border_t =
- typeof args.hide_border_top === `boolean` ? args.hide_border_top : true;
- $: hide_border_b =
- typeof args.hide_border_bottom === `boolean`
- ? args.hide_border_bottom
- : true;
- $: hide_rounded =
- typeof args.hide_rounded === `boolean` ? args.hide_rounded : false;
- $: set_title_background =
- typeof args.set_title_background === `boolean`
- ? args.set_title_background
- : false;
- $: set_default_background =
- typeof args.set_default_background === `boolean`
- ? args.set_default_background
- : false;
-</script>
-
-<div
- id={basis.args.id || ``}
- class={`${fmt_cl(args.classes)} flex flex-col`}
- data-view={basis.args.view || ``}
->
- <div
- class={`relative flex flex-col h-auto w-${$app_layout} gap-[3px] ${set_title_background ? `bg-layer-${args.layer}-surface` : ``}`}
- >
- {#if args.title && (!args.default_el || (args.default_el && args.default_el.show_title))}
- <TrellisTitle
- basis={args.title}
- layer={parse_layer(args.layer - 1)}
- />
- {/if}
- {#if args.default_el}
- <div
- class={`flex flex-col h-auto w-full justify-center items-center`}
- >
- {#if $$slots.default_el}
- <slot name="default_el" />
- {:else if args.default_el}
- <TrellisDefaultLabel
- layer={parse_layer(args.layer - 1)}
- labels={args.default_el.labels
- ? args.default_el.labels
- : [
- {
- label: `${$lls(`common.no_items_to_display`)}.`,
- },
- ]}
- />
- {/if}
- </div>
- {:else if args.list}
- <div class={`flex flex-col w-full justify-center items-center`}>
- {#each args.list as basis}
- <div
- class={`${basis.hide_field ? "hidden" : ``} group flex flex-row h-full w-full justify-end items-center bg-layer-${args.layer}-surface ${basis.full_rounded ? `rounded-touch` : ``} ${hide_rounded ? `` : `first:rounded-t-2xl last:rounded-b-2xl`} ${!basis.hide_active ? `active:bg-layer-${args.layer}-surface_a` : ``} el-re`}
- >
- <div
- class={`flex flex-row h-full w-full gap-1 items-center overflow-y-hidden`}
- >
- {#if !args.hide_offset}
- <TrellisOffset
- basis={basis.offset}
- layer={args.layer}
- />
- {/if}
- {#if $$slots.offset}
- <slot name="offset" />
- {/if}
- {#if `touch` in basis && basis.touch}
- <TrellisTouch
- basis={basis.touch}
- layer={args.layer}
- {hide_border_b}
- {hide_border_t}
- hide_active={!!basis.hide_active}
- />
- {:else if `input` in basis && basis.input}
- <TrellisInput
- basis={basis.input}
- layer={args.layer}
- {hide_border_b}
- {hide_border_t}
- />
- {:else if `select` in basis && basis.select}
- <TrellisSelect
- basis={basis.select}
- layer={args.layer}
- {hide_border_b}
- {hide_border_t}
- hide_active={!!basis.hide_active}
- />
- {/if}
- </div>
- </div>
- {/each}
- </div>
- {/if}
- </div>
- {#if $$slots.append}
- <div
- class={`flex flex-col w-full ${set_default_background ? `bg-layer-${args.layer}-surface` : ``}`}
- >
- <slot name="append" />
- </div>
- {/if}
-</div>
diff --git a/apps-lib/src/lib/components/button/button-horizontal-pair-slide.svelte b/apps-lib/src/lib/components/button/button-horizontal-pair-slide.svelte
@@ -0,0 +1,45 @@
+<script lang="ts">
+ import { Empty } from "$root";
+
+ let {
+ basis,
+ toggle = $bindable(false),
+ }: {
+ basis: {
+ label_l: string;
+ label_r: string;
+ };
+ toggle: boolean;
+ } = $props();
+</script>
+
+<button
+ class={`group relative flex flex-row h-[38px] w-full justify-between items-center rounded-touch bg-layer-2-surface`}
+ onclick={async () => {
+ toggle = !toggle;
+ }}
+>
+ <div
+ class={`z-10 flex flex-row h-full basis-1/2 justify-center items-center bg-transparent`}
+ >
+ <p
+ class={`font-sans font-[400] text-[16px] tracking-wide ${!toggle ? `text-white/60` : `text-layer-0-glyph`} el-re`}
+ >
+ {basis.label_l}
+ </p>
+ </div>
+ <div
+ class={`z-10 flex flex-row h-full basis-1/2 justify-center items-center bg-transparent`}
+ >
+ <p
+ class={`font-sans font-[400] text-[16px] tracking-wide ${toggle ? `text-white/60` : `text-layer-0-glyph`} el-re`}
+ >
+ {basis.label_r}
+ </p>
+ </div>
+ <div
+ class={`z-5 absolute top-0 bottom-0 flex flex-row h-full w-[50%] justify-center items-center rounded-touch bg-th-black transition-all duration-[400ms] ${toggle ? `translate-x-full` : `translate-x-0`}`}
+ >
+ <Empty />
+ </div>
+</button>
diff --git a/apps-lib/src/lib/components/button/button-label-dashed.svelte b/apps-lib/src/lib/components/button/button-label-dashed.svelte
@@ -0,0 +1,24 @@
+<script lang="ts">
+ import type { ICb } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: ICb & {
+ label: string;
+ };
+ } = $props();
+</script>
+
+<button
+ class={`flex flex-row h-line_button w-full justify-center items-center`}
+ onclick={async () => {
+ await basis.callback();
+ }}
+>
+ <p
+ class={`font-sans font-[500] text-lg text-layer-0-glyph-hl tracking-wide`}
+ >
+ {`- ${basis.label} -`}
+ </p>
+</button>
diff --git a/apps-lib/src/lib/components/button/button-label-glyph.svelte b/apps-lib/src/lib/components/button/button-label-glyph.svelte
@@ -0,0 +1,42 @@
+<script lang="ts">
+ import { Glyph } from "$root";
+ import { type GlyphKey, type ICb } from "@radroots/util";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis,
+ children,
+ }: {
+ basis: ICb & {
+ label: string;
+ glyph?: GlyphKey;
+ ring?: boolean;
+ };
+ children?: Snippet;
+ } = $props();
+</script>
+
+<div class={`flex flex-col w-full gap-1 justify-center items-center`}>
+ <button
+ class={`flex flex-row h-12 w-12 p-2 justify-center items-center rounded-2xl bg-layer-1-surface ${basis.ring ? `ring-[4px] ring-layer-2-surface/40` : ``} el-re`}
+ onclick={async () => {
+ await basis.callback();
+ }}
+ >
+ {#if basis.glyph}
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph/80`,
+ dim: `sm`,
+
+ key: basis.glyph,
+ }}
+ />
+ {:else if children}
+ {@render children()}
+ {/if}
+ </button>
+ <p class={`font-sans font-[400] text-sm text-layer-0-glyph el-re`}>
+ {basis.label}
+ </p>
+</div>
diff --git a/apps-lib/src/lib/components/button/button-layout-pair.svelte b/apps-lib/src/lib/components/button/button-layout-pair.svelte
@@ -0,0 +1,62 @@
+<script lang="ts">
+ import { app_lo, ButtonLayout, Empty } from "$root";
+ import {
+ fmt_cl,
+ type CallbackPromise,
+ type IClOpt,
+ type IDisabledOpt,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: IClOpt & {
+ continue: IDisabledOpt & {
+ label: string;
+ callback: CallbackPromise;
+ };
+ back?: IDisabledOpt & {
+ visible: boolean;
+ label?: string;
+ callback: CallbackPromise;
+ };
+ };
+ } = $props();
+</script>
+
+<div
+ class={`${fmt_cl(basis.classes)} flex flex-col gap-1 justify-center items-center ${basis?.back?.visible ? `-translate-y-1` : ``}`}
+>
+ <ButtonLayout
+ basis={{
+ disabled: basis.continue.disabled,
+ label: basis.continue.label,
+ callback: basis.continue.callback,
+ }}
+ ></ButtonLayout>
+ {#if basis.back}
+ <div class={`flex flex-col justify-center items-center el-re`}>
+ {#if basis.back.visible}
+ <button
+ class={`group flex flex-row h-12 w-lo_${$app_lo} justify-center items-center fade-in el-re`}
+ onclick={async (ev) => {
+ ev.stopPropagation();
+ if (!basis.back?.disabled) await basis.back?.callback();
+ }}
+ >
+ <p
+ class={`font-sans font-[600] tracking-wide text-layer-1-glyph-shade ${basis.back?.disabled ? `` : `group-active:text-layer-1-glyph/40`} el-re`}
+ >
+ {basis.back.label || ``}
+ </p>
+ </button>
+ {:else}
+ <div
+ class={`flex flex-row h-4 w-full justify-start items-center`}
+ >
+ <Empty />
+ </div>
+ {/if}
+ </div>
+ {/if}
+</div>
diff --git a/apps-lib/src/lib/components/button/button-layout.svelte b/apps-lib/src/lib/components/button/button-layout.svelte
@@ -0,0 +1,46 @@
+<script lang="ts">
+ import { app_lo } from "$lib/store/app";
+ import {
+ fmt_cl,
+ parse_layer,
+ type CallbackPromise,
+ type IClOpt,
+ type IDisabledOpt,
+ type ILyOpt,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: ILyOpt &
+ IClOpt &
+ IDisabledOpt & {
+ classes_inner?: string;
+ hide_active?: boolean;
+ label: string;
+ callback: CallbackPromise;
+ };
+ } = $props();
+
+ const layer = $derived(parse_layer(basis.layer, 1));
+
+ const classes_active = $derived(
+ !basis.hide_active
+ ? `layer-1-active-surface layer-1-active-raise-less layer-1-active-ring-less`
+ : ``,
+ );
+</script>
+
+<button
+ class={`${fmt_cl(basis.classes)} group flex flex-row h-touch_guide w-lo_${$app_lo} justify-center items-center bg-layer-${layer}-surface rounded-touch ${basis.disabled ? `opacity-60` : classes_active} el-re`}
+ onclick={async (ev) => {
+ ev.stopPropagation();
+ if (!basis.disabled) await basis.callback();
+ }}
+>
+ <p
+ class={`${fmt_cl(basis.classes_inner)} font-sans font-[600] tracking-wide text-layer-${layer}-glyph-shade ${basis.disabled ? `` : `group-active:text-layer-${layer}-glyph/40 `} el-re`}
+ >
+ {basis.label || ``}
+ </p>
+</button>
diff --git a/apps-lib/src/lib/components/button/button-round.svelte b/apps-lib/src/lib/components/button/button-round.svelte
@@ -0,0 +1,25 @@
+<script lang="ts">
+ import { LoadSymbol } from "$root";
+ import { fmt_cl, type IButtonRound } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: IButtonRound;
+ } = $props();
+</script>
+
+<button
+ class={`${fmt_cl(basis.classes)} group flex flex-row h-touch_guide w-full justify-center items-center rounded-full el-re`}
+ onclick={async () => {
+ await basis.callback();
+ }}
+>
+ {#if basis.loading}
+ <LoadSymbol basis={{ dim: `md` }} />
+ {:else}
+ <p class={`font-sans font-[500] text-[16px]`}>
+ {basis.label}
+ </p>
+ {/if}
+</button>
diff --git a/apps-lib/src/lib/components/button/button-simple-glyph.svelte b/apps-lib/src/lib/components/button/button-simple-glyph.svelte
@@ -0,0 +1,33 @@
+<script lang="ts">
+ import {
+ parse_layer,
+ type CallbackPromise,
+ type ILyOpt,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: ILyOpt & {
+ label: string;
+ callback: CallbackPromise;
+ allow_propogation?: boolean;
+ };
+ } = $props();
+
+ const layer = $derived(parse_layer(basis?.layer ? basis.layer : 1));
+</script>
+
+<button
+ class={`group flex flex-row h-[3.5rem] w-full justify-center items-center rounded-touch`}
+ onclick={async (ev) => {
+ if (!basis.allow_propogation) ev.stopPropagation();
+ await basis.callback();
+ }}
+>
+ <p
+ class={`font-sans font-[700] text-xl text-layer-${layer}-glyph capitalize tracking-wider opacity-active`}
+ >
+ {basis.label}
+ </p>
+</button>
diff --git a/apps-lib/src/lib/components/button/button-simple.svelte b/apps-lib/src/lib/components/button/button-simple.svelte
@@ -0,0 +1,21 @@
+<script lang="ts">
+ import { parse_layer, type IButtonSimple } from "@radroots/util";
+
+ 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-layer-${layer}-surface layer-${layer}-active-surface layer-${layer}-active-ring`}
+ onclick={async (ev) => {
+ if (!basis.allow_propogation) ev.stopPropagation();
+ await basis.callback();
+ }}
+>
+ <p
+ class={`font-sans font-[700] text-xl text-layer-0-glyph capitalize tracking-wider opacity-active`}
+ >
+ {basis.label}
+ </p>
+</button>
diff --git a/apps-lib/src/lib/components/button/float-page-button.svelte b/apps-lib/src/lib/components/button/float-page-button.svelte
@@ -0,0 +1,40 @@
+<script lang="ts">
+ import { Glyph, LoadSymbol } from "$root";
+ import {
+ type GeometryScreenPositionHorizontal,
+ type ICb,
+ type IGlyphKey,
+ type ILoadingOpt,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: ICb &
+ ILoadingOpt &
+ IGlyphKey & {
+ posx: Omit<GeometryScreenPositionHorizontal, "center">;
+ };
+ } = $props();
+</script>
+
+<div class={`absolute top-10 ${basis.posx}-6 flex flex-row`}>
+ <button
+ class={`flex flex-row h-12 w-12 justify-center items-center bg-layer-1-surface rounded-full el-re`}
+ onclick={basis.callback}
+ >
+ {#if basis.loading}
+ <LoadSymbol />
+ {:else}
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph`,
+ dim: `sm+`,
+
+ key: basis.glyph,
+ }}
+ />
+ {/if}
+ </button>
+</div>
+<div class="hidden left-6 right-6"></div>
diff --git a/apps-lib/src/lib/components/button/glyph-button-simple.svelte b/apps-lib/src/lib/components/button/glyph-button-simple.svelte
@@ -0,0 +1,47 @@
+<script lang="ts">
+ import { Glyph } from "$root";
+ import {
+ fmt_cl,
+ type CallbackPromise,
+ type GlyphKey,
+ type IClOpt,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: IClOpt & {
+ kind?: `primary` | `neutral`;
+ label: string;
+ callback: CallbackPromise;
+ glyph?: GlyphKey;
+ };
+ } = $props();
+
+ const classes_kind = $derived(
+ basis.kind === `neutral`
+ ? `text-layer-0-glyph-shade`
+ : `text-layer-0-glyph-hl`,
+ );
+</script>
+
+<button
+ class={`${fmt_cl(basis.classes)} group flex flex-row justify-center items-center`}
+ onclick={basis.callback}
+>
+ {#if basis.glyph}
+ <Glyph
+ basis={{
+ classes: `${classes_kind}`,
+ dim: `sm+`,
+
+ key: basis.glyph,
+ }}
+ />
+ {/if}
+ <p
+ class={`font-sans font-[600] text-line_label ${classes_kind} opacity-active`}
+ >
+ {basis.label}
+ </p>
+</button>
diff --git a/apps-lib/src/lib/components/button/glyph-button.svelte b/apps-lib/src/lib/components/button/glyph-button.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+ import { fmt_cl, glyph_style_map, type IGlyph } from "@radroots/util";
+
+ let { basis }: { basis: IGlyph } = $props();
+ const styles = $derived(
+ basis?.dim ? glyph_style_map.get(basis.dim) : glyph_style_map.get(`sm`),
+ );
+</script>
+
+<!-- svelte-ignore a11y_consider_explicit_label -->
+<button
+ class={`${fmt_cl(basis.classes)} flex flex-col justify-center items-center text-[${styles?.gl_1}px] el-re`}
+ onclick={async () => {
+ if (basis.callback) await basis.callback();
+ }}
+>
+ <i class={`ph-bold ph-${basis.key}`}></i>
+</button>
diff --git a/apps-lib/src/lib/components/carousel/carousel-flow-item.svelte b/apps-lib/src/lib/components/carousel/carousel-flow-item.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+ import { fmt_cl, type IClOpt } from "@radroots/util";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis,
+ children,
+ }: {
+ basis?: IClOpt;
+ children: Snippet;
+ } = $props();
+</script>
+
+<div
+ class={`${fmt_cl(basis?.classes)} flex flex-col flex-shrink-0 h-full w-full justify-start items-start`}
+>
+ {@render children()}
+</div>
diff --git a/apps-lib/src/lib/components/carousel/carousel-flow.svelte b/apps-lib/src/lib/components/carousel/carousel-flow.svelte
@@ -0,0 +1,37 @@
+<script lang="ts">
+ import { casl_i } from "@radroots/lib-app";
+ import { fmt_cl, type IClOpt } from "@radroots/util";
+ import { onMount, type Snippet } from "svelte";
+
+ let {
+ basis,
+ el_parent = $bindable(null),
+ children,
+ }: {
+ basis?: IClOpt;
+ el_parent?: HTMLDivElement | null;
+ children: Snippet;
+ } = $props();
+
+ let offset_w = $state(0);
+
+ onMount(() => {
+ if (el_parent && el_parent.children.length > 0)
+ offset_w = (el_parent.children[0] as HTMLElement).offsetWidth;
+ });
+
+ window.addEventListener("resize", () => {
+ if (el_parent && el_parent.children.length > 0)
+ offset_w = (el_parent.children[0] as HTMLElement).offsetWidth;
+ });
+</script>
+
+<div class={`${fmt_cl(basis?.classes)}relative flex flex-col h-full w-full`}>
+ <div
+ bind:this={el_parent}
+ class="flex flex-grow transition-transform duration-500"
+ style={`transform: translateX(-${Math.max($casl_i, 0) * offset_w}px)`}
+ >
+ {@render children()}
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/carousel/carousel-item.svelte b/apps-lib/src/lib/components/carousel/carousel-item.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+ import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util";
+ 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 flex-shrink-0 w-[100vw] justify-start items-center`}
+>
+ {@render children()}
+</div>
diff --git a/apps-lib/src/lib/components/carousel/carousel.svelte b/apps-lib/src/lib/components/carousel/carousel.svelte
@@ -0,0 +1,24 @@
+<script lang="ts">
+ import { casl_i } from "$root";
+ import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis = undefined,
+ children,
+ }: {
+ basis?: IBasisOpt<IClOpt>;
+ children: Snippet;
+ } = $props();
+</script>
+
+<div
+ class={`${fmt_cl(basis?.classes)} relative flex flex-col w-full overflow-hidden`}
+>
+ <div
+ class={`flex h-full transition-transform duration-500`}
+ style={`transform: translateX(-${Math.max($casl_i, 0) * 100}vw)`}
+ >
+ {@render children()}
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/envelope/envelope-lower-full.svelte b/apps-lib/src/lib/components/envelope/envelope-lower-full.svelte
@@ -0,0 +1,29 @@
+<script lang="ts">
+ import type { Snippet } from "svelte";
+ import { quintInOut } from "svelte/easing";
+ import { fly } from "svelte/transition";
+
+ let {
+ basis,
+ children,
+ }: {
+ basis: {
+ visible: boolean;
+ };
+ children: Snippet;
+ } = $props();
+</script>
+
+{#if basis.visible}
+ <div
+ in:fly={{ y: `100%`, easing: quintInOut }}
+ out:fly={{ y: `100%`, easing: quintInOut }}
+ class={`z-50 absolute bottom-0 left-0 flex flex-col h-[100vh] w-full justify-start items-start bg-transparent el-re`}
+ >
+ <div
+ class={`z-[100] [flex flex-col h-full w-full justify-start items-center overflow-y-scroll overflow-x-hidden scroll-hide el-re`}
+ >
+ {@render children()}
+ </div>
+ </div>
+{/if}
diff --git a/apps-lib/src/lib/components/envelope/map-location-select-envelope.svelte b/apps-lib/src/lib/components/envelope/map-location-select-envelope.svelte
@@ -0,0 +1,85 @@
+<script lang="ts">
+ import { ButtonSimple, EnvelopeLowerFull, Map, MapMarkerArea } from "$root";
+ import {
+ type CallbackPromiseGeneric,
+ type GeocoderReverseResult,
+ type GeolocationPoint,
+ type LcGeocodeCallback,
+ type ResolveFarmInfo,
+ cfg_map,
+ parse_geol_point_tup,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ map_geop = $bindable(undefined),
+ map_geoc = $bindable(undefined),
+ }: {
+ basis: {
+ farm_info: ResolveFarmInfo;
+ visible: boolean;
+ lc_geocode: LcGeocodeCallback;
+ lc_submit: CallbackPromiseGeneric<{
+ map_geop: GeolocationPoint | undefined;
+ map_geoc: GeocoderReverseResult | undefined;
+ }>;
+ };
+ map_geop?: GeolocationPoint;
+ map_geoc?: GeocoderReverseResult;
+ } = $props();
+
+ let map: maplibregl.Map | undefined = $state(undefined);
+ let map_init = $state(false);
+
+ $effect(() => {
+ if (basis.visible && map && map_geop && !map_init) {
+ map.setCenter(parse_geol_point_tup(map_geop));
+ map_init = true;
+ }
+ });
+
+ $effect(() => {
+ if (basis.visible === false) map_init = false; //@todo
+ });
+</script>
+
+<EnvelopeLowerFull
+ basis={{
+ visible: basis.visible,
+ }}
+>
+ <div
+ class={`relative flex flex-col h-full w-full justify-center items-center bg-layer-1-surface`}
+ >
+ <Map
+ bind:map
+ basis={{
+ zoom_click_off: true,
+ }}
+ >
+ {#if map_geop}
+ <MapMarkerArea
+ bind:map_geop
+ bind:map_geoc
+ basis={{
+ show_display: !!map_geop,
+ lc_geocode: basis.lc_geocode,
+ }}
+ />
+ {/if}
+ </Map>
+ <div
+ class={`absolute bottom-0 left-0 flex flex-row h-24 w-full px-4 justify-center items-start`}
+ >
+ <ButtonSimple
+ basis={{
+ label: `Choose location`,
+ callback: async () => {
+ await basis.lc_submit({ map_geoc, map_geop });
+ if (map) map.setCenter(cfg_map.coords.default);
+ },
+ }}
+ />
+ </div>
+ </div>
+</EnvelopeLowerFull>
diff --git a/apps-lib/src/lib/components/form/entry-line-idb.svelte b/apps-lib/src/lib/components/form/entry-line-idb.svelte
@@ -0,0 +1,66 @@
+<script lang="ts">
+ import { EntryWrap, Glyph, Input, LoadSymbol } from "$root";
+ import {
+ parse_layer,
+ type IEntryLineIdb,
+ type LoadingDimension,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: IEntryLineIdb;
+ } = $props();
+
+ const layer = $derived(
+ parse_layer(
+ typeof basis.wrap?.layer === `boolean` ? 0 : basis.wrap?.layer,
+ ),
+ );
+
+ const classes_layer = $derived(
+ typeof basis.wrap?.layer === `boolean`
+ ? ``
+ : `text-layer-${layer}-glyph`,
+ );
+
+ const loading_dim: LoadingDimension = $derived(
+ basis.wrap?.style === `guide` ? `md` : `sm`,
+ );
+</script>
+
+<EntryWrap basis={basis?.wrap}>
+ <Input
+ basis={{
+ ...basis.el,
+ classes: `h-entry_line ${basis.el.classes}`,
+ }}
+ />
+ {#if basis.loading}
+ <div
+ class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-4 justify-end items-center fade-in el-re`}
+ >
+ <LoadSymbol
+ basis={{
+ dim: loading_dim,
+ }}
+ />
+ </div>
+ {:else if basis.notify_inline}
+ {#if `glyph` in basis.notify_inline}
+ <div
+ class={`z-5 absolute el-re right-0 top-0 flex flex-row h-full pr-3 justify-end items-center translate-x-[34px] fade-in`}
+ >
+ <Glyph
+ basis={typeof basis.notify_inline.glyph === `string`
+ ? {
+ key: basis.notify_inline.glyph,
+ dim: `xs+`,
+ classes: `${classes_layer}`,
+ }
+ : basis.notify_inline.glyph}
+ />
+ </div>
+ {/if}
+ {/if}
+</EntryWrap>
diff --git a/apps-lib/src/lib/components/form/entry-line.svelte b/apps-lib/src/lib/components/form/entry-line.svelte
@@ -0,0 +1,69 @@
+<script lang="ts">
+ import { EntryWrap, Glyph, InputValue, LoadSymbol } from "$root";
+ import {
+ parse_layer,
+ type IEntryLine,
+ type LoadingDimension,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ value = $bindable(``),
+ }: {
+ basis: IEntryLine;
+ value: string;
+ } = $props();
+
+ const layer = $derived(
+ parse_layer(
+ typeof basis.wrap?.layer === `boolean` ? 0 : basis.wrap?.layer,
+ ),
+ );
+
+ const classes_layer = $derived(
+ typeof basis.wrap?.layer === `boolean`
+ ? ``
+ : `text-layer-${layer}-glyph`,
+ );
+
+ const loading_dim: LoadingDimension = $derived(
+ basis.wrap?.style === `guide` ? `md` : `sm`,
+ );
+</script>
+
+<EntryWrap basis={basis?.wrap}>
+ <InputValue
+ bind:value
+ basis={{
+ ...basis.el,
+ classes: `h-entry_line ${basis.el.classes}`,
+ }}
+ />
+ {#if basis.loading}
+ <div
+ class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-4 justify-end items-center fade-in el-re`}
+ >
+ <LoadSymbol
+ basis={{
+ dim: loading_dim,
+ }}
+ />
+ </div>
+ {:else if basis.notify_inline}
+ {#if `glyph` in basis.notify_inline}
+ <div
+ class={`z-5 absolute el-re right-0 top-0 flex flex-row h-full pr-3 justify-end items-center translate-x-[34px] fade-in`}
+ >
+ <Glyph
+ basis={typeof basis.notify_inline.glyph === `string`
+ ? {
+ key: basis.notify_inline.glyph,
+ dim: `xs+`,
+ classes: `${classes_layer}`,
+ }
+ : basis.notify_inline.glyph}
+ />
+ </div>
+ {/if}
+ {/if}
+</EntryWrap>
diff --git a/apps-lib/src/lib/components/form/entry-multiline.svelte b/apps-lib/src/lib/components/form/entry-multiline.svelte
@@ -0,0 +1,49 @@
+<script lang="ts">
+ import { Glyph, TextArea } from "$root";
+ import { fmt_cl, parse_layer, type IEntryMultiLine } from "@radroots/util";
+
+ let {
+ basis,
+ value = $bindable(``),
+ }: {
+ basis: IEntryMultiLine;
+ value: string;
+ } = $props();
+
+ const layer = $derived(
+ parse_layer(
+ typeof basis.wrap?.layer === `boolean` ? 0 : basis.wrap?.layer,
+ ),
+ );
+
+ const classes_layer = $derived(
+ typeof layer === `boolean`
+ ? `bg-transparent`
+ : `bg-layer-${layer}-surface`,
+ );
+</script>
+
+<div
+ id={basis.wrap?.id || null}
+ class={`${fmt_cl(basis.wrap?.classes)} relative el-re entry-textarea-wrap ${classes_layer}`}
+>
+ <TextArea bind:value basis={basis.el} />
+ {#if basis.notify_inline}
+ {#if `glyph` in basis.notify_inline}
+ <div
+ class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-3 justify-end items-center translate-x-[34px] fade-in `}
+ >
+ <Glyph
+ basis={typeof basis.notify_inline.glyph === `string`
+ ? {
+ key: basis.notify_inline.glyph,
+ dim: `xs+`,
+
+ classes: `text-layer-${layer}-glyph`,
+ }
+ : basis.notify_inline.glyph}
+ />
+ </div>
+ {/if}
+ {/if}
+</div>
diff --git a/apps-lib/src/lib/components/form/entry-select.svelte b/apps-lib/src/lib/components/form/entry-select.svelte
@@ -0,0 +1,48 @@
+<script lang="ts">
+ import { EntryWrap, Glyph, LoadSymbol, Select } from "$root";
+ import { fmt_cl, parse_layer, type IEntrySelect } from "@radroots/util";
+
+ let {
+ basis,
+ value = $bindable(``),
+ }: {
+ basis: IEntrySelect;
+ value: string;
+ } = $props();
+
+ const layer = $derived(
+ typeof basis?.wrap.layer === `boolean`
+ ? parse_layer(0)
+ : parse_layer(basis?.wrap.layer, 1),
+ );
+</script>
+
+<EntryWrap basis={basis?.wrap}>
+ {#if basis?.loading}
+ <div class={`flex flex-row w-full justify-center items-center`}>
+ <LoadSymbol basis={{ dim: `sm`, blades: 8 }} />
+ </div>
+ {:else}
+ <Select
+ bind:value
+ basis={{
+ ...basis.el,
+ classes: `${fmt_cl(basis.el.classes)} w-full`,
+ }}
+ />
+ {/if}
+ {#if !basis?.hide_arrows}
+ <div
+ class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-4 justify-end items-center`}
+ >
+ <Glyph
+ basis={{
+ key: `caret-up-down`,
+ dim: `xs`,
+
+ classes: `text-layer-${layer}-glyph_d`,
+ }}
+ />
+ </div>
+ {/if}
+</EntryWrap>
diff --git a/apps-lib/src/lib/components/form/entry-wrap.svelte b/apps-lib/src/lib/components/form/entry-wrap.svelte
@@ -0,0 +1,36 @@
+<script lang="ts">
+ import {
+ fmt_cl,
+ parse_layer,
+ type IBasisOpt,
+ type IEntryWrap,
+ } from "@radroots/util";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis = undefined,
+ children,
+ }: {
+ basis?: IBasisOpt<IEntryWrap>;
+ children: Snippet;
+ } = $props();
+
+ const layer = $derived(
+ typeof basis?.layer === `boolean`
+ ? parse_layer(0)
+ : parse_layer(basis?.layer),
+ );
+
+ const classes_layer = $derived(
+ typeof basis?.layer === `boolean`
+ ? `bg-transparent`
+ : `bg-layer-${layer}-surface ${basis?.style_a ? `active:bg-layer-${layer}-surface_a` : ``}`,
+ );
+</script>
+
+<button
+ id={basis?.id || null}
+ class={`${fmt_cl(basis?.classes)} relative entry-line-wrap ${!basis?.no_pad ? ` pl-6 pr-4` : ``} h-entry_${basis?.style ? basis.style : `line`} rounded-touch ${classes_layer} el-re`}
+>
+ {@render children()}
+</button>
diff --git a/apps-lib/src/lib/components/form/form-entry-input.svelte b/apps-lib/src/lib/components/form/form-entry-input.svelte
@@ -0,0 +1,44 @@
+<script lang="ts">
+ import { EntryLine, fmt_id, LayoutTrellisLine } from "$root";
+ import type { FormField, IIdOpt } from "@radroots/util";
+
+ let {
+ basis,
+ val = $bindable(``),
+ }: {
+ basis: IIdOpt & {
+ entry_label: string;
+ input_placeholder?: string;
+ input_field?: FormField;
+ };
+ val: string;
+ } = $props();
+
+ const id = $derived(basis.id || ``);
+</script>
+
+<LayoutTrellisLine
+ basis={{
+ label: {
+ value: basis.entry_label || ``,
+ },
+ }}
+>
+ <EntryLine
+ bind:value={val}
+ basis={{
+ wrap: {
+ id: id ? fmt_id(`${id}_wrap`) : undefined,
+ layer: 1,
+ },
+ el: {
+ // id: id ? fmt_id(id) : undefined,
+ layer: 1,
+ classes: `fade-in`,
+ placeholder: basis.input_placeholder || ``,
+ field: basis.input_field || undefined,
+ field_constrain: !!basis.input_field,
+ },
+ }}
+ />
+</LayoutTrellisLine>
diff --git a/apps-lib/src/lib/components/form/form-entry-price-quantity.svelte b/apps-lib/src/lib/components/form/form-entry-price-quantity.svelte
@@ -0,0 +1,113 @@
+<script lang="ts">
+ import {
+ EntryWrap,
+ fmt_id,
+ Input,
+ LayoutTrellisLine,
+ lib_fmt_price,
+ lib_parse_currency_marker,
+ lls,
+ Select,
+ } from "$root";
+ import {
+ fiat_currencies,
+ mass_units,
+ num_str,
+ type ElementCallbackValue,
+ type FormField,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ val_input_price = $bindable(``),
+ val_sel_currency = $bindable(``),
+ val_sel_qty_unit = $bindable(``),
+ }: {
+ basis: {
+ id?: string;
+ input_field?: FormField;
+ callback_input: ElementCallbackValue;
+ };
+ val_input_price: string;
+ val_sel_currency: string;
+ val_sel_qty_unit: string;
+ } = $props();
+
+ const id = $derived(basis.id ? basis.id : ``);
+</script>
+
+<LayoutTrellisLine
+ basis={{
+ label: {
+ value: `${$lls(`icu.*_price`, { value: `${$lls(`common.product`)}` })} (${val_sel_currency}/${`${$lls(`measurement.mass.unit.${val_sel_qty_unit}_ab`)}`})`,
+ },
+ }}
+>
+ <EntryWrap
+ basis={{
+ id: id ? fmt_id(`${id}_wrap`) : undefined,
+ layer: 1,
+ }}
+ >
+ <div class={`flex flex-row justify-start pr-1 items-center`}>
+ <Select
+ bind:value={val_sel_currency}
+ basis={{
+ id: id ? fmt_id(`${id}_sel_currency`) : undefined,
+ layer: 1,
+ sync: true,
+ classes: `w-fit font-sans font-[400] ${val_input_price ? `text-layer-1-glyph_d` : `text-layer-1-glyph_pl`} el-re`,
+ options: [
+ {
+ entries: fiat_currencies.map((i) => ({
+ value: `${i}`,
+ label: lib_parse_currency_marker(i),
+ })),
+ },
+ ],
+ }}
+ />
+ </div>
+ <Input
+ bind:value={val_input_price}
+ basis={{
+ id: id ? fmt_id(`${id}_input_price`) : undefined,
+ layer: 1,
+ sync: true,
+ placeholder: `${$lls(`icu.enter_the_*`, { value: `${$lls(`common.price`)}`.toLowerCase() })}`,
+ field: basis.input_field,
+ callback: basis.callback_input,
+ callback_blur: async ({ el }) => {
+ if (!el.value) return;
+ el.value = lib_fmt_price(el.value, val_sel_currency).slice(
+ 1,
+ );
+ //@todo fmt handles 'en' only
+ },
+ }}
+ />
+ <div class={`flex flex-row gap-2 justify-end items-center`}>
+ <p class={`font-sans font-[400] text-layer-1-glyph_d lowercase`}>
+ {num_str(1)}
+ </p>
+ <Select
+ bind:value={val_sel_qty_unit}
+ basis={{
+ id: id ? fmt_id(`${id}_sel_qty_unit`) : undefined,
+ sync: true,
+ layer: 1,
+ classes: `w-fit font-sans font-[400]`,
+ show_arrows: `r`,
+ options: [
+ {
+ entries: mass_units.map((i) => ({
+ value: i,
+ label: `${$lls(`measurement.mass.unit.${i}_ab`)}`.toLowerCase(),
+ })),
+ },
+ ],
+ }}
+ />
+ </div>
+ </EntryWrap>
+</LayoutTrellisLine>
diff --git a/apps-lib/src/lib/components/form/form-entry-price.svelte b/apps-lib/src/lib/components/form/form-entry-price.svelte
@@ -0,0 +1,118 @@
+<script lang="ts">
+ import {
+ EntryWrap,
+ fmt_id,
+ Input,
+ LayoutTrellisLine,
+ lib_fmt_price,
+ lib_parse_currency_marker,
+ lls,
+ Select,
+ } from "$root";
+ import {
+ fiat_currencies,
+ form_fields,
+ mass_units,
+ type ElementCallbackValue,
+ type IIdOpt,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ val_input_price = $bindable(``),
+ val_sel_currency = $bindable(``),
+ val_sel_quantity_unit = $bindable(``),
+ }: {
+ basis: IIdOpt & {
+ display_symbol?: boolean;
+ entry_label?: string;
+ input_placeholder?: string;
+ callback_input?: ElementCallbackValue;
+ };
+ val_input_price: string;
+ val_sel_currency: string;
+ val_sel_quantity_unit: string;
+ } = $props();
+
+ const id = $derived(basis.id ? basis.id : ``);
+</script>
+
+<LayoutTrellisLine
+ basis={{
+ label: {
+ value: basis.entry_label || ``,
+ },
+ }}
+>
+ <EntryWrap
+ basis={{
+ id: id ? fmt_id(`${id}_wrap`) : undefined,
+ layer: 1,
+ }}
+ >
+ <Input
+ bind:value={val_input_price}
+ basis={{
+ id: id ? fmt_id(`${id}_input_price`) : undefined,
+ layer: 1,
+ sync: true,
+ placeholder: basis.input_placeholder,
+ field: form_fields.price,
+ callback: basis.callback_input,
+ callback_blur: async ({ el }) => {
+ if (!el.value) return;
+ el.value = lib_fmt_price(el.value, val_sel_currency).slice(
+ 1,
+ );
+ //@todo fmt handles 'en' only
+ },
+ }}
+ />
+ <div
+ class={`absolute right-4 flex flex-row justify-start items-center`}
+ >
+ <Select
+ bind:value={val_sel_currency}
+ basis={{
+ id: id ? fmt_id(`${id}_sel_currency`) : undefined,
+ layer: 1,
+ sync: true,
+ classes: `w-fitfont-sans font-[400] el-re`,
+ options: [
+ {
+ entries: fiat_currencies.map((i) => ({
+ value: i,
+ label: basis.display_symbol
+ ? lib_parse_currency_marker(i)
+ : i.toUpperCase(),
+ })),
+ },
+ ],
+ }}
+ />
+ <div class={`flex flex-row px-2 justify-start items-center`}>
+ <p class={`font-sans font-[400] text-layer-0-glyph`}>
+ {`/`}
+ </p>
+ </div>
+ <Select
+ bind:value={val_sel_quantity_unit}
+ basis={{
+ id: id ? fmt_id(`${id}_sel_qty_unit`) : undefined,
+ sync: true,
+ layer: 1,
+ classes: `w-fit font-sans font-[400]`,
+ show_arrows: `r`,
+ options: [
+ {
+ entries: mass_units.map((i) => ({
+ value: i,
+ label: `${$lls(`measurement.mass.unit.${i}_ab`)}`.toLowerCase(),
+ })),
+ },
+ ],
+ }}
+ />
+ </div>
+ </EntryWrap>
+</LayoutTrellisLine>
diff --git a/apps-lib/src/lib/components/form/form-entry-quantity.svelte b/apps-lib/src/lib/components/form/form-entry-quantity.svelte
@@ -0,0 +1,103 @@
+<script lang="ts">
+ import {
+ EntryWrap,
+ fmt_id,
+ Input,
+ LayoutTrellisLine,
+ lls,
+ Select,
+ } from "$root";
+ import {
+ form_fields,
+ mass_units,
+ type ElementCallbackValue,
+ type IIdOpt,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ val_input_quantity = $bindable(``),
+ val_sel_quantity_unit = $bindable(``),
+ val_sel_quantity_label = $bindable(``),
+ }: {
+ basis: IIdOpt & {
+ display_symbol?: boolean;
+ entry_label?: string;
+ input_placeholder?: string;
+ callback_input?: ElementCallbackValue;
+ };
+ val_input_quantity: string;
+ val_sel_quantity_unit: string;
+ val_sel_quantity_label: string;
+ } = $props();
+
+ const id = $derived(basis.id ? basis.id : ``);
+</script>
+
+<LayoutTrellisLine
+ basis={{
+ label: {
+ value: basis.entry_label || ``,
+ },
+ }}
+>
+ <EntryWrap
+ basis={{
+ id: id ? fmt_id(`${id}_wrap`) : undefined,
+ layer: 1,
+ }}
+ >
+ <Input
+ bind:value={val_input_quantity}
+ basis={{
+ id: id ? fmt_id(`${id}_input_quantity`) : undefined,
+ layer: 1,
+ sync: true,
+ placeholder: basis.input_placeholder,
+ field: form_fields.quantity,
+ callback: basis.callback_input,
+ }}
+ />
+ <div
+ class={`absolute right-4 flex flex-row gap-1 justify-start items-center`}
+ >
+ <Select
+ bind:value={val_sel_quantity_unit}
+ basis={{
+ id: id ? fmt_id(`${id}_sel_quantity_unit`) : undefined,
+ layer: 1,
+ sync: true,
+ classes: `w-fit font-sans font-[400] el-re`,
+ options: [
+ {
+ entries: mass_units.map((i) => ({
+ value: i,
+ label: `${$lls(`measurement.mass.unit.${i}_ab`)}`.toLowerCase(),
+ })),
+ },
+ ],
+ }}
+ />
+ <Select
+ bind:value={val_sel_quantity_label}
+ basis={{
+ id: id ? fmt_id(`${id}_sel_quantity_label`) : undefined,
+ sync: true,
+ layer: 1,
+ classes: `w-fit font-sans font-[400] pr-2`,
+ show_arrows: `r`,
+ options: [
+ {
+ entries: [
+ {
+ value: `bag`,
+ label: `${$lls(`common.bag`)}`.toLowerCase(),
+ },
+ ],
+ },
+ ],
+ }}
+ />
+ </div>
+ </EntryWrap>
+</LayoutTrellisLine>
diff --git a/apps-lib/src/lib/components/form/form-entry-select-input.svelte b/apps-lib/src/lib/components/form/form-entry-select-input.svelte
@@ -0,0 +1,92 @@
+<script lang="ts">
+ import {
+ EntryLine,
+ EntrySelect,
+ fmt_id,
+ LayoutTrellisLine,
+ lls,
+ } from "$root";
+ import {
+ type CallbackPromiseGeneric,
+ type ElementCallbackSelect,
+ type FormField,
+ type ISelectOption,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ val_sel = $bindable(``),
+ val_sel_input = $bindable(``),
+ }: {
+ basis: {
+ id: string;
+ visible_input: boolean;
+ callback_select: ElementCallbackSelect;
+ callback_visible: CallbackPromiseGeneric<boolean>;
+ select_entries: ISelectOption<string>[];
+ input_placeholder?: string;
+ input_field?: FormField;
+ entry_label?: string;
+ };
+ val_sel: string;
+ val_sel_input: string;
+ } = $props();
+</script>
+
+<LayoutTrellisLine
+ basis={{
+ label: {
+ value: basis.entry_label || ``,
+ },
+ notify: basis.visible_input
+ ? {
+ label: {
+ value: `${$lls(`common.close`)}`,
+ },
+ callback: async () => {
+ await basis.callback_visible(false);
+ },
+ }
+ : undefined,
+ }}
+>
+ {#if !basis.visible_input}
+ <EntrySelect
+ bind:value={val_sel}
+ basis={{
+ wrap: {
+ id: fmt_id(`${basis.id}_wrap`),
+ layer: 1,
+ },
+ el: {
+ id: fmt_id(basis.id),
+ sync: true,
+ layer: 1,
+ options: [
+ {
+ entries: basis.select_entries,
+ },
+ ],
+ callback: basis.callback_select,
+ },
+ }}
+ />
+ {:else}
+ <EntryLine
+ bind:value={val_sel_input}
+ basis={{
+ wrap: {
+ id: fmt_id(`${basis.id}_wrap`),
+ layer: 1,
+ },
+ el: {
+ layer: 1,
+ classes: `fade-in`,
+ placeholder: basis.input_placeholder || ``,
+ field: basis.input_field || undefined,
+ field_constrain: !!basis.input_field,
+ },
+ }}
+ />
+ {/if}
+</LayoutTrellisLine>
diff --git a/apps-lib/src/lib/components/form/form-entry-select.svelte b/apps-lib/src/lib/components/form/form-entry-select.svelte
@@ -0,0 +1,54 @@
+<script lang="ts">
+ import { EntrySelect, fmt_id, LayoutTrellisLine } from "$root";
+ import {
+ type ElementCallbackSelect,
+ type IClOpt,
+ type IIdOpt,
+ type ISelectOption,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ val = $bindable(``),
+ }: {
+ basis: IIdOpt &
+ IClOpt & {
+ callback?: ElementCallbackSelect;
+ entries: ISelectOption<string>[];
+ entry_label?: string;
+ };
+ val: string;
+ } = $props();
+
+ const id = $derived(basis.id || ``);
+</script>
+
+<LayoutTrellisLine
+ basis={{
+ label: {
+ value: basis.entry_label || ``,
+ },
+ }}
+>
+ <EntrySelect
+ bind:value={val}
+ basis={{
+ wrap: {
+ id: id ? fmt_id(`${id}_wrap`) : undefined,
+ layer: 1,
+ },
+ el: {
+ id: id ? fmt_id(id) : undefined,
+ classes: basis.classes,
+ sync: true,
+ layer: 1,
+ options: [
+ {
+ entries: basis.entries,
+ },
+ ],
+ callback: basis.callback,
+ },
+ }}
+ />
+</LayoutTrellisLine>
diff --git a/apps-lib/src/lib/components/form/form-entry-textarea.svelte b/apps-lib/src/lib/components/form/form-entry-textarea.svelte
@@ -0,0 +1,51 @@
+<script lang="ts">
+ import { EntryMultiline, LayoutTrellisLine, fmt_id } from "$root";
+ import {
+ type ElementCallbackValueKeydown,
+ type FormField,
+ type IIdOpt,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ val = $bindable(``),
+ }: {
+ basis: IIdOpt & {
+ entry_label?: string;
+ placeholder?: string;
+ field?: FormField;
+ callback_keydown?: ElementCallbackValueKeydown<HTMLTextAreaElement>;
+ };
+ val: string;
+ } = $props();
+
+ const id = $derived(basis.id || ``);
+</script>
+
+<LayoutTrellisLine
+ basis={{
+ classes: `h-full`,
+ label: {
+ value: basis.entry_label || ``,
+ },
+ }}
+>
+ <EntryMultiline
+ bind:value={val}
+ basis={{
+ wrap: {
+ id: id ? fmt_id(`${id}_wrap`) : undefined,
+ classes: `h-full`,
+ },
+ el: {
+ classes: `h-full px-2 overflow-hidden`,
+ id: id ? fmt_id(id) : undefined,
+ sync: true,
+ placeholder: basis.placeholder || ``,
+ field: basis.field || undefined,
+ field_constrain: !!basis.field,
+ callback_keydown: basis.callback_keydown,
+ },
+ }}
+ />
+</LayoutTrellisLine>
diff --git a/apps-lib/src/lib/components/form/form-line-ledger-label-select-label.svelte b/apps-lib/src/lib/components/form/form-line-ledger-label-select-label.svelte
@@ -0,0 +1,36 @@
+<script lang="ts">
+ import { ascii } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: {
+ label: string;
+ };
+ } = $props();
+</script>
+
+<div class={`flex flex-row justify-start items-center`}>
+ <p
+ class={`pr-[13px] font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}
+ >
+ {`(${basis.label}`}
+ </p>
+ <div
+ class={`relative flex flex-row justify-start items-center -translate-x-[10px] -translate-y-[1px]`}
+ >
+ <p
+ class={`absolute font-sansd text-trellis_ti text-layer-0-glyph-label uppercase scale-y-[70%] scale-x-[80%] -translate-y-[1px]`}
+ >
+ {`${ascii.up}`}
+ </p>
+ <p
+ class={`absolute font-sansd text-trellis_ti text-layer-0-glyph-label uppercase scale-y-[70%] scale-x-[80%] translate-y-[2px]`}
+ >
+ {`${ascii.down}`}
+ </p>
+ </div>
+ <p class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}>
+ {`)`}
+ </p>
+</div>
diff --git a/apps-lib/src/lib/components/form/form-line-ledger-select.svelte b/apps-lib/src/lib/components/form/form-line-ledger-select.svelte
@@ -0,0 +1,86 @@
+<script lang="ts">
+ import { fmt_id, Input, Select } from "$root";
+ import {
+ type ElementCallbackSelect,
+ type ElementCallbackValueKeydown,
+ type IIdOpt,
+ type ISelectOption,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ value_input = $bindable(``),
+ value_sel = $bindable(``),
+ }: {
+ basis: IIdOpt & {
+ display_value?: string;
+ label?: string;
+ input: {
+ placeholder?: string;
+ callback_keydown?:
+ | ElementCallbackValueKeydown<HTMLInputElement>
+ | undefined;
+ };
+ select: {
+ entries: ISelectOption<string>[];
+ callback?: ElementCallbackSelect;
+ };
+ };
+ value_input?: string;
+ value_sel?: string;
+ } = $props();
+
+ const id = $derived(basis.id || ``);
+</script>
+
+<div class={`flex flex-col w-full gap-2 justify-start items-start`}>
+ {#if basis.label}
+ <div class={`flex flex-row w-full justify-start items-center`}>
+ <p
+ class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}
+ >
+ {basis.label}
+ </p>
+ </div>
+ {/if}
+ <div
+ class={`relative flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`}
+ >
+ {#if basis.display_value}
+ <p class={`font-sans font-[400] text-layer-0-glyph text-form_base`}>
+ {basis.display_value}
+ </p>
+ {:else}
+ <Input
+ bind:value={value_input}
+ basis={{
+ id: id ? fmt_id(`${id}_input`) : undefined,
+ layer: 0,
+ classes: `h-10 placeholder:text-[1.1rem]`,
+ placeholder: basis.input.placeholder || ``,
+ callback_keydown: basis.input.callback_keydown,
+ }}
+ />
+ <div
+ class={`absolute right-0 flex flex-row justify-center items-center`}
+ >
+ <Select
+ bind:value={value_sel}
+ basis={{
+ classes: `w-fit text-layer-1-glyph`,
+ id: id ? fmt_id(`${id}_sel`) : undefined,
+ sync: true,
+ layer: 1,
+ show_arrows: `r`,
+ options: [
+ {
+ entries: basis.select.entries,
+ },
+ ],
+ callback: basis.select.callback,
+ }}
+ />
+ </div>
+ {/if}
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/form/form-line-ledger.svelte b/apps-lib/src/lib/components/form/form-line-ledger.svelte
@@ -0,0 +1,88 @@
+<script lang="ts">
+ import { fmt_id, Input, SelectMenu } from "$root";
+ import {
+ type ElementCallbackValueKeydown,
+ type FormField,
+ type IIdOpt,
+ type ISelectOption,
+ } from "@radroots/util";
+ import FormLineLedgerLabelSelectLabel from "./form-line-ledger-label-select-label.svelte";
+
+ let {
+ basis,
+ value = $bindable(``),
+ value_label_sel = $bindable(``),
+ }: {
+ basis: IIdOpt & {
+ display_value?: string;
+ label?: string;
+ label_select?: {
+ label: string;
+ entries: ISelectOption<string>[];
+ };
+ input?: {
+ placeholder?: string;
+ field?: FormField;
+ callback_keydown?:
+ | ElementCallbackValueKeydown<HTMLInputElement>
+ | undefined;
+ };
+ };
+ value?: string;
+ value_label_sel?: string;
+ } = $props();
+
+ const id = $derived(basis.id || ``);
+</script>
+
+<div class={`flex flex-col w-full gap-2 justify-start items-start`}>
+ {#if basis.label}
+ <div class={`flex flex-row w-full justify-start gap-1 items-center`}>
+ <p
+ class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}
+ >
+ {basis.label}
+ </p>
+ {#if basis.label_select}
+ <SelectMenu
+ bind:value={value_label_sel}
+ basis={{
+ layer: 0,
+ options: [
+ {
+ entries: basis.label_select.entries,
+ },
+ ],
+ }}
+ >
+ <FormLineLedgerLabelSelectLabel
+ basis={{
+ label: basis.label_select.label,
+ }}
+ />
+ </SelectMenu>
+ {/if}
+ </div>
+ {/if}
+ <div
+ class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`}
+ >
+ {#if basis.display_value}
+ <p class={`font-sans font-[400] text-layer-0-glyph text-form_base`}>
+ {basis.display_value}
+ </p>
+ {:else if basis.input}
+ <Input
+ bind:value
+ basis={{
+ id: id ? fmt_id(id) : undefined,
+ layer: 0,
+ classes: `h-10 placeholder:text-[1.1rem]`,
+ field: basis.input.field,
+ placeholder: basis.input.placeholder || ``,
+ callback_keydown: basis.input.callback_keydown,
+ }}
+ />
+ {/if}
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/form/form-review-display.svelte b/apps-lib/src/lib/components/form/form-review-display.svelte
@@ -0,0 +1,48 @@
+<script lang="ts">
+ import { Glyph } from "$root";
+ import {
+ fmt_cl,
+ type CallbackPromise,
+ type IClOpt,
+ type IClOptWrap,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: IClOpt &
+ IClOptWrap & {
+ label: string;
+ value: string;
+ value_pl?: string;
+ handle_back: CallbackPromise;
+ };
+ } = $props();
+</script>
+
+<div
+ class={`${fmt_cl(basis.classes_wr)} flex flex-row h-6 w-full justify-between items-start`}
+>
+ <p
+ class={`font-sans font-[500] text-layer-1-glyph_d tracking-tight capitalize`}
+ >
+ {basis.label}
+ </p>
+ <button
+ class={`flex flex-row max-w-[210px] gap-1 justify-end items-start`}
+ onclick={async () => {}}
+ >
+ <p
+ class={`${fmt_cl(basis.classes)} font-sans font-[400] text-[1.05rem] text-justify truncate text-layer-1-glyph_d`}
+ >
+ {basis.value || basis.value_pl || ``}
+ </p>
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph pt-1`,
+ dim: `xs`,
+ key: `caret-right`,
+ }}
+ />
+ </button>
+</div>
diff --git a/apps-lib/src/lib/components/form/trade_field_display_kv.svelte b/apps-lib/src/lib/components/form/trade_field_display_kv.svelte
@@ -0,0 +1,86 @@
+<script lang="ts">
+ import { fmt_id, Glyph, idb } from "$root";
+ import {
+ el_focus,
+ fmt_cl,
+ type CallbackPromise,
+ type IClOpt,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: IClOpt & {
+ visible: boolean;
+ label: string;
+ display: IClOpt & {
+ undef?: string;
+ nostyle?: boolean;
+ } & (
+ | {
+ kv: string;
+ }
+ | {
+ value: string;
+ }
+ );
+
+ kv_wrap?: string;
+ handle_back: CallbackPromise;
+ };
+ } = $props();
+
+ const classes_undef = $derived(
+ basis.visible &&
+ ((`kv` in basis.display && !idb.get(fmt_id(basis.display.kv))) ||
+ (`value` in basis.display && !basis.display.value))
+ ? `opacity-60`
+ : ``,
+ );
+</script>
+
+<div
+ class={`${fmt_cl(basis.classes)} flex flex-row h-6 w-full justify-between items-start`}
+>
+ <p
+ class={`font-sans font-[500] text-layer-1-glyph_d tracking-tight capitalize`}
+ >
+ {basis.label}
+ </p>
+ {#if basis.visible}
+ <button
+ class={`flex flex-row max-w-[210px] gap-1 justify-end items-start`}
+ onclick={async () => {
+ await el_focus(
+ fmt_id(
+ basis.kv_wrap
+ ? basis.kv_wrap
+ : `kv` in basis.display
+ ? `${basis.display.kv}_wrap`
+ : ``,
+ ),
+ async () => await basis.handle_back(),
+ );
+ }}
+ >
+ <p
+ class={`${fmt_cl(basis.display.classes)} font-sans font-[400] text-[1.05rem] text-justify truncate text-layer-1-glyph_d ${classes_undef} ${basis.display.nostyle ? `` : `capitalize`}`}
+ >
+ {#if `kv` in basis.display}
+ {#await idb.get(fmt_id(basis.display.kv)) then kv_val}
+ {kv_val || basis.display.undef || ``}
+ {/await}
+ {:else}
+ {basis.display.value || basis.display.undef || ``}
+ {/if}
+ </p>
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph ${classes_undef} pt-1`,
+ dim: `xs`,
+ key: `caret-right`,
+ }}
+ />
+ </button>
+ {/if}
+</div>
diff --git a/apps-lib/src/lib/components/glyph/glyph-circle.svelte b/apps-lib/src/lib/components/glyph/glyph-circle.svelte
@@ -0,0 +1,20 @@
+<script lang="ts">
+ import { GlyphButton } from "$root";
+ import { type IGlyphCircle, fmt_cl, glyph_style_map } from "@radroots/util";
+
+ let { basis }: { basis: IGlyphCircle } = $props();
+
+ const styles = $derived(
+ basis?.glyph?.dim
+ ? glyph_style_map.get(basis?.glyph?.dim)
+ : glyph_style_map.get(`sm`),
+ );
+</script>
+
+{#if styles?.dim_1}
+ <div
+ class={`${fmt_cl(basis?.classes_wrap)} flex flex-col h-[${styles?.dim_1}px] w-[${styles?.dim_1}px] justify-center items-center rounded-full el-re`}
+ >
+ <GlyphButton basis={basis?.glyph}></GlyphButton>
+ </div>
+{/if}
diff --git a/apps-lib/src/lib/components/glyph/glyph.svelte b/apps-lib/src/lib/components/glyph/glyph.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+ import { fmt_cl, glyph_style_map, type IGlyph } from "@radroots/util";
+
+ let { basis }: { basis: IGlyph } = $props();
+
+ const styles = $derived(
+ basis?.dim ? glyph_style_map.get(basis.dim) : glyph_style_map.get(`sm`),
+ );
+
+ const weight = $derived(basis.weight ? `-${basis.weight}` : `-bold`);
+</script>
+
+<div
+ id={basis.id || null}
+ class={`${fmt_cl(basis.classes)} flex flex-row text-[${styles?.gl_1}px] justify-center items-center`}
+>
+ <i class={`ph${weight} ph-${basis.key}`}></i>
+</div>
diff --git a/apps-lib/src/lib/components/label/label-display.svelte b/apps-lib/src/lib/components/label/label-display.svelte
@@ -0,0 +1,30 @@
+<script lang="ts">
+ import { fmt_cl, parse_layer, type ILabelDisplay } from "@radroots/util";
+
+ let { basis }: { basis: ILabelDisplay } = $props();
+
+ const layer = $derived(
+ typeof basis?.layer === `boolean` ? 0 : parse_layer(basis?.layer, 1),
+ );
+
+ const classes_layer = $derived(
+ typeof layer === `boolean`
+ ? `bg-transparent`
+ : `bg-layer-${layer}-surface`,
+ );
+
+ const clases_style = $derived(
+ basis.style === `guide` ? `h-entry_guide rounded-touch` : ``,
+ );
+</script>
+
+<div
+ id={basis.id_wrap || null}
+ class={`${fmt_cl(basis.classes)} relative el-re entry-line-wrap px-2 ${classes_layer} ${clases_style}`}
+>
+ <p
+ class={`${fmt_cl(basis.label.classes)} font-sans font-[400] text-layer-0-glyph`}
+ >
+ {basis.label.value}
+ </p>
+</div>
diff --git a/apps-lib/src/lib/components/layout/layout-bottom-button.svelte b/apps-lib/src/lib/components/layout/layout-bottom-button.svelte
@@ -0,0 +1,12 @@
+<script lang="ts">
+ import { app_lo } from "$lib/store/app";
+ import type { Snippet } from "svelte";
+
+ let { children }: { children: Snippet } = $props();
+</script>
+
+<div
+ class={`z-10 absolute bottom-0 h-lo_bottom_button_${$app_lo} flex flex-row w-full px-4 justify-center items-center`}
+>
+ {@render children()}
+</div>
diff --git a/apps-lib/src/lib/components/layout/layout-page.svelte b/apps-lib/src/lib/components/layout/layout-page.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+ import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util";
+ 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/src/lib/components/layout/layout-trellis-line.svelte b/apps-lib/src/lib/components/layout/layout-trellis-line.svelte
@@ -0,0 +1,61 @@
+<script lang="ts">
+ import { Glyph } from "$root";
+ import {
+ fmt_cl,
+ type IBasisOpt,
+ type ILayoutTrellisLine,
+ } from "@radroots/util";
+ import type { Snippet } from "svelte";
+ import { fade } from "svelte/transition";
+
+ let {
+ basis = undefined,
+ children,
+ }: {
+ basis?: IBasisOpt<ILayoutTrellisLine>;
+ children: Snippet;
+ } = $props();
+</script>
+
+<div
+ 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
+ class={`flex flex-row h-5 w-full px-2 gap-[6px] justify-start items-center`}
+ >
+ {#if basis?.label && `value` in basis?.label}
+ <p
+ class={`${fmt_cl(basis?.label.classes)} font-sans font-[400] uppercase text-layer-0-glyph text-sm`}
+ >
+ {basis?.label.value}
+ </p>
+ {/if}
+ {#if basis?.notify}
+ <button
+ in:fade={{ duration: 200 }}
+ out:fade={{ delay: 50, duration: 200 }}
+ onclick={async () => {
+ await basis?.notify?.callback();
+ }}
+ class={`${fmt_cl(basis?.notify.classes)} flex flex-row gap-1 justify-end items-center text-layer-0-glyph/80 el-re`}
+ >
+ {#if `glyph` in basis?.notify && basis?.notify?.glyph && basis.notify.glyph_first}
+ <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-xs`}
+ >
+ {basis?.notify.label.value}
+ </p>
+ {/if}
+ {#if `glyph` in basis?.notify && basis?.notify?.glyph && !basis.notify.glyph_first}
+ <Glyph basis={basis.notify.glyph} />
+ {/if}
+ </button>
+ {/if}
+ </div>
+ {/if}
+ {@render children()}
+</div>
diff --git a/apps-lib/src/lib/components/layout/layout-trellis.svelte b/apps-lib/src/lib/components/layout/layout-trellis.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+ import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util";
+ 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 pb-12 gap-6 justify-center items-center scroll-hide`}
+>
+ {@render children()}
+</div>
diff --git a/apps-lib/src/lib/components/layout/layout-view.svelte b/apps-lib/src/lib/components/layout/layout-view.svelte
@@ -0,0 +1,50 @@
+<script lang="ts">
+ import { nav_blur, ph_blur, tabs_blur } from "$root";
+ import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util";
+ 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);
+ };
+
+ //${$layout_view_cover ? `` : `pt-h_lo_view_${$app_lo}`}
+</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/src/lib/components/layout/layout-window.svelte b/apps-lib/src/lib/components/layout/layout-window.svelte
@@ -0,0 +1,38 @@
+<script lang="ts">
+ import {
+ app_lo,
+ app_tilt,
+ app_toast,
+ app_win,
+ envelope_tilt,
+ envelope_visible,
+ } from "$root";
+ import { onMount } from "svelte";
+
+ let { children } = $props();
+
+ onMount(async () => {
+ try {
+ app_win.set({ h: window.innerHeight, w: window.innerWidth });
+ app_toast.set(false);
+ } catch (e) {
+ console.log(`(layout mount) `, e);
+ } finally {
+ }
+ });
+
+ envelope_visible.subscribe(async (_envelope_visible) => {
+ if (_envelope_visible && $envelope_tilt) app_tilt.set(true);
+ else app_tilt.set(false);
+ });
+</script>
+
+<div
+ class={`relative lg:hidden flex flex-col h-[100vh] w-full bg-layer-0-surface ${$app_tilt ? `scale-y-[96%] translate-y-4 rounded-t-[3rem]` : ``} overflow-x-hidden overflow-y-scroll scroll-hide delay-75 duration-200 el-re`}
+>
+ {#if $app_lo}
+ <div class={`flex flex-col h-full w-full`}>
+ {@render children()}
+ </div>
+ {/if}
+</div>
diff --git a/apps-lib/src/lib/components/lib/css.svelte b/apps-lib/src/lib/components/lib/css.svelte
@@ -0,0 +1 @@
+<div class="hidden -bottom-lo_bottom_button_ios0 -bottom-lo_bottom_button_ios1 -bottom-lo_bottom_button_webm0 -bottom-lo_bottom_button_webm1 -bottom-nav_page_header_ios0 -bottom-nav_page_header_ios1 -bottom-nav_page_header_webm0 -bottom-nav_page_header_webm1 -bottom-nav_page_toolbar_ios0 -bottom-nav_page_toolbar_ios1 -bottom-nav_page_toolbar_webm0 -bottom-nav_page_toolbar_webm1 -bottom-nav_tabs_ios0 -bottom-nav_tabs_ios1 -bottom-nav_tabs_webm0 -bottom-nav_tabs_webm1 -left-lo_ios0 -left-lo_ios1 -left-lo_textdesc_ios0 -left-lo_textdesc_ios1 -left-lo_textdesc_webm0 -left-lo_textdesc_webm1 -left-lo_webm0 -left-lo_webm1 -right-lo_ios0 -right-lo_ios1 -right-lo_textdesc_ios0 -right-lo_textdesc_ios1 -right-lo_textdesc_webm0 -right-lo_textdesc_webm1 -right-lo_webm0 -right-lo_webm1 -top-lo_bottom_button_ios0 -top-lo_bottom_button_ios1 -top-lo_bottom_button_webm0 -top-lo_bottom_button_webm1 -top-nav_page_header_ios0 -top-nav_page_header_ios1 -top-nav_page_header_webm0 -top-nav_page_header_webm1 -top-nav_page_toolbar_ios0 -top-nav_page_toolbar_ios1 -top-nav_page_toolbar_webm0 -top-nav_page_toolbar_webm1 -top-nav_tabs_ios0 -top-nav_tabs_ios1 -top-nav_tabs_webm0 -top-nav_tabs_webm1 -translate-x-w_lo_ios0 -translate-x-w_lo_ios1 -translate-x-w_lo_textdesc_ios0 -translate-x-w_lo_textdesc_ios1 -translate-x-w_lo_textdesc_webm0 -translate-x-w_lo_textdesc_webm1 -translate-x-w_lo_webm0 -translate-x-w_lo_webm1 -translate-y-h_lo_bottom_button_ios0 -translate-y-h_lo_bottom_button_ios1 -translate-y-h_lo_bottom_button_webm0 -translate-y-h_lo_bottom_button_webm1 -translate-y-h_nav_page_header_ios0 -translate-y-h_nav_page_header_ios1 -translate-y-h_nav_page_header_webm0 -translate-y-h_nav_page_header_webm1 -translate-y-h_nav_page_toolbar_ios0 -translate-y-h_nav_page_toolbar_ios1 -translate-y-h_nav_page_toolbar_webm0 -translate-y-h_nav_page_toolbar_webm1 -translate-y-h_nav_tabs_ios0 -translate-y-h_nav_tabs_ios1 -translate-y-h_nav_tabs_webm0 -translate-y-h_nav_tabs_webm1 active:bg-layer-0-glyph active:bg-layer-0-glyph-hl active:bg-layer-0-glyph-hl_a active:bg-layer-0-glyph-label active:bg-layer-0-glyph-shade active:bg-layer-0-glyph_a active:bg-layer-0-glyph_pl active:bg-layer-0-surface active:bg-layer-0-surface-blur active:bg-layer-0-surface-edge active:bg-layer-0-surface_a active:bg-layer-0-surface_w active:bg-layer-1-glyph active:bg-layer-1-glyph-hl active:bg-layer-1-glyph-hl_a active:bg-layer-1-glyph-label active:bg-layer-1-glyph-shade active:bg-layer-1-glyph_a active:bg-layer-1-glyph_d active:bg-layer-1-glyph_pl active:bg-layer-1-surface active:bg-layer-1-surface-edge active:bg-layer-1-surface-err active:bg-layer-1-surface-focus active:bg-layer-1-surface_a active:bg-layer-2-glyph active:bg-layer-2-glyph-hl active:bg-layer-2-glyph-hl_a active:bg-layer-2-glyph-shade active:bg-layer-2-glyph_a active:bg-layer-2-glyph_d active:bg-layer-2-glyph_pl active:bg-layer-2-surface active:bg-layer-2-surface-edge active:bg-layer-2-surface_a active:bg-radroots-accent-focus active:border-layer-0-glyph active:border-layer-0-glyph-hl active:border-layer-0-glyph-hl_a active:border-layer-0-glyph-label active:border-layer-0-glyph-shade active:border-layer-0-glyph_a active:border-layer-0-glyph_pl active:border-layer-0-surface active:border-layer-0-surface-blur active:border-layer-0-surface-edge active:border-layer-0-surface_a active:border-layer-0-surface_w active:border-layer-1-glyph active:border-layer-1-glyph-hl active:border-layer-1-glyph-hl_a active:border-layer-1-glyph-label active:border-layer-1-glyph-shade active:border-layer-1-glyph_a active:border-layer-1-glyph_d active:border-layer-1-glyph_pl active:border-layer-1-surface active:border-layer-1-surface-edge active:border-layer-1-surface-err active:border-layer-1-surface-focus active:border-layer-1-surface_a active:border-layer-2-glyph active:border-layer-2-glyph-hl active:border-layer-2-glyph-hl_a active:border-layer-2-glyph-shade active:border-layer-2-glyph_a active:border-layer-2-glyph_d active:border-layer-2-glyph_pl active:border-layer-2-surface active:border-layer-2-surface-edge active:border-layer-2-surface_a active:border-radroots-accent-focus active:text-layer-0-glyph active:text-layer-0-glyph-hl active:text-layer-0-glyph-hl_a active:text-layer-0-glyph-label active:text-layer-0-glyph-shade active:text-layer-0-glyph_a active:text-layer-0-glyph_pl active:text-layer-0-surface active:text-layer-0-surface-blur active:text-layer-0-surface-edge active:text-layer-0-surface_a active:text-layer-0-surface_w active:text-layer-1-glyph active:text-layer-1-glyph-hl active:text-layer-1-glyph-hl_a active:text-layer-1-glyph-label active:text-layer-1-glyph-shade active:text-layer-1-glyph_a active:text-layer-1-glyph_d active:text-layer-1-glyph_pl active:text-layer-1-surface active:text-layer-1-surface-edge active:text-layer-1-surface-err active:text-layer-1-surface-focus active:text-layer-1-surface_a active:text-layer-2-glyph active:text-layer-2-glyph-hl active:text-layer-2-glyph-hl_a active:text-layer-2-glyph-shade active:text-layer-2-glyph_a active:text-layer-2-glyph_d active:text-layer-2-glyph_pl active:text-layer-2-surface active:text-layer-2-surface-edge active:text-layer-2-surface_a active:text-radroots-accent-focus bg-layer-0-glyph bg-layer-0-glyph-hl bg-layer-0-glyph-hl_a bg-layer-0-glyph-label bg-layer-0-glyph-shade bg-layer-0-glyph_a bg-layer-0-glyph_pl bg-layer-0-surface bg-layer-0-surface-blur bg-layer-0-surface-edge bg-layer-0-surface_a bg-layer-0-surface_w bg-layer-1-glyph bg-layer-1-glyph-hl bg-layer-1-glyph-hl_a bg-layer-1-glyph-label bg-layer-1-glyph-shade bg-layer-1-glyph_a bg-layer-1-glyph_d bg-layer-1-glyph_pl bg-layer-1-surface bg-layer-1-surface-edge bg-layer-1-surface-err bg-layer-1-surface-focus bg-layer-1-surface_a bg-layer-2-glyph bg-layer-2-glyph-hl bg-layer-2-glyph-hl_a bg-layer-2-glyph-shade bg-layer-2-glyph_a bg-layer-2-glyph_d bg-layer-2-glyph_pl bg-layer-2-surface bg-layer-2-surface-edge bg-layer-2-surface_a bg-radroots-accent-focus border-layer-0-glyph border-layer-0-glyph-hl border-layer-0-glyph-hl_a border-layer-0-glyph-label border-layer-0-glyph-shade border-layer-0-glyph_a border-layer-0-glyph_pl border-layer-0-surface border-layer-0-surface-blur border-layer-0-surface-edge border-layer-0-surface_a border-layer-0-surface_w border-layer-1-glyph border-layer-1-glyph-hl border-layer-1-glyph-hl_a border-layer-1-glyph-label border-layer-1-glyph-shade border-layer-1-glyph_a border-layer-1-glyph_d border-layer-1-glyph_pl border-layer-1-surface border-layer-1-surface-edge border-layer-1-surface-err border-layer-1-surface-focus border-layer-1-surface_a border-layer-2-glyph border-layer-2-glyph-hl border-layer-2-glyph-hl_a border-layer-2-glyph-shade border-layer-2-glyph_a border-layer-2-glyph_d border-layer-2-glyph_pl border-layer-2-surface border-layer-2-surface-edge border-layer-2-surface_a border-radroots-accent-focus bottom-lo_bottom_button_ios0 bottom-lo_bottom_button_ios1 bottom-lo_bottom_button_webm0 bottom-lo_bottom_button_webm1 bottom-nav_page_header_ios0 bottom-nav_page_header_ios1 bottom-nav_page_header_webm0 bottom-nav_page_header_webm1 bottom-nav_page_toolbar_ios0 bottom-nav_page_toolbar_ios1 bottom-nav_page_toolbar_webm0 bottom-nav_page_toolbar_webm1 bottom-nav_tabs_ios0 bottom-nav_tabs_ios1 bottom-nav_tabs_webm0 bottom-nav_tabs_webm1 focus:bg-layer-0-glyph focus:bg-layer-0-glyph-hl focus:bg-layer-0-glyph-hl_a focus:bg-layer-0-glyph-label focus:bg-layer-0-glyph-shade focus:bg-layer-0-glyph_a focus:bg-layer-0-glyph_pl focus:bg-layer-0-surface focus:bg-layer-0-surface-blur focus:bg-layer-0-surface-edge focus:bg-layer-0-surface_a focus:bg-layer-0-surface_w focus:bg-layer-1-glyph focus:bg-layer-1-glyph-hl focus:bg-layer-1-glyph-hl_a focus:bg-layer-1-glyph-label focus:bg-layer-1-glyph-shade focus:bg-layer-1-glyph_a focus:bg-layer-1-glyph_d focus:bg-layer-1-glyph_pl focus:bg-layer-1-surface focus:bg-layer-1-surface-edge focus:bg-layer-1-surface-err focus:bg-layer-1-surface-focus focus:bg-layer-1-surface_a focus:bg-layer-2-glyph focus:bg-layer-2-glyph-hl focus:bg-layer-2-glyph-hl_a focus:bg-layer-2-glyph-shade focus:bg-layer-2-glyph_a focus:bg-layer-2-glyph_d focus:bg-layer-2-glyph_pl focus:bg-layer-2-surface focus:bg-layer-2-surface-edge focus:bg-layer-2-surface_a focus:bg-radroots-accent-focus focus:border-layer-0-glyph focus:border-layer-0-glyph-hl focus:border-layer-0-glyph-hl_a focus:border-layer-0-glyph-label focus:border-layer-0-glyph-shade focus:border-layer-0-glyph_a focus:border-layer-0-glyph_pl focus:border-layer-0-surface focus:border-layer-0-surface-blur focus:border-layer-0-surface-edge focus:border-layer-0-surface_a focus:border-layer-0-surface_w focus:border-layer-1-glyph focus:border-layer-1-glyph-hl focus:border-layer-1-glyph-hl_a focus:border-layer-1-glyph-label focus:border-layer-1-glyph-shade focus:border-layer-1-glyph_a focus:border-layer-1-glyph_d focus:border-layer-1-glyph_pl focus:border-layer-1-surface focus:border-layer-1-surface-edge focus:border-layer-1-surface-err focus:border-layer-1-surface-focus focus:border-layer-1-surface_a focus:border-layer-2-glyph focus:border-layer-2-glyph-hl focus:border-layer-2-glyph-hl_a focus:border-layer-2-glyph-shade focus:border-layer-2-glyph_a focus:border-layer-2-glyph_d focus:border-layer-2-glyph_pl focus:border-layer-2-surface focus:border-layer-2-surface-edge focus:border-layer-2-surface_a focus:border-radroots-accent-focus focus:text-layer-0-glyph focus:text-layer-0-glyph-hl focus:text-layer-0-glyph-hl_a focus:text-layer-0-glyph-label focus:text-layer-0-glyph-shade focus:text-layer-0-glyph_a focus:text-layer-0-glyph_pl focus:text-layer-0-surface focus:text-layer-0-surface-blur focus:text-layer-0-surface-edge focus:text-layer-0-surface_a focus:text-layer-0-surface_w focus:text-layer-1-glyph focus:text-layer-1-glyph-hl focus:text-layer-1-glyph-hl_a focus:text-layer-1-glyph-label focus:text-layer-1-glyph-shade focus:text-layer-1-glyph_a focus:text-layer-1-glyph_d focus:text-layer-1-glyph_pl focus:text-layer-1-surface focus:text-layer-1-surface-edge focus:text-layer-1-surface-err focus:text-layer-1-surface-focus focus:text-layer-1-surface_a focus:text-layer-2-glyph focus:text-layer-2-glyph-hl focus:text-layer-2-glyph-hl_a focus:text-layer-2-glyph-shade focus:text-layer-2-glyph_a focus:text-layer-2-glyph_d focus:text-layer-2-glyph_pl focus:text-layer-2-surface focus:text-layer-2-surface-edge focus:text-layer-2-surface_a focus:text-radroots-accent-focus group-active:bg-layer-0-glyph group-active:bg-layer-0-glyph-hl group-active:bg-layer-0-glyph-hl_a group-active:bg-layer-0-glyph-label group-active:bg-layer-0-glyph-shade group-active:bg-layer-0-glyph_a group-active:bg-layer-0-glyph_pl group-active:bg-layer-0-surface group-active:bg-layer-0-surface-blur group-active:bg-layer-0-surface-edge group-active:bg-layer-0-surface_a group-active:bg-layer-0-surface_w group-active:bg-layer-1-glyph group-active:bg-layer-1-glyph-hl group-active:bg-layer-1-glyph-hl_a group-active:bg-layer-1-glyph-label group-active:bg-layer-1-glyph-shade group-active:bg-layer-1-glyph_a group-active:bg-layer-1-glyph_d group-active:bg-layer-1-glyph_pl group-active:bg-layer-1-surface group-active:bg-layer-1-surface-edge group-active:bg-layer-1-surface-err group-active:bg-layer-1-surface-focus group-active:bg-layer-1-surface_a group-active:bg-layer-2-glyph group-active:bg-layer-2-glyph-hl group-active:bg-layer-2-glyph-hl_a group-active:bg-layer-2-glyph-shade group-active:bg-layer-2-glyph_a group-active:bg-layer-2-glyph_d group-active:bg-layer-2-glyph_pl group-active:bg-layer-2-surface group-active:bg-layer-2-surface-edge group-active:bg-layer-2-surface_a group-active:bg-radroots-accent-focus group-active:border-layer-0-glyph group-active:border-layer-0-glyph-hl group-active:border-layer-0-glyph-hl_a group-active:border-layer-0-glyph-label group-active:border-layer-0-glyph-shade group-active:border-layer-0-glyph_a group-active:border-layer-0-glyph_pl group-active:border-layer-0-surface group-active:border-layer-0-surface-blur group-active:border-layer-0-surface-edge group-active:border-layer-0-surface_a group-active:border-layer-0-surface_w group-active:border-layer-1-glyph group-active:border-layer-1-glyph-hl group-active:border-layer-1-glyph-hl_a group-active:border-layer-1-glyph-label group-active:border-layer-1-glyph-shade group-active:border-layer-1-glyph_a group-active:border-layer-1-glyph_d group-active:border-layer-1-glyph_pl group-active:border-layer-1-surface group-active:border-layer-1-surface-edge group-active:border-layer-1-surface-err group-active:border-layer-1-surface-focus group-active:border-layer-1-surface_a group-active:border-layer-2-glyph group-active:border-layer-2-glyph-hl group-active:border-layer-2-glyph-hl_a group-active:border-layer-2-glyph-shade group-active:border-layer-2-glyph_a group-active:border-layer-2-glyph_d group-active:border-layer-2-glyph_pl group-active:border-layer-2-surface group-active:border-layer-2-surface-edge group-active:border-layer-2-surface_a group-active:border-radroots-accent-focus group-active:text-layer-0-glyph group-active:text-layer-0-glyph-hl group-active:text-layer-0-glyph-hl_a group-active:text-layer-0-glyph-label group-active:text-layer-0-glyph-shade group-active:text-layer-0-glyph_a group-active:text-layer-0-glyph_pl group-active:text-layer-0-surface group-active:text-layer-0-surface-blur group-active:text-layer-0-surface-edge group-active:text-layer-0-surface_a group-active:text-layer-0-surface_w group-active:text-layer-1-glyph group-active:text-layer-1-glyph-hl group-active:text-layer-1-glyph-hl_a group-active:text-layer-1-glyph-label group-active:text-layer-1-glyph-shade group-active:text-layer-1-glyph_a group-active:text-layer-1-glyph_d group-active:text-layer-1-glyph_pl group-active:text-layer-1-surface group-active:text-layer-1-surface-edge group-active:text-layer-1-surface-err group-active:text-layer-1-surface-focus group-active:text-layer-1-surface_a group-active:text-layer-2-glyph group-active:text-layer-2-glyph-hl group-active:text-layer-2-glyph-hl_a group-active:text-layer-2-glyph-shade group-active:text-layer-2-glyph_a group-active:text-layer-2-glyph_d group-active:text-layer-2-glyph_pl group-active:text-layer-2-surface group-active:text-layer-2-surface-edge group-active:text-layer-2-surface_a group-active:text-radroots-accent-focus group-focus:bg-layer-0-glyph group-focus:bg-layer-0-glyph-hl group-focus:bg-layer-0-glyph-hl_a group-focus:bg-layer-0-glyph-label group-focus:bg-layer-0-glyph-shade group-focus:bg-layer-0-glyph_a group-focus:bg-layer-0-glyph_pl group-focus:bg-layer-0-surface group-focus:bg-layer-0-surface-blur group-focus:bg-layer-0-surface-edge group-focus:bg-layer-0-surface_a group-focus:bg-layer-0-surface_w group-focus:bg-layer-1-glyph group-focus:bg-layer-1-glyph-hl group-focus:bg-layer-1-glyph-hl_a group-focus:bg-layer-1-glyph-label group-focus:bg-layer-1-glyph-shade group-focus:bg-layer-1-glyph_a group-focus:bg-layer-1-glyph_d group-focus:bg-layer-1-glyph_pl group-focus:bg-layer-1-surface group-focus:bg-layer-1-surface-edge group-focus:bg-layer-1-surface-err group-focus:bg-layer-1-surface-focus group-focus:bg-layer-1-surface_a group-focus:bg-layer-2-glyph group-focus:bg-layer-2-glyph-hl group-focus:bg-layer-2-glyph-hl_a group-focus:bg-layer-2-glyph-shade group-focus:bg-layer-2-glyph_a group-focus:bg-layer-2-glyph_d group-focus:bg-layer-2-glyph_pl group-focus:bg-layer-2-surface group-focus:bg-layer-2-surface-edge group-focus:bg-layer-2-surface_a group-focus:bg-radroots-accent-focus group-focus:border-layer-0-glyph group-focus:border-layer-0-glyph-hl group-focus:border-layer-0-glyph-hl_a group-focus:border-layer-0-glyph-label group-focus:border-layer-0-glyph-shade group-focus:border-layer-0-glyph_a group-focus:border-layer-0-glyph_pl group-focus:border-layer-0-surface group-focus:border-layer-0-surface-blur group-focus:border-layer-0-surface-edge group-focus:border-layer-0-surface_a group-focus:border-layer-0-surface_w group-focus:border-layer-1-glyph group-focus:border-layer-1-glyph-hl group-focus:border-layer-1-glyph-hl_a group-focus:border-layer-1-glyph-label group-focus:border-layer-1-glyph-shade group-focus:border-layer-1-glyph_a group-focus:border-layer-1-glyph_d group-focus:border-layer-1-glyph_pl group-focus:border-layer-1-surface group-focus:border-layer-1-surface-edge group-focus:border-layer-1-surface-err group-focus:border-layer-1-surface-focus group-focus:border-layer-1-surface_a group-focus:border-layer-2-glyph group-focus:border-layer-2-glyph-hl group-focus:border-layer-2-glyph-hl_a group-focus:border-layer-2-glyph-shade group-focus:border-layer-2-glyph_a group-focus:border-layer-2-glyph_d group-focus:border-layer-2-glyph_pl group-focus:border-layer-2-surface group-focus:border-layer-2-surface-edge group-focus:border-layer-2-surface_a group-focus:border-radroots-accent-focus group-focus:text-layer-0-glyph group-focus:text-layer-0-glyph-hl group-focus:text-layer-0-glyph-hl_a group-focus:text-layer-0-glyph-label group-focus:text-layer-0-glyph-shade group-focus:text-layer-0-glyph_a group-focus:text-layer-0-glyph_pl group-focus:text-layer-0-surface group-focus:text-layer-0-surface-blur group-focus:text-layer-0-surface-edge group-focus:text-layer-0-surface_a group-focus:text-layer-0-surface_w group-focus:text-layer-1-glyph group-focus:text-layer-1-glyph-hl group-focus:text-layer-1-glyph-hl_a group-focus:text-layer-1-glyph-label group-focus:text-layer-1-glyph-shade group-focus:text-layer-1-glyph_a group-focus:text-layer-1-glyph_d group-focus:text-layer-1-glyph_pl group-focus:text-layer-1-surface group-focus:text-layer-1-surface-edge group-focus:text-layer-1-surface-err group-focus:text-layer-1-surface-focus group-focus:text-layer-1-surface_a group-focus:text-layer-2-glyph group-focus:text-layer-2-glyph-hl group-focus:text-layer-2-glyph-hl_a group-focus:text-layer-2-glyph-shade group-focus:text-layer-2-glyph_a group-focus:text-layer-2-glyph_d group-focus:text-layer-2-glyph_pl group-focus:text-layer-2-surface group-focus:text-layer-2-surface-edge group-focus:text-layer-2-surface_a group-focus:text-radroots-accent-focus h-[12px] h-[16px] h-[17px] h-[18px] h-[20px] h-[22px] h-[24px] h-[28px] h-[36px] h-lo_bottom_button_ios0 h-lo_bottom_button_ios1 h-lo_bottom_button_webm0 h-lo_bottom_button_webm1 h-nav_page_header_ios0 h-nav_page_header_ios1 h-nav_page_header_webm0 h-nav_page_header_webm1 h-nav_page_toolbar_ios0 h-nav_page_toolbar_ios1 h-nav_page_toolbar_webm0 h-nav_page_toolbar_webm1 h-nav_tabs_ios0 h-nav_tabs_ios1 h-nav_tabs_webm0 h-nav_tabs_webm1 left-lo_ios0 left-lo_ios1 left-lo_textdesc_ios0 left-lo_textdesc_ios1 left-lo_textdesc_webm0 left-lo_textdesc_webm1 left-lo_webm0 left-lo_webm1 max-h-lo_bottom_button_ios0 max-h-lo_bottom_button_ios1 max-h-lo_bottom_button_webm0 max-h-lo_bottom_button_webm1 max-h-nav_page_header_ios0 max-h-nav_page_header_ios1 max-h-nav_page_header_webm0 max-h-nav_page_header_webm1 max-h-nav_page_toolbar_ios0 max-h-nav_page_toolbar_ios1 max-h-nav_page_toolbar_webm0 max-h-nav_page_toolbar_webm1 max-h-nav_tabs_ios0 max-h-nav_tabs_ios1 max-h-nav_tabs_webm0 max-h-nav_tabs_webm1 max-w-lo_ios0 max-w-lo_ios1 max-w-lo_textdesc_ios0 max-w-lo_textdesc_ios1 max-w-lo_textdesc_webm0 max-w-lo_textdesc_webm1 max-w-lo_webm0 max-w-lo_webm1 min-h-lo_bottom_button_ios0 min-h-lo_bottom_button_ios1 min-h-lo_bottom_button_webm0 min-h-lo_bottom_button_webm1 min-h-nav_page_header_ios0 min-h-nav_page_header_ios1 min-h-nav_page_header_webm0 min-h-nav_page_header_webm1 min-h-nav_page_toolbar_ios0 min-h-nav_page_toolbar_ios1 min-h-nav_page_toolbar_webm0 min-h-nav_page_toolbar_webm1 min-h-nav_tabs_ios0 min-h-nav_tabs_ios1 min-h-nav_tabs_webm0 min-h-nav_tabs_webm1 min-w-lo_ios0 min-w-lo_ios1 min-w-lo_textdesc_ios0 min-w-lo_textdesc_ios1 min-w-lo_textdesc_webm0 min-w-lo_textdesc_webm1 min-w-lo_webm0 min-w-lo_webm1 pb-h_lo_bottom_button_ios0 pb-h_lo_bottom_button_ios1 pb-h_lo_bottom_button_webm0 pb-h_lo_bottom_button_webm1 pb-h_nav_page_header_ios0 pb-h_nav_page_header_ios1 pb-h_nav_page_header_webm0 pb-h_nav_page_header_webm1 pb-h_nav_page_toolbar_ios0 pb-h_nav_page_toolbar_ios1 pb-h_nav_page_toolbar_webm0 pb-h_nav_page_toolbar_webm1 pb-h_nav_tabs_ios0 pb-h_nav_tabs_ios1 pb-h_nav_tabs_webm0 pb-h_nav_tabs_webm1 pl-w_lo_ios0 pl-w_lo_ios1 pl-w_lo_textdesc_ios0 pl-w_lo_textdesc_ios1 pl-w_lo_textdesc_webm0 pl-w_lo_textdesc_webm1 pl-w_lo_webm0 pl-w_lo_webm1 pr-w_lo_ios0 pr-w_lo_ios1 pr-w_lo_textdesc_ios0 pr-w_lo_textdesc_ios1 pr-w_lo_textdesc_webm0 pr-w_lo_textdesc_webm1 pr-w_lo_webm0 pr-w_lo_webm1 pt-h_lo_bottom_button_ios0 pt-h_lo_bottom_button_ios1 pt-h_lo_bottom_button_webm0 pt-h_lo_bottom_button_webm1 pt-h_nav_page_header_ios0 pt-h_nav_page_header_ios1 pt-h_nav_page_header_webm0 pt-h_nav_page_header_webm1 pt-h_nav_page_toolbar_ios0 pt-h_nav_page_toolbar_ios1 pt-h_nav_page_toolbar_webm0 pt-h_nav_page_toolbar_webm1 pt-h_nav_tabs_ios0 pt-h_nav_tabs_ios1 pt-h_nav_tabs_webm0 pt-h_nav_tabs_webm1 right-lo_ios0 right-lo_ios1 right-lo_textdesc_ios0 right-lo_textdesc_ios1 right-lo_textdesc_webm0 right-lo_textdesc_webm1 right-lo_webm0 right-lo_webm1 text-[12px] text-[15px] text-[16px] text-[18px] text-[19px] text-[20px] text-[21px] text-[23px] text-[24px] text-[26px] text-[27px] text-[28px] text-[30px] text-[36px] text-[40px] text-layer-0-glyph text-layer-0-glyph-hl text-layer-0-glyph-hl_a text-layer-0-glyph-label text-layer-0-glyph-shade text-layer-0-glyph_a text-layer-0-glyph_pl text-layer-0-surface text-layer-0-surface-blur text-layer-0-surface-edge text-layer-0-surface_a text-layer-0-surface_w text-layer-1-glyph text-layer-1-glyph-hl text-layer-1-glyph-hl_a text-layer-1-glyph-label text-layer-1-glyph-shade text-layer-1-glyph_a text-layer-1-glyph_d text-layer-1-glyph_pl text-layer-1-surface text-layer-1-surface-edge text-layer-1-surface-err text-layer-1-surface-focus text-layer-1-surface_a text-layer-2-glyph text-layer-2-glyph-hl text-layer-2-glyph-hl_a text-layer-2-glyph-shade text-layer-2-glyph_a text-layer-2-glyph_d text-layer-2-glyph_pl text-layer-2-surface text-layer-2-surface-edge text-layer-2-surface_a text-radroots-accent-focus top-lo_bottom_button_ios0 top-lo_bottom_button_ios1 top-lo_bottom_button_webm0 top-lo_bottom_button_webm1 top-nav_page_header_ios0 top-nav_page_header_ios1 top-nav_page_header_webm0 top-nav_page_header_webm1 top-nav_page_toolbar_ios0 top-nav_page_toolbar_ios1 top-nav_page_toolbar_webm0 top-nav_page_toolbar_webm1 top-nav_tabs_ios0 top-nav_tabs_ios1 top-nav_tabs_webm0 top-nav_tabs_webm1 translate-x-w_lo_ios0 translate-x-w_lo_ios1 translate-x-w_lo_textdesc_ios0 translate-x-w_lo_textdesc_ios1 translate-x-w_lo_textdesc_webm0 translate-x-w_lo_textdesc_webm1 translate-x-w_lo_webm0 translate-x-w_lo_webm1 translate-y-h_lo_bottom_button_ios0 translate-y-h_lo_bottom_button_ios1 translate-y-h_lo_bottom_button_webm0 translate-y-h_lo_bottom_button_webm1 translate-y-h_nav_page_header_ios0 translate-y-h_nav_page_header_ios1 translate-y-h_nav_page_header_webm0 translate-y-h_nav_page_header_webm1 translate-y-h_nav_page_toolbar_ios0 translate-y-h_nav_page_toolbar_ios1 translate-y-h_nav_page_toolbar_webm0 translate-y-h_nav_page_toolbar_webm1 translate-y-h_nav_tabs_ios0 translate-y-h_nav_tabs_ios1 translate-y-h_nav_tabs_webm0 translate-y-h_nav_tabs_webm1 w-[12px] w-[16px] w-[17px] w-[18px] w-[20px] w-[22px] w-[24px] w-[28px] w-[36px] w-lo_ios0 w-lo_ios1 w-lo_textdesc_ios0 w-lo_textdesc_ios1 w-lo_textdesc_webm0 w-lo_textdesc_webm1 w-lo_webm0 w-lo_webm1"></div>
+\ No newline at end of file
diff --git a/apps-lib/src/lib/component/lib/empty.svelte b/apps-lib/src/lib/components/lib/empty.svelte
diff --git a/apps-lib/src/lib/components/lib/fade.svelte b/apps-lib/src/lib/components/lib/fade.svelte
@@ -0,0 +1,21 @@
+<script lang="ts">
+ import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util";
+ import type { Snippet } from "svelte";
+ import { fade, type FadeParams } from "svelte/transition";
+
+ let {
+ basis = undefined,
+ children,
+ }: {
+ basis?: IBasisOpt<IClOpt> & { in?: FadeParams; out?: FadeParams };
+ children: Snippet;
+ } = $props();
+</script>
+
+<div
+ in:fade={basis?.in || { duration: 200 }}
+ out:fade={basis?.out || { delay: 50, duration: 200 }}
+ class={`${fmt_cl(basis?.classes)} flex`}
+>
+ {@render children()}
+</div>
diff --git a/apps-lib/src/lib/components/lib/image-blob.svelte b/apps-lib/src/lib/components/lib/image-blob.svelte
@@ -0,0 +1,36 @@
+<script lang="ts">
+ import { type IImageBlob } from "@radroots/util";
+ import { onDestroy, onMount } from "svelte";
+
+ let { basis }: { basis: IImageBlob } = $props();
+
+ let img_url = $state(``);
+
+ onMount(async () => {
+ try {
+ if (!basis.data) return;
+ img_url = URL.createObjectURL(
+ new Blob([basis.data], {
+ type: "image/jpeg",
+ }),
+ );
+ } catch (e) {
+ console.log(`(error) image blob `, e);
+ }
+ });
+
+ onDestroy(async () => {
+ try {
+ URL.revokeObjectURL(img_url);
+ } catch (e) {}
+ });
+</script>
+
+{#if img_url}
+ <img
+ id={basis.id || null}
+ src={img_url}
+ alt={basis.alt || null}
+ style={`height: 100%; width: 100%; object-fit: cover; display: block;`}
+ />
+{/if}
diff --git a/apps-lib/src/lib/components/lib/image-path.svelte b/apps-lib/src/lib/components/lib/image-path.svelte
@@ -0,0 +1,32 @@
+<script lang="ts">
+ import { fmt_cl, type IImagePath } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let { basis }: { basis: IImagePath } = $props();
+
+ let img_src = $state(``);
+
+ onMount(async () => {
+ try {
+ if (basis.path) img_src = basis.path;
+ } catch (e) {
+ } finally {
+ }
+ });
+</script>
+
+{#if img_src}
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
+ <img
+ id={basis?.id || null}
+ class={`${fmt_cl(basis?.classes)}`}
+ src={img_src}
+ alt={basis?.alt || null}
+ style={`height: 100%; width: 100%; object-fit: cover; display: block;`}
+ onclick={async (ev) => {
+ ev.stopPropagation();
+ if (basis?.callback) await basis.callback(ev);
+ }}
+ />
+{/if}
diff --git a/apps-lib/src/lib/components/lib/input-value.svelte b/apps-lib/src/lib/components/lib/input-value.svelte
@@ -0,0 +1,81 @@
+<script lang="ts">
+ import {
+ type IInputValue,
+ fmt_cl,
+ parse_layer,
+ value_constrain,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ el = $bindable(null),
+ value = $bindable(``),
+ }: {
+ basis: IInputValue<string>;
+ el?: HTMLInputElement | null;
+ value?: string;
+ } = $props();
+
+ const layer = $derived(
+ typeof basis?.layer === `boolean`
+ ? parse_layer(0)
+ : parse_layer(basis?.layer),
+ );
+
+ const classes_layer = $derived(
+ typeof basis?.layer === `boolean` || typeof basis?.layer === `undefined`
+ ? ``
+ : `bg-layer-${layer}-surface text-layer-${layer}-glyph placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`,
+ );
+
+ const handle_on_input = async (el: HTMLInputElement): Promise<void> => {
+ try {
+ let pass = true;
+ let val = el?.value;
+ if (basis?.field && el) {
+ val = value_constrain(basis?.field.charset, val);
+ el.value = val;
+ if (
+ !basis?.field.validate.test(val) &&
+ basis?.field_constrain
+ ) {
+ //@todo set styles
+ }
+ pass = basis?.field.validate.test(val);
+ }
+ if (basis?.callback) await basis?.callback({ value: val, pass });
+ } catch (e) {
+ console.log(`(error) handle_on_input `, e);
+ }
+ };
+
+ $effect(() => {
+ console.log(`value `, value);
+ });
+</script>
+
+<input
+ bind:this={el}
+ bind:value
+ disabled={!!basis.disabled}
+ oninput={async ({ currentTarget: el }) => {
+ await handle_on_input(el);
+ }}
+ onblur={async ({ currentTarget: el }) => {
+ if (basis.callback_blur) await basis.callback_blur({ el });
+ }}
+ onfocus={async ({ currentTarget: el }) => {
+ if (basis.callback_focus) await basis.callback_focus({ el });
+ }}
+ onkeydown={async (ev) => {
+ if (basis?.callback_keydown)
+ await basis?.callback_keydown({
+ key: ev.key,
+ key_s: ev.key === `Enter`,
+ el: ev.currentTarget,
+ });
+ }}
+ type="text"
+ class={`${fmt_cl(basis?.classes)} el-input ${classes_layer} el-re`}
+ placeholder={basis?.placeholder || ``}
+/>
diff --git a/apps-lib/src/lib/components/lib/input.svelte b/apps-lib/src/lib/components/lib/input.svelte
@@ -0,0 +1,115 @@
+<script lang="ts">
+ import { browser } from "$app/environment";
+ import { idb } from "$root";
+ import {
+ type IInput,
+ fmt_cl,
+ parse_layer,
+ value_constrain,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let {
+ basis,
+ el = $bindable(null),
+ value = $bindable(``),
+ }: {
+ basis: IInput<string>;
+ el?: HTMLInputElement | null;
+ value?: string;
+ } = $props();
+
+ onMount(async () => {
+ try {
+ await kv_init();
+ } catch (e) {
+ } finally {
+ }
+ });
+
+ const id = $derived(basis?.id ? basis.id : null);
+
+ const layer = $derived(
+ typeof basis?.layer === `boolean`
+ ? parse_layer(0)
+ : parse_layer(basis?.layer),
+ );
+
+ const classes_layer = $derived(
+ typeof basis?.layer === `boolean` || typeof basis?.layer === `undefined`
+ ? ``
+ : `bg-layer-${layer}-surface text-layer-${layer}-glyph_d placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`,
+ );
+
+ $effect(() => {
+ if (id && basis?.sync) {
+ (async () => {
+ if (browser) await idb.set(id, value || ``);
+ })();
+ }
+ });
+
+ const kv_init = async (): Promise<void> => {
+ try {
+ if (!id) return;
+ if (basis?.sync && browser) {
+ const kv_val = await idb.get(id);
+ if (kv_val && el) el.value = kv_val;
+ else await idb.set(id, ``);
+ }
+ if (basis?.callback_mount && el)
+ await basis?.callback_mount({ el });
+ } catch (e) {
+ console.log(`(error) kv_init `, e);
+ }
+ };
+
+ const handle_on_input = async (el: HTMLInputElement): Promise<void> => {
+ try {
+ let pass = true;
+ let val = el?.value;
+ if (basis?.field && el) {
+ val = value_constrain(basis?.field.charset, val);
+ el.value = val;
+ if (
+ !basis?.field.validate.test(val) &&
+ basis?.field_constrain
+ ) {
+ //@todo set styles
+ }
+ pass = basis?.field.validate.test(val);
+ }
+ if (basis?.sync && id && browser) await idb.set(id, val);
+ if (basis?.callback) await basis?.callback({ value: val, pass });
+ } catch (e) {
+ console.log(`(error) handle_on_input `, e);
+ }
+ };
+</script>
+
+<input
+ bind:this={el}
+ bind:value
+ disabled={!!basis.disabled}
+ oninput={async ({ currentTarget: el }) => {
+ await handle_on_input(el);
+ }}
+ onblur={async ({ currentTarget: el }) => {
+ if (basis.callback_blur) await basis.callback_blur({ el });
+ }}
+ onfocus={async ({ currentTarget: el }) => {
+ if (basis.callback_focus) await basis.callback_focus({ el });
+ }}
+ onkeydown={async (ev) => {
+ if (basis?.callback_keydown)
+ await basis?.callback_keydown({
+ key: ev.key,
+ key_s: ev.key === `Enter`,
+ el: ev.currentTarget,
+ });
+ }}
+ {id}
+ type="text"
+ class={`${fmt_cl(basis?.classes)} el-input ${classes_layer} el-re`}
+ placeholder={basis?.placeholder || ``}
+/>
diff --git a/apps-lib/src/lib/components/lib/label-swap.svelte b/apps-lib/src/lib/components/lib/label-swap.svelte
@@ -0,0 +1,41 @@
+<script lang="ts">
+ import {
+ fmt_cl,
+ parse_layer,
+ type ILabelSwap,
+ type ILyOpt,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ el = $bindable(null),
+ }: {
+ basis: ILabelSwap & ILyOpt;
+ el?: HTMLLabelElement | null;
+ } = $props();
+
+ const layer = $derived(parse_layer(basis?.layer ? basis.layer : 1));
+</script>
+
+<div class={`flex flex-row justify-start items-center`}>
+ <!-- svelte-ignore a11y_label_has_associated_control -->
+ <label
+ bind:this={el}
+ class={`swap${basis.swap.toggle ? ` swap-active` : ``}`}
+ >
+ <div class="swap-on">
+ <p
+ class={`${fmt_cl(basis.swap.on.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-60`)} font-sans -translate-y-[1px] el-re`}
+ >
+ {basis.swap.on.value}
+ </p>
+ </div>
+ <div class="swap-off">
+ <p
+ class={`${fmt_cl(basis.swap.off.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-60`)} font-sans -translate-y-[1px] el-re`}
+ >
+ {basis.swap.off.value}
+ </p>
+ </div>
+ </label>
+</div>
diff --git a/apps-lib/src/lib/components/lib/load-symbol-white.svelte b/apps-lib/src/lib/components/lib/load-symbol-white.svelte
@@ -0,0 +1,70 @@
+<script lang="ts">
+ import { loading_style_map, type ILoadSymbol } from "@radroots/util";
+
+ let {
+ basis = undefined,
+ }: {
+ basis?: ILoadSymbol;
+ } = $props();
+
+ const styles = $derived(
+ basis?.dim
+ ? loading_style_map.get(basis?.dim)
+ : loading_style_map.get("sm"),
+ );
+
+ const num_blades = $derived(basis?.blades || 8);
+</script>
+
+<div
+ class={`relative flex flex-row justify-center items-center h-[${styles?.dim_1}px] w-[${styles?.dim_1}px] fade-in el-re`}
+>
+ <div
+ class={`${num_blades === 12 ? `spinner12-white center` : `spinner8-white center`} text-[${styles?.gl_2 || styles?.dim_1}px]`}
+ >
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ {#if num_blades === 12}
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`}
+ ></div>
+ {/if}
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/lib/load-symbol.svelte b/apps-lib/src/lib/components/lib/load-symbol.svelte
@@ -0,0 +1,70 @@
+<script lang="ts">
+ import { type ILoadSymbol, loading_style_map } from "@radroots/util";
+
+ let {
+ basis = undefined,
+ }: {
+ basis?: ILoadSymbol;
+ } = $props();
+
+ const styles = $derived(
+ basis?.dim
+ ? loading_style_map.get(basis?.dim)
+ : loading_style_map.get("sm"),
+ );
+
+ const num_blades = $derived(basis?.blades || 8);
+</script>
+
+<div
+ class={`relative flex flex-row justify-center items-center h-[${styles?.dim_1}px] w-[${styles?.dim_1}px] fade-in el-re`}
+>
+ <div
+ class={`${num_blades === 12 ? `spinner12 center` : `spinner8 center`} text-[${styles?.gl_2 || styles?.dim_1}px]`}
+ >
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ {#if num_blades === 12}
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ <div
+ class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
+ ></div>
+ {/if}
+ </div>
+</div>
diff --git a/apps-lib/src/lib/component/lib/logo-circle-sm.svelte b/apps-lib/src/lib/components/lib/logo-circle-sm.svelte
diff --git a/apps-lib/src/lib/components/lib/logo-letters.svelte b/apps-lib/src/lib/components/lib/logo-letters.svelte
@@ -0,0 +1,5 @@
+<p
+ class={`font-sansd italic font-[700] text-[1.7rem] text-layer-0-glyph lowercase`}
+>
+ {`radroots`}
+</p>
diff --git a/apps-lib/src/lib/components/lib/logo_circle.svelte b/apps-lib/src/lib/components/lib/logo_circle.svelte
@@ -0,0 +1,18 @@
+<div
+ class={`relative flex flex-col h-[196px] w-full justify-center items-center`}
+>
+ <div
+ class={`relative flex flex-row h-36 w-36 justify-center items-center bg-layer-2-surface rounded-full`}
+ >
+ <p
+ class={`font-sans font-[900] text-6xl text-layer-0-glyph -tracking-[0.4rem] -translate-x-[6px]`}
+ >
+ {"»`,"}
+ </p>
+ <p
+ class={`font-sans font-[900] text-6xl text-layer-0-glyph translate-x-[8px]`}
+ >
+ {"-"}
+ </p>
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/lib/select-menu.svelte b/apps-lib/src/lib/components/lib/select-menu.svelte
@@ -0,0 +1,79 @@
+<script lang="ts">
+ import { fmt_cl, parse_layer, type ISelect } from "@radroots/util";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis,
+ value = $bindable(``),
+ el_wrap = $bindable(null),
+ el_select = $bindable(null),
+ children,
+ }: {
+ basis: ISelect;
+ value: string;
+ el_wrap?: HTMLDivElement | null;
+ el_select?: HTMLSelectElement | null;
+ children?: Snippet;
+ } = $props();
+
+ const layer = $derived(
+ parse_layer(typeof basis?.layer === `boolean` ? basis.layer : 0),
+ );
+
+ const classes_layer = $derived(
+ typeof basis?.layer === `boolean` ? `` : `text-layer-${layer}-glyph`,
+ );
+</script>
+
+<div
+ class={`relative flex flex-row h-max w-auto justify-center items-center`}
+ bind:this={el_wrap}
+>
+ <div
+ class={`${fmt_cl(basis.classes)} z-20 absolute top-0 left-0 flex flex-row h-full w-full justify-end items-center ${classes_layer}`}
+ >
+ <select
+ class={`select select-ghost h-full w-full bg-transparent focus:border-0 focus:outline-0 text-transparent focus:text-transparent`}
+ bind:this={el_select}
+ bind:value
+ onchange={async (e) => {
+ const opt = basis.options
+ .map((i) => i.entries)
+ .reduce((_, j) => j, [])
+ .find(
+ (k: { value: string }) =>
+ 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>
+ </div>
+ {#if children}
+ <div class={`z-10 flex flex-row h-full w-full`}>
+ {@render children()}
+ </div>
+ {/if}
+</div>
diff --git a/apps-lib/src/lib/components/lib/select.svelte b/apps-lib/src/lib/components/lib/select.svelte
@@ -0,0 +1,121 @@
+<script lang="ts">
+ import { browser } from "$app/environment";
+ import { Glyph, handle_err, idb } from "$root";
+ import { fmt_cl, parse_layer, type ISelect } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let {
+ basis,
+ value = $bindable(``),
+ el = $bindable(null),
+ }: {
+ basis: ISelect;
+ value: string;
+ el?: HTMLSelectElement | null;
+ } = $props();
+
+ const id = $derived(basis?.id ? basis.id : null);
+
+ const layer = $derived(
+ typeof basis?.layer === `boolean`
+ ? parse_layer(0)
+ : parse_layer(basis.layer),
+ );
+
+ const classes_layer = $derived(
+ typeof basis?.layer === `boolean`
+ ? ``
+ : !value
+ ? `text-layer-${layer}-glyph/60`
+ : `text-layer-${layer}-glyph_d`,
+ );
+
+ onMount(async () => {
+ try {
+ if (id && basis?.sync_init && browser) {
+ const sync_val = await idb.get(id);
+ await idb.set(id, sync_val || ``);
+ }
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
+ });
+
+ $effect(() => {
+ if (browser && id && basis?.sync) {
+ (async () => {
+ await idb.set(id, value);
+ })();
+ }
+ });
+
+ 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 && id && browser) await idb.set(id, value);
+ if (basis.callback && opt) await basis.callback(opt);
+ } catch (e) {
+ console.log(`(error) handle_on_change `, e);
+ }
+ };
+</script>
+
+{#if basis?.show_arrows === "l"}
+ <div class={`flex flex-row pr-[2px] justify-center items-center`}>
+ <Glyph
+ basis={{
+ key: `caret-up-down`,
+ dim: `xs`,
+
+ classes: `text-layer-${layer}-glyph translate-y-[1px]`,
+ }}
+ />
+ </div>
+{/if}
+<select
+ bind:this={el}
+ bind:value
+ onchange={async ({ currentTarget: el }) => {
+ handle_on_change(el);
+ }}
+ {id}
+ class={`${fmt_cl(basis.classes)} z-10 el-select ${classes_layer}`}
+>
+ {#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>
+{#if basis?.show_arrows === "r"}
+ <div class={`flex flex-row pl-[2px] justify-center items-center`}>
+ <Glyph
+ basis={{
+ key: `caret-up-down`,
+ dim: `xs`,
+
+ classes: `text-layer-${layer}-glyph`,
+ }}
+ />
+ </div>
+{/if}
diff --git a/apps-lib/src/lib/components/lib/text-area.svelte b/apps-lib/src/lib/components/lib/text-area.svelte
@@ -0,0 +1,96 @@
+<script lang="ts">
+ import { browser } from "$app/environment";
+ import { handle_err, idb } from "$root";
+ import {
+ type ITextArea,
+ fmt_cl,
+ fmt_textarea_value,
+ parse_layer,
+ sleep,
+ value_constrain_textarea,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let {
+ basis,
+ value = $bindable(``),
+ el = $bindable(null),
+ }: {
+ basis: ITextArea;
+ value?: string;
+ el?: HTMLTextAreaElement | null;
+ } = $props();
+
+ const id = $derived(basis?.id ? basis.id : null);
+ const layer = $derived(
+ typeof basis.layer === `boolean` ? 0 : parse_layer(basis.layer, 1),
+ );
+
+ onMount(async () => {
+ try {
+ if (id && basis?.sync && browser) {
+ const sync_val = await idb.get(id);
+ await idb.set(id, sync_val || ``);
+ }
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
+ });
+
+ $effect(() => {
+ if (browser && id && basis?.sync && value) {
+ (async () => {
+ await sleep(100);
+ await idb.set(id, value);
+ })();
+ }
+ });
+
+ const handle_on_input = async (el: HTMLTextAreaElement): Promise<void> => {
+ try {
+ let pass = true;
+ let val = el?.value;
+ if (basis?.field && el) {
+ val = value_constrain_textarea(basis?.field.charset, val);
+ el.value = fmt_textarea_value(val);
+ if (
+ !basis?.field.validate.test(val) &&
+ basis?.field_constrain
+ ) {
+ //@todo set styles
+ }
+ pass = basis?.field.validate.test(val);
+ }
+ if (id && basis?.sync && browser) await idb.set(id, val);
+ if (basis?.callback) await basis?.callback({ value: val, pass });
+ } catch (e) {
+ console.log(`(error) handle_on_input `, e);
+ }
+ };
+</script>
+
+<textarea
+ bind:this={el}
+ bind:value
+ oninput={async ({ currentTarget: el }) => {
+ await handle_on_input(el);
+ }}
+ onblur={async ({ currentTarget: el }) => {
+ if (basis.callback_blur) await basis.callback_blur({ el });
+ }}
+ onfocus={async ({ currentTarget: el }) => {
+ if (basis.callback_focus) await basis.callback_focus({ el });
+ }}
+ onkeydown={async (ev) => {
+ if (basis.callback_keydown)
+ await basis.callback_keydown({
+ key: ev.key,
+ key_s: ev.key === `Enter`,
+ el: ev.currentTarget,
+ });
+ }}
+ {id}
+ contenteditable="true"
+ class={`${fmt_cl(basis.classes)} el-textarea p-2 w-full bg-layer-${layer}-surface text-layer-${layer}-glyph_d placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`}
+ placeholder={basis.placeholder || ``}
+></textarea>
diff --git a/apps-lib/src/lib/components/lib/theme.svelte b/apps-lib/src/lib/components/lib/theme.svelte
@@ -0,0 +1,17 @@
+<script lang="ts">
+ import { browser } from "$app/environment";
+ import { app_th, app_thc, theme_set } from "$root";
+ import { parse_color_mode, parse_theme_key } from "@radroots/theme";
+
+ app_thc.subscribe(
+ (_app_thc) =>
+ browser &&
+ theme_set(parse_theme_key($app_thc), parse_color_mode(_app_thc)),
+ );
+
+ app_th.subscribe(
+ (_app_th) =>
+ browser &&
+ theme_set(parse_theme_key(_app_th), parse_color_mode($app_thc)),
+ );
+</script>
diff --git a/apps-lib/src/lib/components/lib/toast.svelte b/apps-lib/src/lib/components/lib/toast.svelte
@@ -0,0 +1,54 @@
+<script lang="ts">
+ import { app_lo } from "$root";
+ import {
+ fmt_cl,
+ get_layout,
+ parse_layer,
+ toast_layout_map,
+ toast_style_map,
+ type IToast,
+ type IToastKind,
+ } from "@radroots/util";
+
+ let { basis }: { basis: IToast } = $props();
+
+ const styles: IToastKind[] = $derived(
+ basis?.styles ? basis.styles : [`simple`],
+ );
+ const layout = $derived(get_layout($app_lo));
+ const layer = $derived(basis.layer ? parse_layer(basis.layer) : 1);
+</script>
+
+<div
+ class={`${fmt_cl(toast_layout_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(toast_style_map.get(style)?.outer)).join(` `))}`}
+ >
+ <div
+ class={`absolute top-0 left-4 flex flex-row h-full items-center text-layer-${layer}-glyph`}
+ >
+ <glyph
+ basis={{
+ key: `info`,
+ weight: `regular`,
+ dim: `md`,
+ ...basis.glyph,
+ }}
+ ></glyph>
+ </div>
+ <div
+ class={`col-span-12 flex flex-row pl-1 ${fmt_cl(styles.map((style) => fmt_cl(toast_style_map.get(style)?.inner)).join(` `))}`}
+ >
+ {#if `value` in basis.label}
+ <p
+ class={`font-sans font-[500] truncate text-layer-${layer}-glyph -translate-y-[1px]`}
+ >
+ {basis.label.value}
+ </p>
+ {/if}
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/lib/wrap-border.svelte b/apps-lib/src/lib/components/lib/wrap-border.svelte
@@ -0,0 +1,22 @@
+<script lang="ts">
+ import { fmt_cl, type IClOpt } from "@radroots/util";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis,
+ children,
+ }: {
+ basis: IClOpt;
+ children: Snippet;
+ } = $props();
+</script>
+
+<div
+ class={`${fmt_cl(basis.classes)} relative flex flex-col w-full py-[4px] px-[4px] justify-start items-center rounded-[32px] overflow-hidden bg-white shadow-sm`}
+>
+ <div
+ class={`flex flex-row h-full w-full justify-center items-center bg-white/30 overflow-hidden rounded-[28px]`}
+ >
+ {@render children()}
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/map/map-marker-area-display.svelte b/apps-lib/src/lib/components/map/map-marker-area-display.svelte
@@ -0,0 +1,58 @@
+<script lang="ts">
+ import { Fade, Glyph } from "$root";
+ import type { IBasisOpt } from "@radroots/util";
+
+ let {
+ basis = undefined,
+ }: {
+ basis?: IBasisOpt<{
+ primary: string;
+ admin: string;
+ country: string;
+ }>;
+ } = $props();
+</script>
+
+{#if basis}
+ <Fade
+ basis={{
+ classes: `flex-col w-full justify-center items-start`,
+ }}
+ >
+ <div
+ class={`flex flex-col w-fit px-5 py-[10px] justify-start items-start bg-layer-1-surface rounded-3xl shadow-lg`}
+ >
+ <div class={`flex flex-col w-full gap-1 justify-start items-start`}>
+ <div class={`flex flex-row gap-1 justify-start items-center`}>
+ <p
+ class={`font-sans font-[600] text-[0.95rem] text-layer-2-glyph`}
+ >
+ {basis.primary}
+ </p>
+ <Glyph
+ basis={{
+ classes: `text-layer-2-glyph -translate-y-[2px]`,
+ dim: `xs`,
+
+ key: `map-pin-simple`,
+ }}
+ />
+ </div>
+ <div
+ class={`flex flex-row w-full gap-1 justify-start items-center`}
+ >
+ <p
+ class={`font-sans font-[600] text-[0.95rem] tracking-tight text-layer-2-glyph`}
+ >
+ {`${basis.admin},`}
+ </p>
+ <p
+ class={`font-sans font-[600] text-[0.95rem] tracking-tight text-layer-2-glyph`}
+ >
+ {`${basis.country}`}
+ </p>
+ </div>
+ </div>
+ </div>
+ </Fade>
+{/if}
diff --git a/apps-lib/src/lib/components/map/map-marker-area.svelte b/apps-lib/src/lib/components/map/map-marker-area.svelte
@@ -0,0 +1,44 @@
+<script lang="ts">
+ import { MapMarkerAreaDisplay } from "$root";
+ import { Marker, Popup } from "@radroots/svelte-maplibre";
+ import {
+ type GeocoderReverseResult,
+ type GeolocationPoint,
+ type IMapMarkerArea,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ map_geop = $bindable(),
+ map_geoc = $bindable(undefined),
+ }: {
+ basis: IMapMarkerArea;
+ map_geop: GeolocationPoint;
+ map_geoc?: GeocoderReverseResult | undefined;
+ } = $props();
+</script>
+
+<Marker
+ bind:lngLat={map_geop}
+ draggable={!basis.no_drag}
+ class={`flex flex-row h-[100px] w-[100px] bg-blue-400/20 border-[2px] border-white justify-center items-center rounded-full shadow-lg`}
+ ondragend={async () => {
+ if (!map_geop) return;
+ const geoc = await basis.lc_geocode(map_geop);
+ if (geoc) map_geoc = geoc;
+ }}
+>
+ {#if basis.show_display}
+ <Popup open={basis.show_display} offset={[0, -55]}>
+ <MapMarkerAreaDisplay
+ basis={map_geoc
+ ? {
+ primary: map_geoc.name,
+ admin: map_geoc.admin1_name,
+ country: map_geoc.country_name,
+ }
+ : undefined}
+ />
+ </Popup>
+ {/if}
+</Marker>
diff --git a/apps-lib/src/lib/components/map/map.svelte b/apps-lib/src/lib/components/map/map.svelte
@@ -0,0 +1,42 @@
+<script lang="ts">
+ import { app_thc } from "$lib/store/app";
+ import { MapLibre } from "@radroots/svelte-maplibre";
+ import { cfg_map, fmt_cl, type IClOpt } from "@radroots/util";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis = undefined,
+ map = $bindable(undefined),
+ children,
+ }: {
+ basis?: IClOpt & {
+ interactive?: boolean;
+ zoom_click_off?: boolean;
+ };
+ map?: maplibregl.Map;
+ interactive?: boolean;
+ children: Snippet;
+ } = $props();
+
+ const interactive = $derived(
+ typeof basis?.interactive === `boolean` ? basis?.interactive : true,
+ );
+
+ const zoomOnDoubleClick = $derived(
+ typeof basis?.zoom_click_off === `boolean`
+ ? basis?.zoom_click_off
+ : true,
+ );
+</script>
+
+<MapLibre
+ bind:map
+ class="{fmt_cl(basis?.classes)} relative h-full w-full"
+ zoom={10}
+ style={cfg_map.styles.base[$app_thc]}
+ attributionControl={false}
+ {interactive}
+ {zoomOnDoubleClick}
+>
+ {@render children()}
+</MapLibre>
diff --git a/apps-lib/src/lib/components/marker/marker-indexed-view.svelte b/apps-lib/src/lib/components/marker/marker-indexed-view.svelte
@@ -0,0 +1,31 @@
+<script lang="ts">
+ import { Empty } from "$root";
+ import { type CallbackPromiseGeneric } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: {
+ index_curr: number;
+ index_max: number;
+ callback_index: CallbackPromiseGeneric<number>;
+ };
+ } = $props();
+</script>
+
+<div class={`flex flex-row w-full justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[5px] w-full gap-[6px] justify-start items-center`}
+ >
+ {#each Array(basis.index_max).fill(0) as _, index}
+ <button
+ class={`flex flex-row flex-grow h-full justify-start items-center ${basis.index_curr >= index ? `bg-th-black` : `bg-layer-2-surface`} rounded-full el-re`}
+ onclick={async () => {
+ await basis.callback_index(index);
+ }}
+ >
+ <Empty />
+ </button>
+ {/each}
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/navigation/navigation-tabs.svelte b/apps-lib/src/lib/components/navigation/navigation-tabs.svelte
@@ -0,0 +1,99 @@
+<script lang="ts">
+ import { goto } from "$app/navigation";
+ import { page } from "$app/state";
+ import { app_lo, Empty, Glyph } from "$root";
+</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-layer-1-surface 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-layer-0-glyph/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-layer-0-glyph/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-layer-0-glyph/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-layer-0-glyph/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`}
+ >
+ <Empty />
+ </div>
+ </div>
+ </button>
+ </div>
+ <button
+ class={`flex flex-row h-[3.1rem] w-[3.1rem] justify-center items-center bg-layer-1-surface rounded-full backdrop-blur-lg`}
+ onclick={async () => {}}
+ >
+ <Glyph
+ basis={{
+ classes: `text-[22px] text-layer-0-glyph/80`,
+
+ key: `plus`,
+ }}
+ />
+ </button>
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/navigation/page-header.svelte b/apps-lib/src/lib/components/navigation/page-header.svelte
@@ -0,0 +1,54 @@
+<script lang="ts">
+ import { app_lo, callback_route, Empty, ph_blur } from "$root";
+ import type { IPageHeader } from "@radroots/util";
+ 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-layer-0-surface-blur/30 backdrop-blur-lg`}
+ >
+ <Empty />
+ </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-layer-0-glyph 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/src/lib/components/navigation/page-toolbar.svelte b/apps-lib/src/lib/components/navigation/page-toolbar.svelte
@@ -0,0 +1,52 @@
+<script lang="ts">
+ import { goto } from "$app/navigation";
+ import { app_lo, Glyph, LogoCircleSm, PageHeader } from "$root";
+ import type { IBasisOpt, IPageToolbar } from "@radroots/util";
+ import type { Snippet } from "svelte";
+ import LogoLetters from "../lib/logo-letters.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-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-layer-0-glyph`,
+ dim: `lg`,
+
+ key: `gear`,
+ }}
+ />
+ </button>
+</div>
+{#if basis?.header}
+ <PageHeader basis={basis.header}>
+ {#if header_option}
+ {@render header_option()}
+ {/if}
+ </PageHeader>
+{/if}
diff --git a/apps-lib/src/lib/components/trellis/trellis-default-label.svelte b/apps-lib/src/lib/components/trellis/trellis-default-label.svelte
@@ -0,0 +1,36 @@
+<script lang="ts">
+ import type { ThemeLayer } from "@radroots/theme";
+ import { fmt_cl, type ITrellisDefaultLabel } from "@radroots/util";
+
+ let {
+ layer,
+ labels,
+ classes = ``,
+ }: {
+ layer: ThemeLayer;
+ labels: ITrellisDefaultLabel[];
+ classes?: string;
+ } = $props();
+</script>
+
+<div class={`${fmt_cl(classes)} flex flex-row`}>
+ <p class={`font-sans text-trellis_ti text-layer-${layer}-glyph-shade`}>
+ {#each labels as label}
+ <span class={`${fmt_cl(label.classes)} font-sans text-trellis_ti`}>
+ {#if `callback` in label}
+ <button
+ class={``}
+ onclick={async () => {
+ if (`callback` in label && label.callback)
+ await label.callback();
+ }}
+ >
+ {label.label}
+ </button>
+ {:else}
+ {label.label}
+ {/if}
+ </span>
+ {/each}
+ </p>
+</div>
diff --git a/apps-lib/src/lib/components/trellis/trellis-end.svelte b/apps-lib/src/lib/components/trellis/trellis-end.svelte
@@ -0,0 +1,36 @@
+<script lang="ts">
+ import { Glyph } from "$root";
+ import type { ThemeLayer } from "@radroots/theme";
+ import { type ITrellisBasisTouchEnd } from "@radroots/util";
+
+ let {
+ basis,
+ layer,
+ hide_active,
+ }: {
+ basis: ITrellisBasisTouchEnd;
+ layer: ThemeLayer;
+ hide_active: boolean;
+ } = $props();
+</script>
+
+<div
+ class={`absolute top-0 right-0 h-full w-max flex flex-row justify-center items-center`}
+>
+ <button
+ class={`flex pr-3`}
+ onclick={async (ev) => {
+ if (basis.callback) await basis.callback(ev);
+ }}
+ >
+ {#if basis.glyph}
+ <Glyph
+ basis={{
+ classes: `text-layer-${layer}-glyph-shade ${hide_active ? `` : `group-active:text-layer-${layer}-glyph_a`} translate-y-[1px] opacity-70`,
+ dim: `xs+`,
+ ...basis.glyph,
+ }}
+ />
+ {/if}
+ </button>
+</div>
diff --git a/apps-lib/src/lib/components/trellis/trellis-input.svelte b/apps-lib/src/lib/components/trellis/trellis-input.svelte
@@ -0,0 +1,87 @@
+<script lang="ts">
+ import { Glyph, Input, LoadSymbol } from "$root";
+ import type { ThemeLayer } from "@radroots/theme";
+ import {
+ fmt_cl,
+ fmt_trellis,
+ type ITrellisBasisInput,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ layer,
+ hide_border_b,
+ hide_border_t,
+ }: {
+ basis: ITrellisBasisInput;
+ layer: ThemeLayer;
+ hide_border_b: boolean;
+ hide_border_t: boolean;
+ } = $props();
+</script>
+
+<div class={`flex flex-row flex-grow h-full w-full`}>
+ <div
+ class={`${fmt_trellis(hide_border_b, hide_border_t)} flex flex-row h-line w-full justify-start items-center border-t-line border-layer-${layer}-surface-edge overflow-hidden`}
+ >
+ {#if basis.line_label && basis.line_label.value}
+ <div
+ class={`${fmt_cl(basis.line_label.classes)} flex flex-row h-full justify-start items-center overflow-x-hidden`}
+ >
+ <p class={`font-sans text-layer-${layer}-glyph_b`}>
+ {basis.line_label.value}
+ </p>
+ </div>
+ {/if}
+ <div
+ class={`relative flex flex-row flex-grow h-full pr-12 justify-start items-center`}
+ >
+ <Input
+ basis={{
+ ...basis.basis,
+ layer: layer,
+ }}
+ />
+ {#if basis.action}
+ {#if basis.action.visible}
+ <div
+ class={`absolute top-0 right-0 flex flex-row h-full w-12 pr-4 justify-end items-center fade-in`}
+ >
+ {#if basis.action.loading}
+ <div class={`flex flex-row fade-in`}>
+ <LoadSymbol
+ basis={{
+ dim: `glyph-send-button`,
+ blades: 8,
+ classes: `text-layer-${layer}-glyph el-re`,
+ }}
+ />
+ </div>
+ {:else}
+ <button
+ class={`group fade-in-long`}
+ onclick={async () => {
+ if (basis.action?.callback)
+ await basis.action.callback();
+ }}
+ >
+ <Glyph
+ basis={basis.action.glyph
+ ? {
+ dim: `md-`,
+ ...basis.action.glyph,
+ }
+ : {
+ key: `plus`,
+ classes: `text-layer-${layer}-glyph`,
+ dim: `md-`,
+ }}
+ />
+ </button>
+ {/if}
+ </div>
+ {/if}
+ {/if}
+ </div>
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/trellis/trellis-line.svelte b/apps-lib/src/lib/components/trellis/trellis-line.svelte
@@ -0,0 +1,56 @@
+<script lang="ts">
+ import { LoadSymbol } from "$root";
+ import type { ThemeLayer } from "@radroots/theme";
+ import { fmt_trellis, type CallbackPromiseGeneric } from "@radroots/util";
+ import type { Snippet } from "svelte";
+
+ let {
+ loading = false,
+ layer,
+ callback,
+ hide_border_b,
+ hide_border_t,
+ children,
+ el_end,
+ }: {
+ loading?: boolean;
+ layer: ThemeLayer;
+ callback?: CallbackPromiseGeneric<MouseEvent>;
+ hide_border_b: boolean;
+ hide_border_t: boolean;
+ children: Snippet;
+ el_end?: Snippet;
+ } = $props();
+</script>
+
+<button
+ class={`flex flex-row flex-grow overflow-hidden`}
+ onclick={async (ev) => {
+ if (callback) await callback(ev);
+ }}
+>
+ <div
+ class={`${fmt_trellis(hide_border_b, hide_border_t)} flex flex-row h-full w-full justify-center items-center border-t-line border-layer-${layer}-surface-edge el-re`}
+ >
+ {#if loading}
+ <div
+ class={`flex flex-row h-full w-full justify-center items-center`}
+ >
+ <LoadSymbol basis={{ dim: `sm` }} />
+ </div>
+ {:else}
+ <div
+ class={`relative group flex flex-row h-line w-full pr-[2px] justify-between items-center el-re`}
+ >
+ <div
+ class={`flex flex-row h-full w-trellis_display justify-between items-center`}
+ >
+ {@render children()}
+ </div>
+ {#if el_end}
+ {@render el_end()}
+ {/if}
+ </div>
+ {/if}
+ </div>
+</button>
diff --git a/apps-lib/src/lib/components/trellis/trellis-offset.svelte b/apps-lib/src/lib/components/trellis/trellis-offset.svelte
@@ -0,0 +1,71 @@
+<script lang="ts">
+ import { Empty, Glyph, GlyphCircle, LoadSymbol } from "$root";
+ import type { ThemeLayer } from "@radroots/theme";
+ import {
+ fmt_cl,
+ type ITrellisBasisOffset,
+ type ITrellisBasisOffsetMod,
+ } from "@radroots/util";
+
+ let {
+ basis = undefined,
+ layer,
+ }: {
+ basis?: ITrellisBasisOffset;
+ layer: ThemeLayer;
+ } = $props();
+
+ const mod: ITrellisBasisOffsetMod = $derived(basis?.mod ? basis.mod : `sm`);
+</script>
+
+<div class={`flex flex-row h-full`}>
+ {#if mod === `sm`}
+ <div class={`${fmt_cl(``)} flex flex-row h-full w-[22px]`}>
+ <Empty />
+ </div>
+ {:else if mod === `glyph`}
+ <div class={`flex flex-row pr-[2px]`}>
+ <div class={`${fmt_cl(``)} flex flex-row h-full w-trellisOffset`}>
+ <Empty />
+ </div>
+ </div>
+ {:else if typeof mod === `object`}
+ <div
+ class={`flex flex-row h-full min-w-[20px] w-trellisOffset justify-center items-center pr-3`}
+ >
+ <button
+ class={`fade-in pl-2 translate-x-[3px] translate-y-[1px]`}
+ onclick={async (ev) => {
+ if (mod.loading) return;
+ else if (typeof basis !== `boolean` && basis?.callback)
+ await basis.callback(ev);
+ }}
+ >
+ {#if mod.loading}
+ <LoadSymbol basis={{ blades: 8, dim: `xs` }} />
+ {:else if `glyph` in mod}
+ <Glyph
+ basis={{
+ classes: mod.glyph.classes
+ ? mod.glyph.classes
+ : `text-layer-${layer}-glyph`,
+ ...mod.glyph,
+ }}
+ />
+ {:else if `glyph_circle` in mod}
+ <GlyphCircle
+ basis={{
+ classes_wrap: mod.glyph_circle?.classes_wrap,
+ glyph: {
+ classes: mod.glyph_circle?.glyph?.classes
+ ? mod.glyph_circle?.glyph?.classes
+ : `text-layer-${layer}-glyph`,
+ ...mod.glyph_circle?.glyph,
+ },
+ }}
+ ></GlyphCircle>
+ {/if}
+ </button>
+ </div>
+ {/if}
+</div>
diff --git a/apps-lib/src/lib/components/trellis/trellis-row-display-value.svelte b/apps-lib/src/lib/components/trellis/trellis-row-display-value.svelte
@@ -0,0 +1,47 @@
+<script lang="ts">
+ import { Glyph } from "$root";
+ import type { ThemeLayer } from "@radroots/theme";
+ import {
+ fmt_cl,
+ get_label_classes_kind,
+ type ITrellisKindDisplayValue,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ layer,
+ hide_active,
+ }: {
+ basis: ITrellisKindDisplayValue;
+ layer: ThemeLayer;
+ hide_active: boolean;
+ } = $props();
+</script>
+
+<button
+ class={`z-10 flex flex-grow justify-end`}
+ onclick={async (ev) => {
+ ev.stopPropagation();
+ if (basis.callback) await basis.callback(ev);
+ }}
+>
+ {#if `icon` in basis}
+ <Glyph
+ basis={{
+ classes:
+ basis.icon.classes ||
+ `${get_label_classes_kind(layer, `shade`, hide_active)}`,
+ key: basis.icon.key,
+ dim: `sm`,
+ }}
+ />
+ {:else if basis.label}
+ {#if `value` in basis.label}
+ <p
+ class={`${fmt_cl(basis.label.classes)} font-sans text-line_d_e line-clamp-1 text-layer-0-glyph-label el-re`}
+ >
+ {basis.label.value}
+ </p>
+ {/if}
+ {/if}
+</button>
diff --git a/apps-lib/src/lib/components/trellis/trellis-row-label.svelte b/apps-lib/src/lib/components/trellis/trellis-row-label.svelte
@@ -0,0 +1,67 @@
+<script lang="ts">
+ import { GlyphButton } from "$root";
+ import type { ThemeLayer } from "@radroots/theme";
+ import {
+ fmt_cl,
+ get_label_classes_kind,
+ type ILabelTupFields,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ layer,
+ hide_active,
+ }: {
+ basis: ILabelTupFields;
+ layer: ThemeLayer;
+ hide_active: boolean;
+ } = $props();
+</script>
+
+<div class={`flex flex-row h-full items-center justify-between`}>
+ {#if basis.left && basis.left.length}
+ <div class={`flex flex-row h-full items-center truncate`}>
+ {#each basis.left as title_l}
+ <div
+ class={`${fmt_cl(title_l.classes_wrap)} flex flex-row h-full items-center ${get_label_classes_kind(layer, undefined, hide_active)} ${title_l.hide_truncate ? `` : `truncate`}`}
+ >
+ {#if `glyph` in title_l}
+ <div
+ class={`flex flex-row justify-start items-center pr-2`}
+ >
+ <GlyphButton basis={{ ...title_l.glyph }}
+ ></GlyphButton>
+ </div>
+ {:else if `value` in title_l}
+ <p
+ class={`${fmt_cl(title_l.classes)} font-sans text-line_d ${title_l.hide_truncate ? `` : `truncate`} el-re`}
+ >
+ {title_l.value || ``}
+ </p>
+ {/if}
+ </div>
+ {/each}
+ </div>
+ {/if}
+ {#if basis.right && basis.right.length}
+ <div
+ class={`flex flex-row h-full w-content items-center justify-end pr-4`}
+ >
+ {#each basis.right.reverse() as title_r}
+ <div
+ class={`${fmt_cl(title_r.classes_wrap)} flex flex-row h-full gap-1 items-center ${title_r.hide_truncate ? `` : `truncate`}`}
+ >
+ {#if `glyph` in title_r}
+ <GlyphButton basis={{ ...title_r.glyph }}></GlyphButton>
+ {:else if `value` in title_r}
+ <p
+ class={`${fmt_cl(title_r.classes)} font-sans text-line_d text-layer-${layer}-glyph_d ${title_r.hide_truncate ? `` : `truncate`} el-re`}
+ >
+ {title_r.value || ``}
+ </p>
+ {/if}
+ </div>
+ {/each}
+ </div>
+ {/if}
+</div>
diff --git a/apps-lib/src/lib/components/trellis/trellis-select.svelte b/apps-lib/src/lib/components/trellis/trellis-select.svelte
@@ -0,0 +1,67 @@
+<script lang="ts">
+ import {
+ LoadSymbol,
+ SelectMenu,
+ TrellisEnd,
+ TrellisLine,
+ TrellisRowDisplayValue,
+ TrellisRowLabel,
+ } from "$root";
+ import type { ThemeLayer } from "@radroots/theme";
+ import type { ITrellisBasisSelect } from "@radroots/util";
+
+ let {
+ basis,
+ layer,
+ hide_active,
+ hide_border_b,
+ hide_border_t,
+ }: {
+ basis: ITrellisBasisSelect;
+ layer: ThemeLayer;
+ hide_active: boolean;
+ hide_border_b: boolean;
+ hide_border_t: boolean;
+ } = $props();
+
+ const loading = $derived(
+ typeof basis?.loading === `boolean` ? basis.loading : false,
+ );
+
+ const value = $derived(basis.el.value);
+</script>
+
+<TrellisLine
+ {layer}
+ {loading}
+ {hide_border_b}
+ {hide_border_t}
+ callback={basis.callback}
+>
+ <TrellisRowLabel basis={basis.label} {layer} {hide_active} />
+ {#if basis.display}
+ <div class={`flex flex-row pr-3 justify-center items-end`}>
+ <SelectMenu {value} basis={basis.el}>
+ {#if basis.display.loading}
+ <div
+ class={`flex flex-row h-full w-full justify-end items-center`}
+ >
+ <LoadSymbol basis={{ dim: `sm` }} />
+ </div>
+ {:else}
+ <TrellisRowDisplayValue
+ basis={{ ...basis.display }}
+ {layer}
+ {hide_active}
+ />
+ {/if}
+ </SelectMenu>
+ </div>
+ {/if}
+
+ {#snippet el_end()}
+ {#if basis.end}
+ <TrellisEnd basis={basis.end} {layer} {hide_active} />
+ {/if}
+ {/snippet}
+</TrellisLine>
diff --git a/apps-lib/src/lib/components/trellis/trellis-title.svelte b/apps-lib/src/lib/components/trellis/trellis-title.svelte
@@ -0,0 +1,68 @@
+<script lang="ts">
+ import { Empty, Glyph, LabelSwap } from "$root";
+ import type { ThemeLayer } from "@radroots/theme";
+ import { type ITrellisTitle, fmt_cl } from "@radroots/util";
+
+ let {
+ basis,
+ layer = 0,
+ }: {
+ basis: ITrellisTitle;
+ layer: ThemeLayer;
+ } = $props();
+
+ const mod = $derived(basis?.mod ? basis.mod : `sm`);
+</script>
+
+<div
+ class={`${fmt_cl(basis.classes)} flex flex-row h-[24px] w-full pl-[2px] gap-1 items-center`}
+>
+ <button
+ class={`flex flex-row h-full w-max items-center gap-1 ${mod === `glyph` ? `pl-[36px]` : mod === `sm` ? `pl-[16px]` : ``}`}
+ onclick={async () => {
+ if (basis && basis.callback) await basis.callback();
+ }}
+ >
+ {#if basis.value === true}
+ <Empty />
+ {:else}
+ <p
+ class={`font-sans text-trellis_ti text-layer-${layer}-glyph-label uppercase`}
+ >
+ {basis.value || ``}
+ </p>
+ {/if}
+ </button>
+ {#if basis.link}
+ <button
+ class={`${fmt_cl(basis.link.classes)} group flex flex-row h-full w-max items-center`}
+ onclick={async () => {
+ if (basis.link && basis.link.callback)
+ await basis.link.callback();
+ }}
+ >
+ {#if basis.link.label}
+ {#if `swap` in basis.link.label}
+ <LabelSwap basis={basis.link.label} />
+ {:else if `value` in basis.link.label}
+ <p
+ class={`${fmt_cl(basis.link.label.classes)} font-sans text-trellis_ti uppercase fade-in`}
+ >
+ {basis.link.label.value || ``}
+ </p>
+ {/if}
+ {/if}
+ {#if basis.link.glyph}
+ <div class={`flex flex-row w-max`}>
+ <Glyph
+ basis={{
+ ...basis.link.glyph,
+ dim: `xs-`,
+ classes: `${fmt_cl(basis.link.glyph.classes)} fade-in`,
+ }}
+ />
+ </div>
+ {/if}
+ </button>
+ {/if}
+</div>
diff --git a/apps-lib/src/lib/components/trellis/trellis-touch.svelte b/apps-lib/src/lib/components/trellis/trellis-touch.svelte
@@ -0,0 +1,41 @@
+<script lang="ts">
+ import {
+ TrellisEnd,
+ TrellisLine,
+ TrellisRowDisplayValue,
+ TrellisRowLabel,
+ } from "$root";
+ import type { ITrellisBasisTouch, ThemeLayer } from "@radroots/util";
+
+ let {
+ basis,
+ layer,
+ hide_active,
+ hide_border_b,
+ hide_border_t,
+ }: {
+ basis: ITrellisBasisTouch;
+ layer: ThemeLayer;
+ hide_active: boolean;
+ hide_border_b: boolean;
+ hide_border_t: boolean;
+ } = $props();
+</script>
+
+<TrellisLine {layer} {hide_border_b} {hide_border_t} callback={basis.callback}>
+ <TrellisRowLabel basis={basis.label} {layer} {hide_active} />
+ {#if basis.display}
+ <TrellisRowDisplayValue
+ basis={{
+ ...basis.display,
+ }}
+ {layer}
+ {hide_active}
+ />
+ {/if}
+ {#snippet el_end()}
+ {#if basis.end}
+ <TrellisEnd basis={basis.end} {layer} {hide_active} />
+ {/if}
+ {/snippet}
+</TrellisLine>
diff --git a/apps-lib/src/lib/components/trellis/trellis.svelte b/apps-lib/src/lib/components/trellis/trellis.svelte
@@ -0,0 +1,146 @@
+<script lang="ts">
+ import {
+ app_lo,
+ lls,
+ TrellisDefaultLabel,
+ TrellisInput,
+ TrellisOffset,
+ TrellisSelect,
+ TrellisTitle,
+ TrellisTouch,
+ } from "$root";
+ import { fmt_cl, parse_layer, type ITrellis } from "@radroots/util";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis,
+ el_default,
+ el_offset,
+ el_append,
+ }: {
+ basis: ITrellis;
+ el_default?: Snippet;
+ el_offset?: Snippet;
+ el_append?: Snippet;
+ } = $props();
+
+ const hide_border_t = $derived(
+ typeof basis.hide_border_top === `boolean`
+ ? basis.hide_border_top
+ : true,
+ );
+
+ const hide_border_b = $derived(
+ typeof basis.hide_border_bottom === `boolean`
+ ? basis.hide_border_bottom
+ : true,
+ );
+
+ const hide_rounded = $derived(
+ typeof basis.hide_rounded === `boolean` ? basis.hide_rounded : false,
+ );
+
+ const set_title_background = $derived(
+ typeof basis.set_title_background === `boolean`
+ ? basis.set_title_background
+ : false,
+ );
+
+ const set_default_background = $derived(
+ typeof basis.set_default_background === `boolean`
+ ? basis.set_default_background
+ : false,
+ );
+</script>
+
+<div
+ id={basis.id || ``}
+ class={`${fmt_cl(basis.classes)} flex flex-col`}
+ data-view={basis.view || ``}
+>
+ <div
+ class={`relative flex flex-col h-auto w-lo_${$app_lo} gap-[3px] ${set_title_background ? `bg-layer-${basis.layer}-surface` : ``}`}
+ >
+ {#if basis.title && (!basis.default_el || (basis.default_el && basis.default_el.show_title))}
+ <TrellisTitle
+ basis={basis.title}
+ layer={parse_layer(basis.layer - 1)}
+ />
+ {/if}
+ {#if basis.default_el}
+ <div
+ class={`flex flex-col h-auto w-full justify-center items-center`}
+ >
+ {#if el_default}
+ {@render el_default()}
+ {:else if basis.default_el}
+ <TrellisDefaultLabel
+ layer={parse_layer(basis.layer - 1)}
+ labels={basis.default_el.labels
+ ? basis.default_el.labels
+ : [
+ {
+ label: `${$lls(`common.no_items_to_display`)}.`,
+ },
+ ]}
+ />
+ {/if}
+ </div>
+ {:else if basis.list}
+ <div class={`flex flex-col w-full justify-center items-center`}>
+ {#each basis.list as li}
+ {#if li}
+ <div
+ class={`${li.hide_field ? `hidden` : ``} group flex flex-row h-full w-full justify-end items-center bg-layer-${basis.layer}-surface ${li.full_rounded ? `rounded-touch` : ``} ${hide_rounded ? `` : `first:rounded-t-2xl last:rounded-b-2xl`} ${!li.hide_active ? `active:bg-layer-${basis.layer}-surface_a` : ``} el-re`}
+ >
+ <div
+ class={`flex flex-row h-full w-full gap-1 items-center overflow-y-hidden`}
+ >
+ {#if !basis.hide_offset}
+ <TrellisOffset
+ basis={li.offset}
+ layer={basis.layer}
+ />
+ {/if}
+ {#if el_offset}
+ {@render el_offset()}
+ {/if}
+ {#if `touch` in li && li.touch}
+ <TrellisTouch
+ basis={li.touch}
+ layer={basis.layer}
+ {hide_border_b}
+ {hide_border_t}
+ hide_active={!!li.hide_active}
+ />
+ {:else if `input` in li && li.input}
+ <TrellisInput
+ basis={li.input}
+ layer={basis.layer}
+ {hide_border_b}
+ {hide_border_t}
+ />
+ {:else if `select` in li && li.select}
+ <TrellisSelect
+ basis={li.select}
+ layer={basis.layer}
+ {hide_border_b}
+ {hide_border_t}
+ hide_active={!!li.hide_active}
+ />
+ {/if}
+ </div>
+ </div>
+ {/if}
+ {/each}
+ </div>
+ {/if}
+ </div>
+ {#if el_append}
+ <div
+ class={`flex flex-col w-full ${set_default_background ? `bg-layer-${basis.layer}-surface` : ``}`}
+ >
+ {@render el_append()}
+ </div>
+ {/if}
+</div>
diff --git a/apps-lib/src/lib/components/upload/image-upload-add-photo.svelte b/apps-lib/src/lib/components/upload/image-upload-add-photo.svelte
@@ -0,0 +1,41 @@
+<script lang="ts">
+ import { Glyph, lls } from "$root";
+ import type { LcPhotoAddCallback } from "@radroots/util";
+
+ let {
+ basis,
+ photo_path = $bindable(``),
+ }: {
+ basis: {
+ lc_handle_photo_add: LcPhotoAddCallback;
+ };
+ photo_path: string;
+ } = $props();
+</script>
+
+<div class={`relative flex flex-row w-full justify-center items-center`}>
+ <button
+ class={`flex flex-row h-[5rem] w-[5rem] justify-center items-center bg-layer-1-surface/60 rounded-full`}
+ onclick={async () => {
+ const photo_path_add = await basis.lc_handle_photo_add();
+ if (photo_path_add) photo_path = photo_path_add;
+ }}
+ >
+ <Glyph
+ basis={{
+ classes: `text-[40px] text-layer-2-glyph`,
+ dim: `sm`,
+ key: `camera`,
+ }}
+ />
+ <div
+ class={`absolute -bottom-[1.8rem] flex flex-row justify-start items-center`}
+ >
+ <p
+ class={`font-arch font-[600] text-sm text-layer-0-glyph capitalize`}
+ >
+ {`${$lls(`icu.add_*`, { value: `${$lls(`common.photo`)}` })}`}
+ </p>
+ </div>
+ </button>
+</div>
diff --git a/apps-lib/src/lib/components/upload/image-upload-buttons-aspect.svelte b/apps-lib/src/lib/components/upload/image-upload-buttons-aspect.svelte
@@ -0,0 +1,95 @@
+<script lang="ts">
+ import { ButtonLabelGlyph, Empty } from "$root";
+ import {
+ type CallbackPromiseGeneric,
+ type ImageAspectRatio,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: {
+ callback: CallbackPromiseGeneric<ImageAspectRatio>;
+ };
+ } = $props();
+
+ let ratio_curr: ImageAspectRatio = $state(`auto`);
+</script>
+
+<div class={`flex flex-row w-full justify-around items-center`}>
+ <ButtonLabelGlyph
+ basis={{
+ glyph: `crop`,
+ label: `Auto`,
+ ring: ratio_curr === `auto`,
+ callback: async () => {
+ ratio_curr = `auto`;
+ await basis.callback(`auto`);
+ },
+ }}
+ />
+ <ButtonLabelGlyph
+ basis={{
+ label: `1:1`,
+ ring: ratio_curr === `1/1`,
+ callback: async () => {
+ ratio_curr = `1/1`;
+ await basis.callback(`1/1`);
+ },
+ }}
+ >
+ <div
+ class={`flex flex-row h-5 w-5 justify-start items-center rounded-md bg-layer-2-surface shadow-lg`}
+ >
+ <Empty />
+ </div>
+ </ButtonLabelGlyph>
+ <ButtonLabelGlyph
+ basis={{
+ label: `4:3`,
+ ring: ratio_curr === `4/3`,
+ callback: async () => {
+ ratio_curr = `4/3`;
+ await basis.callback(`4/3`);
+ },
+ }}
+ >
+ <div
+ class={`flex flex-row w-5 aspect-[4/3] justify-start items-center rounded-md bg-layer-2-surface shadow-lg rotate-90`}
+ >
+ <Empty />
+ </div>
+ </ButtonLabelGlyph>
+ <ButtonLabelGlyph
+ basis={{
+ label: `16:9`,
+ ring: ratio_curr === `16/9`,
+ callback: async () => {
+ ratio_curr = `16/9`;
+ await basis.callback(`16/9`);
+ },
+ }}
+ >
+ <div
+ class={`flex flex-row w-5 aspect-[16/9] justify-start items-center rounded-md bg-layer-2-surface shadow-lg rotate-90`}
+ >
+ <Empty />
+ </div>
+ </ButtonLabelGlyph>
+ <ButtonLabelGlyph
+ basis={{
+ label: `3:4`,
+ ring: ratio_curr === `3/4`,
+ callback: async () => {
+ ratio_curr = `3/4`;
+ await basis.callback(`3/4`);
+ },
+ }}
+ >
+ <div
+ class={`flex flex-row w-5 aspect-[3/4] justify-start items-center rounded-md bg-layer-2-surface shadow-lg`}
+ >
+ <Empty />
+ </div>
+ </ButtonLabelGlyph>
+</div>
diff --git a/apps-lib/src/lib/components/upload/image-upload-simple.svelte b/apps-lib/src/lib/components/upload/image-upload-simple.svelte
@@ -0,0 +1,65 @@
+<script lang="ts">
+ import { Glyph, ImagePath, ImageUploadButtonsAspect } from "$root";
+ import {
+ list_assign,
+ type LcPhotoAddMultipleCallback,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ photo_paths = $bindable([]),
+ }: {
+ basis: {
+ lc_handle_photo_add: LcPhotoAddMultipleCallback;
+ };
+ photo_paths: string[];
+ } = $props();
+
+ let img_data = $state(``);
+</script>
+
+<div class={`flex flex-col w-full gap-4 justify-center items-center`}>
+ {#if img_data}
+ <div
+ class={`flex flex-row h-[20rem] w-full gap-2 justify-center items-center rounded-3xl bg-layer-1-surface/60 overflow-hidden`}
+ >
+ <ImagePath basis={{ path: img_data }} />
+ </div>
+ {:else}
+ <button
+ class={`flex flex-row w-full justify-center items-center`}
+ onclick={async () => {
+ const photo_paths_add = await basis.lc_handle_photo_add();
+ if (photo_paths_add) {
+ photo_paths = list_assign(photo_paths, photo_paths_add);
+ img_data = photo_paths[0];
+ }
+ }}
+ >
+ <div
+ class={`flex flex-row h-[20rem] w-full gap-2 justify-center items-center border-line border-layer-0-glyph/20 border-[3px] border-dashed rounded-3xl bg-layer-1-surface/60`}
+ >
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph/80`,
+ dim: `sm`,
+
+ key: `upload-simple`,
+ }}
+ />
+ <p
+ class={`font-sans font-[400] text-layer-0-glyph/80 capitalize`}
+ >
+ {`Upload image`}
+ </p>
+ </div>
+ </button>
+ {/if}
+ <ImageUploadButtonsAspect
+ basis={{
+ callback: async (ratio) => {
+ console.log(`ratio `, ratio);
+ },
+ }}
+ />
+</div>
diff --git a/apps-lib/src/lib/el/glyph.svelte b/apps-lib/src/lib/el/glyph.svelte
@@ -1,19 +0,0 @@
-<script lang="ts">
- import { fmt_cl, glyph_style_map, type IGlyph } from "$lib";
-
- export let basis: IGlyph;
- $: basis = basis;
-
- $: weight =
- !basis?.weight || basis?.weight === `regular` ? `` : `-${basis.weight}`;
- $: styles = basis?.dim
- ? glyph_style_map.get(basis.dim)
- : glyph_style_map.get(`sm`);
-</script>
-
-<div
- id={basis.id || null}
- class={`${fmt_cl(basis.classes)} flex flex-row text-[${styles?.gl_1}px] justify-center items-center`}
->
- <i class={`ph${weight} ph-${basis.key}`}></i>
-</div>
diff --git a/apps-lib/src/lib/el/image-blob.svelte b/apps-lib/src/lib/el/image-blob.svelte
@@ -1,43 +0,0 @@
-<script lang="ts">
- import type { IIdOpt } from "$lib";
- import { onDestroy, onMount } from "svelte";
-
- export let basis: IIdOpt & {
- data: Uint8Array | undefined;
- alt?: string;
- };
-
- let img_url = ``;
-
- onMount(async () => {
- try {
- if (!basis.data) return;
- img_url = URL.createObjectURL(
- new Blob([basis.data], {
- type: "image/jpeg",
- }),
- );
- } catch (e) {
- console.log(`(error) image blob `, e);
- }
- });
-
- onDestroy(async () => {
- try {
- URL.revokeObjectURL(img_url);
- } catch (e) {}
- });
-</script>
-
-{#if img_url}
- <img id={basis.id || null} src={img_url} alt={basis.alt || null} />
-{/if}
-
-<style>
- img {
- height: 100%;
- width: 100%;
- object-fit: cover;
- display: block;
- }
-</style>
diff --git a/apps-lib/src/lib/el/image-path.svelte b/apps-lib/src/lib/el/image-path.svelte
@@ -1,48 +0,0 @@
-<!-- svelte-ignore a11y-click-events-have-key-events -->
-<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
-<script lang="ts">
- import { type ICbGOpt, type IClOpt, type IIdOpt, fmt_cl } from "$lib";
- import { onMount } from "svelte";
-
- export let basis: IClOpt &
- ICbGOpt<
- MouseEvent & {
- currentTarget: EventTarget & HTMLImageElement;
- }
- > &
- IIdOpt & {
- path?: string;
- alt?: string;
- };
-
- let img_src = ``;
-
- onMount(async () => {
- try {
- if (basis.path) img_src = basis.path;
- } catch (e) {
- } finally {
- }
- });
-</script>
-
-{#if img_src}
- <img
- id={basis?.id || null}
- class={`${fmt_cl(basis?.classes)}`}
- src={img_src}
- alt={basis?.alt || null}
- on:click|stopPropagation={async (ev) => {
- if (basis?.callback) await basis.callback(ev);
- }}
- />
-{/if}
-
-<style>
- img {
- height: 100%;
- width: 100%;
- object-fit: cover;
- display: block;
- }
-</style>
diff --git a/apps-lib/src/lib/el/input.svelte b/apps-lib/src/lib/el/input.svelte
@@ -1,104 +0,0 @@
-<script lang="ts">
- import { browser } from "$app/environment";
- import {
- type IInput,
- fmt_cl,
- kv_basis,
- parse_layer,
- value_constrain,
- } from "$lib";
- import { onMount } from "svelte";
-
- let el: HTMLInputElement | null = null;
-
- export let value: string = ``;
- export let basis: IInput<string>;
-
- onMount(async () => {
- try {
- await kv_init();
- } catch (e) {
- } finally {
- }
- });
-
- $: basis = basis;
- $: id = basis?.id ? basis?.id : null;
- $: layer =
- typeof basis?.layer === `boolean`
- ? parse_layer(0)
- : parse_layer(basis?.layer);
- $: classes_layer =
- typeof basis?.layer === `boolean` || typeof basis?.layer === `undefined`
- ? ``
- : `bg-layer-${layer}-surface text-layer-${layer}-glyph placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`;
- $: if (basis?.id && basis?.sync && value) {
- (async () => {
- try {
- browser && (await kv_basis.set(basis?.id, value));
- } catch (e) {}
- })();
- }
-
- const kv_init = async (): Promise<void> => {
- try {
- if (!basis?.id) return;
- if (basis?.sync) {
- const kv_val = browser && (await kv_basis.get(basis?.id));
- if (kv_val && el) el.value = kv_val;
- else browser && (await kv_basis.set(basis?.id, ``));
- }
- if (basis?.callback_mount) await basis?.callback_mount({ el });
- } catch (e) {
- console.log(`(error) kv_init `, e);
- }
- };
-
- const handle_on_input = async (el: HTMLInputElement): Promise<void> => {
- try {
- let pass = true;
- let val = el?.value;
- if (basis?.field && el) {
- val = value_constrain(basis?.field.charset, val);
- el.value = val;
- if (
- !basis?.field.validate.test(val) &&
- basis?.field.validate_keypress
- ) {
- //@todo set styles
- }
- pass = basis?.field.validate.test(val);
- }
- if (basis?.sync) browser && (await kv_basis.set(basis?.id, val));
- if (basis?.callback) await basis?.callback({ value: val, pass });
- } catch (e) {
- console.log(`(error) handle_on_input `, e);
- }
- };
-</script>
-
-<input
- bind:this={el}
- bind:value
- 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:focus={async ({ currentTarget: el }) => {
- if (basis.callback_focus) await basis.callback_focus({ el });
- }}
- on:keydown={async (ev) => {
- if (basis?.callback_keydown)
- await basis?.callback_keydown({
- key: ev.key,
- key_s: ev.key === `Enter`,
- el: ev.currentTarget,
- });
- }}
- {id}
- type="text"
- class={`${fmt_cl(basis?.classes)} el-input ${classes_layer} el-re`}
- placeholder={basis?.placeholder || ``}
-/>
diff --git a/apps-lib/src/lib/el/label-swap.svelte b/apps-lib/src/lib/el/label-swap.svelte
@@ -1,32 +0,0 @@
-<!-- svelte-ignore a11y-label-has-associated-control -->
-<script lang="ts">
- import { fmt_cl, type ILabelSwap, type ILyOpt } from "$lib";
-
- export let el_swap: HTMLLabelElement | null = null;
- export let basis: ILabelSwap & ILyOpt;
- $: basis = basis;
-
- $: layer = basis?.layer ? basis.layer : 1;
-</script>
-
-<div class={`flex flex-row justify-start items-center`}>
- <label
- bind:this={el_swap}
- class={`swap${basis.swap.toggle ? ` swap-active` : ``}`}
- >
- <div class="swap-on">
- <p
- class={`${fmt_cl(basis.swap.on.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-60`)} font-sans -translate-y-[1px] el-re`}
- >
- {basis.swap.on.value}
- </p>
- </div>
- <div class="swap-off">
- <p
- class={`${fmt_cl(basis.swap.off.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-60`)} font-sans -translate-y-[1px] el-re`}
- >
- {basis.swap.off.value}
- </p>
- </div>
- </label>
-</div>
diff --git a/apps-lib/src/lib/el/load-symbol.svelte b/apps-lib/src/lib/el/load-symbol.svelte
@@ -1,64 +0,0 @@
-<script lang="ts">
- import { type ILoadSymbol, loading_style_map } from "$lib";
-
- export let basis: ILoadSymbol | undefined = undefined;
- $: basis = basis;
-
- $: styles = basis?.dim
- ? loading_style_map.get(basis?.dim)
- : loading_style_map.get("sm");
- $: num_blades = basis?.blades || 8;
-</script>
-
-<div
- class={`relative flex flex-row justify-center items-center h-[${styles?.dim_1}px] w-[${styles?.dim_1}px] fade-in el-re`}
->
- <div
- class={`${num_blades === 12 ? `spinner12 center` : `spinner8 center`} text-[${styles?.gl_2 || styles?.dim_1}px]`}
- >
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- {#if num_blades === 12}
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- <div
- class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`}
- ></div>
- {/if}
- </div>
-</div>
diff --git a/apps-lib/src/lib/el/select-menu.svelte b/apps-lib/src/lib/el/select-menu.svelte
@@ -1,64 +0,0 @@
-<script lang="ts">
- import { fmt_cl, parse_layer, type ISelect } from "$lib";
-
- export let value: string;
- export let basis: ISelect;
- $: 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);
- $: classes_layer =
- typeof basis?.layer === `boolean` ? `` : `text-layer-${layer}-glyph`;
-</script>
-
-<div
- class={`relative flex flex-row h-max w-auto justify-center items-center`}
- bind:this={el_wrap}
->
- <div
- class={`${fmt_cl(basis.classes)} z-20 absolute top-0 left-0 flex flex-row h-full w-full justify-end items-center ${classes_layer}`}
- >
- <select
- class={`select select-ghost h-full w-full bg-transparent focus:border-0 focus:outline-0 text-transparent focus:text-transparent`}
- 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>
- </div>
- <div class={`z-10 flex flex-row h-full w-full`}>
- <slot name="element" />
- </div>
-</div>
diff --git a/apps-lib/src/lib/el/select.svelte b/apps-lib/src/lib/el/select.svelte
@@ -1,111 +0,0 @@
-<script lang="ts">
- import { browser } from "$app/environment";
- import { fmt_cl, Glyph, kv_basis, parse_layer, type ISelect } from "$lib";
- import { onMount } from "svelte";
-
- let el: HTMLSelectElement | null = null;
-
- export let value: string;
- export let basis: ISelect;
-
- $: basis = basis;
- $: id = basis?.id ? basis?.id : null;
- $: layer =
- typeof basis?.layer === `boolean`
- ? parse_layer(0)
- : parse_layer(basis.layer);
- $: classes_layer =
- typeof basis?.layer === `boolean`
- ? ``
- : !value
- ? `text-layer-${layer}-glyph_pl`
- : `text-layer-${layer}-glyph_d`;
- onMount(async () => {
- try {
- if (basis.id && basis.sync_init) {
- const sync_val = browser && (await kv_basis.get(basis.id));
- browser && (await kv_basis.set(basis.id, sync_val || ``));
- }
- } catch (e) {
- } finally {
- }
- });
-
- $: if (basis?.id && basis?.sync) {
- (async () => {
- try {
- browser && (await kv_basis.set(basis?.id, value));
- } catch (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) browser && (await kv_basis.set(basis?.id, value));
- if (basis.callback && opt) await basis.callback(opt);
- } catch (e) {
- console.log(`(error) handle_on_change `, e);
- }
- };
-</script>
-
-{#if basis?.show_arrows === "l"}
- <div class={`flex flex-row justify-center items-center`}>
- <Glyph
- basis={{
- key: `caret-up-down`,
- dim: `xs`,
- weight: `bold`,
- classes: `text-layer-${layer}-glyph translate-y-[1px]`,
- }}
- ></Glyph>
- </div>
-{/if}
-<select
- bind:this={el}
- bind:value
- on:change={async ({ currentTarget: el }) => {
- handle_on_change(el);
- }}
- {id}
- class={`${fmt_cl(basis.classes)} z-10 el-select ${classes_layer}`}
->
- {#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>
-{#if basis?.show_arrows === "r"}
- <div class={`flex flex-row justify-center items-center`}>
- <Glyph
- basis={{
- key: `caret-up-down`,
- dim: `xs`,
- weight: `bold`,
- classes: `text-layer-${layer}-glyph`,
- }}
- ></Glyph>
- </div>
-{/if}
diff --git a/apps-lib/src/lib/el/styles.svelte b/apps-lib/src/lib/el/styles.svelte
@@ -1 +0,0 @@
-<div class="hidden -bottom-lo_view_mobile_base -bottom-lo_view_mobile_y -bottom-nav_mobile_base -bottom-nav_mobile_y -bottom-tabs_mobile_base -bottom-tabs_mobile_y -bottom-trellis_centered_mobile_base -bottom-trellis_centered_mobile_y -bottom-view_mobile_base -bottom-view_mobile_y -bottom-view_offset_mobile_base -bottom-view_offset_mobile_y -top-lo_view_mobile_base -top-lo_view_mobile_y -top-nav_mobile_base -top-nav_mobile_y -top-tabs_mobile_base -top-tabs_mobile_y -top-trellis_centered_mobile_base -top-trellis_centered_mobile_y -top-view_mobile_base -top-view_mobile_y -top-view_offset_mobile_base -top-view_offset_mobile_y -translate-y-h_lo_view_mobile_base -translate-y-h_lo_view_mobile_y -translate-y-h_nav_mobile_base -translate-y-h_nav_mobile_y -translate-y-h_tabs_mobile_base -translate-y-h_tabs_mobile_y -translate-y-h_trellis_centered_mobile_base -translate-y-h_trellis_centered_mobile_y -translate-y-h_view_mobile_base -translate-y-h_view_mobile_y -translate-y-h_view_offset_mobile_base -translate-y-h_view_offset_mobile_y active:bg-layer_0_glyph active:bg-layer_0_glyph_a active:bg-layer_0_glyph_hl active:bg-layer_0_glyph_hl_a active:bg-layer_0_glyph_label active:bg-layer_0_glyph_pl active:bg-layer_0_glyph_shade active:bg-layer_0_surface active:bg-layer_0_surface_a active:bg-layer_0_surface_blur active:bg-layer_0_surface_edge active:bg-layer_0_surface_w active:bg-layer_1_glyph active:bg-layer_1_glyph_a active:bg-layer_1_glyph_d active:bg-layer_1_glyph_hl active:bg-layer_1_glyph_hl_a active:bg-layer_1_glyph_label active:bg-layer_1_glyph_pl active:bg-layer_1_glyph_shade active:bg-layer_1_surface active:bg-layer_1_surface_a active:bg-layer_1_surface_edge active:bg-layer_1_surface_err active:bg-layer_1_surface_focus active:bg-layer_2_glyph active:bg-layer_2_glyph_a active:bg-layer_2_glyph_d active:bg-layer_2_glyph_hl active:bg-layer_2_glyph_hl_a active:bg-layer_2_glyph_pl active:bg-layer_2_glyph_shade active:bg-layer_2_surface active:bg-layer_2_surface_a active:bg-layer_2_surface_edge active:border-layer_0_glyph active:border-layer_0_glyph_a active:border-layer_0_glyph_hl active:border-layer_0_glyph_hl_a active:border-layer_0_glyph_label active:border-layer_0_glyph_pl active:border-layer_0_glyph_shade active:border-layer_0_surface active:border-layer_0_surface_a active:border-layer_0_surface_blur active:border-layer_0_surface_edge active:border-layer_0_surface_w active:border-layer_1_glyph active:border-layer_1_glyph_a active:border-layer_1_glyph_d active:border-layer_1_glyph_hl active:border-layer_1_glyph_hl_a active:border-layer_1_glyph_label active:border-layer_1_glyph_pl active:border-layer_1_glyph_shade active:border-layer_1_surface active:border-layer_1_surface_a active:border-layer_1_surface_edge active:border-layer_1_surface_err active:border-layer_1_surface_focus active:border-layer_2_glyph active:border-layer_2_glyph_a active:border-layer_2_glyph_d active:border-layer_2_glyph_hl active:border-layer_2_glyph_hl_a active:border-layer_2_glyph_pl active:border-layer_2_glyph_shade active:border-layer_2_surface active:border-layer_2_surface_a active:border-layer_2_surface_edge active:text-layer_0_glyph active:text-layer_0_glyph_a active:text-layer_0_glyph_hl active:text-layer_0_glyph_hl_a active:text-layer_0_glyph_label active:text-layer_0_glyph_pl active:text-layer_0_glyph_shade active:text-layer_0_surface active:text-layer_0_surface_a active:text-layer_0_surface_blur active:text-layer_0_surface_edge active:text-layer_0_surface_w active:text-layer_1_glyph active:text-layer_1_glyph_a active:text-layer_1_glyph_d active:text-layer_1_glyph_hl active:text-layer_1_glyph_hl_a active:text-layer_1_glyph_label active:text-layer_1_glyph_pl active:text-layer_1_glyph_shade active:text-layer_1_surface active:text-layer_1_surface_a active:text-layer_1_surface_edge active:text-layer_1_surface_err active:text-layer_1_surface_focus active:text-layer_2_glyph active:text-layer_2_glyph_a active:text-layer_2_glyph_d active:text-layer_2_glyph_hl active:text-layer_2_glyph_hl_a active:text-layer_2_glyph_pl active:text-layer_2_glyph_shade active:text-layer_2_surface active:text-layer_2_surface_a active:text-layer_2_surface_edge bg-layer_0_glyph bg-layer_0_glyph_a bg-layer_0_glyph_hl bg-layer_0_glyph_hl_a bg-layer_0_glyph_label bg-layer_0_glyph_pl bg-layer_0_glyph_shade bg-layer_0_surface bg-layer_0_surface_a bg-layer_0_surface_blur bg-layer_0_surface_edge bg-layer_0_surface_w bg-layer_1_glyph bg-layer_1_glyph_a bg-layer_1_glyph_d bg-layer_1_glyph_hl bg-layer_1_glyph_hl_a bg-layer_1_glyph_label bg-layer_1_glyph_pl bg-layer_1_glyph_shade bg-layer_1_surface bg-layer_1_surface_a bg-layer_1_surface_edge bg-layer_1_surface_err bg-layer_1_surface_focus bg-layer_2_glyph bg-layer_2_glyph_a bg-layer_2_glyph_d bg-layer_2_glyph_hl bg-layer_2_glyph_hl_a bg-layer_2_glyph_pl bg-layer_2_glyph_shade bg-layer_2_surface bg-layer_2_surface_a bg-layer_2_surface_edge border-layer_0_glyph border-layer_0_glyph_a border-layer_0_glyph_hl border-layer_0_glyph_hl_a border-layer_0_glyph_label border-layer_0_glyph_pl border-layer_0_glyph_shade border-layer_0_surface border-layer_0_surface_a border-layer_0_surface_blur border-layer_0_surface_edge border-layer_0_surface_w border-layer_1_glyph border-layer_1_glyph_a border-layer_1_glyph_d border-layer_1_glyph_hl border-layer_1_glyph_hl_a border-layer_1_glyph_label border-layer_1_glyph_pl border-layer_1_glyph_shade border-layer_1_surface border-layer_1_surface_a border-layer_1_surface_edge border-layer_1_surface_err border-layer_1_surface_focus border-layer_2_glyph border-layer_2_glyph_a border-layer_2_glyph_d border-layer_2_glyph_hl border-layer_2_glyph_hl_a border-layer_2_glyph_pl border-layer_2_glyph_shade border-layer_2_surface border-layer_2_surface_a border-layer_2_surface_edge bottom-lo_view_mobile_base bottom-lo_view_mobile_y bottom-nav_mobile_base bottom-nav_mobile_y bottom-tabs_mobile_base bottom-tabs_mobile_y bottom-trellis_centered_mobile_base bottom-trellis_centered_mobile_y bottom-view_mobile_base bottom-view_mobile_y bottom-view_offset_mobile_base bottom-view_offset_mobile_y focus:bg-layer_0_glyph focus:bg-layer_0_glyph_a focus:bg-layer_0_glyph_hl focus:bg-layer_0_glyph_hl_a focus:bg-layer_0_glyph_label focus:bg-layer_0_glyph_pl focus:bg-layer_0_glyph_shade focus:bg-layer_0_surface focus:bg-layer_0_surface_a focus:bg-layer_0_surface_blur focus:bg-layer_0_surface_edge focus:bg-layer_0_surface_w focus:bg-layer_1_glyph focus:bg-layer_1_glyph_a focus:bg-layer_1_glyph_d focus:bg-layer_1_glyph_hl focus:bg-layer_1_glyph_hl_a focus:bg-layer_1_glyph_label focus:bg-layer_1_glyph_pl focus:bg-layer_1_glyph_shade focus:bg-layer_1_surface focus:bg-layer_1_surface_a focus:bg-layer_1_surface_edge focus:bg-layer_1_surface_err focus:bg-layer_1_surface_focus focus:bg-layer_2_glyph focus:bg-layer_2_glyph_a focus:bg-layer_2_glyph_d focus:bg-layer_2_glyph_hl focus:bg-layer_2_glyph_hl_a focus:bg-layer_2_glyph_pl focus:bg-layer_2_glyph_shade focus:bg-layer_2_surface focus:bg-layer_2_surface_a focus:bg-layer_2_surface_edge focus:border-layer_0_glyph focus:border-layer_0_glyph_a focus:border-layer_0_glyph_hl focus:border-layer_0_glyph_hl_a focus:border-layer_0_glyph_label focus:border-layer_0_glyph_pl focus:border-layer_0_glyph_shade focus:border-layer_0_surface focus:border-layer_0_surface_a focus:border-layer_0_surface_blur focus:border-layer_0_surface_edge focus:border-layer_0_surface_w focus:border-layer_1_glyph focus:border-layer_1_glyph_a focus:border-layer_1_glyph_d focus:border-layer_1_glyph_hl focus:border-layer_1_glyph_hl_a focus:border-layer_1_glyph_label focus:border-layer_1_glyph_pl focus:border-layer_1_glyph_shade focus:border-layer_1_surface focus:border-layer_1_surface_a focus:border-layer_1_surface_edge focus:border-layer_1_surface_err focus:border-layer_1_surface_focus focus:border-layer_2_glyph focus:border-layer_2_glyph_a focus:border-layer_2_glyph_d focus:border-layer_2_glyph_hl focus:border-layer_2_glyph_hl_a focus:border-layer_2_glyph_pl focus:border-layer_2_glyph_shade focus:border-layer_2_surface focus:border-layer_2_surface_a focus:border-layer_2_surface_edge focus:text-layer_0_glyph focus:text-layer_0_glyph_a focus:text-layer_0_glyph_hl focus:text-layer_0_glyph_hl_a focus:text-layer_0_glyph_label focus:text-layer_0_glyph_pl focus:text-layer_0_glyph_shade focus:text-layer_0_surface focus:text-layer_0_surface_a focus:text-layer_0_surface_blur focus:text-layer_0_surface_edge focus:text-layer_0_surface_w focus:text-layer_1_glyph focus:text-layer_1_glyph_a focus:text-layer_1_glyph_d focus:text-layer_1_glyph_hl focus:text-layer_1_glyph_hl_a focus:text-layer_1_glyph_label focus:text-layer_1_glyph_pl focus:text-layer_1_glyph_shade focus:text-layer_1_surface focus:text-layer_1_surface_a focus:text-layer_1_surface_edge focus:text-layer_1_surface_err focus:text-layer_1_surface_focus focus:text-layer_2_glyph focus:text-layer_2_glyph_a focus:text-layer_2_glyph_d focus:text-layer_2_glyph_hl focus:text-layer_2_glyph_hl_a focus:text-layer_2_glyph_pl focus:text-layer_2_glyph_shade focus:text-layer_2_surface focus:text-layer_2_surface_a focus:text-layer_2_surface_edge group-active:bg-layer_0_glyph group-active:bg-layer_0_glyph_a group-active:bg-layer_0_glyph_hl group-active:bg-layer_0_glyph_hl_a group-active:bg-layer_0_glyph_label group-active:bg-layer_0_glyph_pl group-active:bg-layer_0_glyph_shade group-active:bg-layer_0_surface group-active:bg-layer_0_surface_a group-active:bg-layer_0_surface_blur group-active:bg-layer_0_surface_edge group-active:bg-layer_0_surface_w group-active:bg-layer_1_glyph group-active:bg-layer_1_glyph_a group-active:bg-layer_1_glyph_d group-active:bg-layer_1_glyph_hl group-active:bg-layer_1_glyph_hl_a group-active:bg-layer_1_glyph_label group-active:bg-layer_1_glyph_pl group-active:bg-layer_1_glyph_shade group-active:bg-layer_1_surface group-active:bg-layer_1_surface_a group-active:bg-layer_1_surface_edge group-active:bg-layer_1_surface_err group-active:bg-layer_1_surface_focus group-active:bg-layer_2_glyph group-active:bg-layer_2_glyph_a group-active:bg-layer_2_glyph_d group-active:bg-layer_2_glyph_hl group-active:bg-layer_2_glyph_hl_a group-active:bg-layer_2_glyph_pl group-active:bg-layer_2_glyph_shade group-active:bg-layer_2_surface group-active:bg-layer_2_surface_a group-active:bg-layer_2_surface_edge group-active:border-layer_0_glyph group-active:border-layer_0_glyph_a group-active:border-layer_0_glyph_hl group-active:border-layer_0_glyph_hl_a group-active:border-layer_0_glyph_label group-active:border-layer_0_glyph_pl group-active:border-layer_0_glyph_shade group-active:border-layer_0_surface group-active:border-layer_0_surface_a group-active:border-layer_0_surface_blur group-active:border-layer_0_surface_edge group-active:border-layer_0_surface_w group-active:border-layer_1_glyph group-active:border-layer_1_glyph_a group-active:border-layer_1_glyph_d group-active:border-layer_1_glyph_hl group-active:border-layer_1_glyph_hl_a group-active:border-layer_1_glyph_label group-active:border-layer_1_glyph_pl group-active:border-layer_1_glyph_shade group-active:border-layer_1_surface group-active:border-layer_1_surface_a group-active:border-layer_1_surface_edge group-active:border-layer_1_surface_err group-active:border-layer_1_surface_focus group-active:border-layer_2_glyph group-active:border-layer_2_glyph_a group-active:border-layer_2_glyph_d group-active:border-layer_2_glyph_hl group-active:border-layer_2_glyph_hl_a group-active:border-layer_2_glyph_pl group-active:border-layer_2_glyph_shade group-active:border-layer_2_surface group-active:border-layer_2_surface_a group-active:border-layer_2_surface_edge group-active:text-layer_0_glyph group-active:text-layer_0_glyph_a group-active:text-layer_0_glyph_hl group-active:text-layer_0_glyph_hl_a group-active:text-layer_0_glyph_label group-active:text-layer_0_glyph_pl group-active:text-layer_0_glyph_shade group-active:text-layer_0_surface group-active:text-layer_0_surface_a group-active:text-layer_0_surface_blur group-active:text-layer_0_surface_edge group-active:text-layer_0_surface_w group-active:text-layer_1_glyph group-active:text-layer_1_glyph_a group-active:text-layer_1_glyph_d group-active:text-layer_1_glyph_hl group-active:text-layer_1_glyph_hl_a group-active:text-layer_1_glyph_label group-active:text-layer_1_glyph_pl group-active:text-layer_1_glyph_shade group-active:text-layer_1_surface group-active:text-layer_1_surface_a group-active:text-layer_1_surface_edge group-active:text-layer_1_surface_err group-active:text-layer_1_surface_focus group-active:text-layer_2_glyph group-active:text-layer_2_glyph_a group-active:text-layer_2_glyph_d group-active:text-layer_2_glyph_hl group-active:text-layer_2_glyph_hl_a group-active:text-layer_2_glyph_pl group-active:text-layer_2_glyph_shade group-active:text-layer_2_surface group-active:text-layer_2_surface_a group-active:text-layer_2_surface_edge group-focus:bg-layer_0_glyph group-focus:bg-layer_0_glyph_a group-focus:bg-layer_0_glyph_hl group-focus:bg-layer_0_glyph_hl_a group-focus:bg-layer_0_glyph_label group-focus:bg-layer_0_glyph_pl group-focus:bg-layer_0_glyph_shade group-focus:bg-layer_0_surface group-focus:bg-layer_0_surface_a group-focus:bg-layer_0_surface_blur group-focus:bg-layer_0_surface_edge group-focus:bg-layer_0_surface_w group-focus:bg-layer_1_glyph group-focus:bg-layer_1_glyph_a group-focus:bg-layer_1_glyph_d group-focus:bg-layer_1_glyph_hl group-focus:bg-layer_1_glyph_hl_a group-focus:bg-layer_1_glyph_label group-focus:bg-layer_1_glyph_pl group-focus:bg-layer_1_glyph_shade group-focus:bg-layer_1_surface group-focus:bg-layer_1_surface_a group-focus:bg-layer_1_surface_edge group-focus:bg-layer_1_surface_err group-focus:bg-layer_1_surface_focus group-focus:bg-layer_2_glyph group-focus:bg-layer_2_glyph_a group-focus:bg-layer_2_glyph_d group-focus:bg-layer_2_glyph_hl group-focus:bg-layer_2_glyph_hl_a group-focus:bg-layer_2_glyph_pl group-focus:bg-layer_2_glyph_shade group-focus:bg-layer_2_surface group-focus:bg-layer_2_surface_a group-focus:bg-layer_2_surface_edge group-focus:border-layer_0_glyph group-focus:border-layer_0_glyph_a group-focus:border-layer_0_glyph_hl group-focus:border-layer_0_glyph_hl_a group-focus:border-layer_0_glyph_label group-focus:border-layer_0_glyph_pl group-focus:border-layer_0_glyph_shade group-focus:border-layer_0_surface group-focus:border-layer_0_surface_a group-focus:border-layer_0_surface_blur group-focus:border-layer_0_surface_edge group-focus:border-layer_0_surface_w group-focus:border-layer_1_glyph group-focus:border-layer_1_glyph_a group-focus:border-layer_1_glyph_d group-focus:border-layer_1_glyph_hl group-focus:border-layer_1_glyph_hl_a group-focus:border-layer_1_glyph_label group-focus:border-layer_1_glyph_pl group-focus:border-layer_1_glyph_shade group-focus:border-layer_1_surface group-focus:border-layer_1_surface_a group-focus:border-layer_1_surface_edge group-focus:border-layer_1_surface_err group-focus:border-layer_1_surface_focus group-focus:border-layer_2_glyph group-focus:border-layer_2_glyph_a group-focus:border-layer_2_glyph_d group-focus:border-layer_2_glyph_hl group-focus:border-layer_2_glyph_hl_a group-focus:border-layer_2_glyph_pl group-focus:border-layer_2_glyph_shade group-focus:border-layer_2_surface group-focus:border-layer_2_surface_a group-focus:border-layer_2_surface_edge group-focus:text-layer_0_glyph group-focus:text-layer_0_glyph_a group-focus:text-layer_0_glyph_hl group-focus:text-layer_0_glyph_hl_a group-focus:text-layer_0_glyph_label group-focus:text-layer_0_glyph_pl group-focus:text-layer_0_glyph_shade group-focus:text-layer_0_surface group-focus:text-layer_0_surface_a group-focus:text-layer_0_surface_blur group-focus:text-layer_0_surface_edge group-focus:text-layer_0_surface_w group-focus:text-layer_1_glyph group-focus:text-layer_1_glyph_a group-focus:text-layer_1_glyph_d group-focus:text-layer_1_glyph_hl group-focus:text-layer_1_glyph_hl_a group-focus:text-layer_1_glyph_label group-focus:text-layer_1_glyph_pl group-focus:text-layer_1_glyph_shade group-focus:text-layer_1_surface group-focus:text-layer_1_surface_a group-focus:text-layer_1_surface_edge group-focus:text-layer_1_surface_err group-focus:text-layer_1_surface_focus group-focus:text-layer_2_glyph group-focus:text-layer_2_glyph_a group-focus:text-layer_2_glyph_d group-focus:text-layer_2_glyph_hl group-focus:text-layer_2_glyph_hl_a group-focus:text-layer_2_glyph_pl group-focus:text-layer_2_glyph_shade group-focus:text-layer_2_surface group-focus:text-layer_2_surface_a group-focus:text-layer_2_surface_edge h-[12px] h-[16px] h-[17px] h-[18px] h-[20px] h-[22px] h-[24px] h-[28px] h-[36px] h-entry_bold h-entry_guide h-entry_line h-envelope_button h-envelope_top h-line h-line_label h-lo_view_mobile_base h-lo_view_mobile_y h-nav_mobile_base h-nav_mobile_y h-tabs_mobile_base h-tabs_mobile_y h-toast_min h-touch_bold h-touch_guide h-trellis_centered_mobile_base h-trellis_centered_mobile_y h-view_mobile_base h-view_mobile_y h-view_offset_mobile_base h-view_offset_mobile_y pb-h_lo_view_mobile_base pb-h_lo_view_mobile_y pb-h_nav_mobile_base pb-h_nav_mobile_y pb-h_tabs_mobile_base pb-h_tabs_mobile_y pb-h_trellis_centered_mobile_base pb-h_trellis_centered_mobile_y pb-h_view_mobile_base pb-h_view_mobile_y pb-h_view_offset_mobile_base pb-h_view_offset_mobile_y pt-h_lo_view_mobile_base pt-h_lo_view_mobile_y pt-h_nav_mobile_base pt-h_nav_mobile_y pt-h_tabs_mobile_base pt-h_tabs_mobile_y pt-h_trellis_centered_mobile_base pt-h_trellis_centered_mobile_y pt-h_view_mobile_base pt-h_view_mobile_y pt-h_view_offset_mobile_base pt-h_view_offset_mobile_y text-[12px] text-[15px] text-[16px] text-[18px] text-[19px] text-[20px] text-[21px] text-[23px] text-[24px] text-[26px] text-[27px] text-[28px] text-[30px] text-[36px] text-[40px] text-layer_0_glyph text-layer_0_glyph_a text-layer_0_glyph_hl text-layer_0_glyph_hl_a text-layer_0_glyph_label text-layer_0_glyph_pl text-layer_0_glyph_shade text-layer_0_surface text-layer_0_surface_a text-layer_0_surface_blur text-layer_0_surface_edge text-layer_0_surface_w text-layer_1_glyph text-layer_1_glyph_a text-layer_1_glyph_d text-layer_1_glyph_hl text-layer_1_glyph_hl_a text-layer_1_glyph_label text-layer_1_glyph_pl text-layer_1_glyph_shade text-layer_1_surface text-layer_1_surface_a text-layer_1_surface_edge text-layer_1_surface_err text-layer_1_surface_focus text-layer_2_glyph text-layer_2_glyph_a text-layer_2_glyph_d text-layer_2_glyph_hl text-layer_2_glyph_hl_a text-layer_2_glyph_pl text-layer_2_glyph_shade text-layer_2_surface text-layer_2_surface_a text-layer_2_surface_edge top-lo_view_mobile_base top-lo_view_mobile_y top-nav_mobile_base top-nav_mobile_y top-tabs_mobile_base top-tabs_mobile_y top-trellis_centered_mobile_base top-trellis_centered_mobile_y top-view_mobile_base top-view_mobile_y top-view_offset_mobile_base top-view_offset_mobile_y translate-y-h_lo_view_mobile_base translate-y-h_lo_view_mobile_y translate-y-h_nav_mobile_base translate-y-h_nav_mobile_y translate-y-h_tabs_mobile_base translate-y-h_tabs_mobile_y translate-y-h_trellis_centered_mobile_base translate-y-h_trellis_centered_mobile_y translate-y-h_view_mobile_base translate-y-h_view_mobile_y translate-y-h_view_offset_mobile_base translate-y-h_view_offset_mobile_y w-[12px] w-[16px] w-[17px] w-[18px] w-[20px] w-[22px] w-[24px] w-[28px] w-[36px] w-mobile_base w-mobile_y"></div>
-\ No newline at end of file
diff --git a/apps-lib/src/lib/el/text-area.svelte b/apps-lib/src/lib/el/text-area.svelte
@@ -1,98 +0,0 @@
-<script lang="ts">
- import { browser } from "$app/environment";
- import {
- type ITextAreaElement,
- fmt_cl,
- fmt_textarea_value,
- kv_basis,
- parse_layer,
- value_constrain_textarea,
- } from "$lib";
- import { onMount } from "svelte";
-
- let el: HTMLTextAreaElement | null = null;
-
- export let value: string = ``;
- export let basis: ITextAreaElement;
- $: basis = basis;
-
- $: id = basis.id ? basis.id : null;
- $: layer =
- typeof basis.layer === `boolean` ? 0 : parse_layer(basis.layer, 1); //@todo
-
- onMount(async () => {
- try {
- await kv_init();
- } catch (e) {}
- });
-
- $: if (basis?.id && basis?.sync && value) {
- (async () => {
- try {
- browser && (await kv_basis.set(basis?.id, value));
- } catch (e) {}
- })();
- }
-
- const kv_init = async (): Promise<void> => {
- try {
- if (!basis?.id) return;
- if (basis?.sync) {
- const kv_val = browser && (await kv_basis.get(basis?.id));
- if (kv_val && el) el.value = fmt_textarea_value(kv_val);
- else browser && (await kv_basis.set(basis?.id, ``));
- }
- if (basis?.on_mount) await basis?.on_mount(el);
- } catch (e) {
- console.log(`(error) kv_init `, e);
- }
- };
-
- const handle_on_input = async (el: HTMLTextAreaElement): Promise<void> => {
- try {
- let pass = true;
- let val = el?.value;
- if (basis?.field && el) {
- val = value_constrain_textarea(basis?.field.charset, val);
- el.value = fmt_textarea_value(val);
- if (
- !basis?.field.validate.test(val) &&
- basis?.field.validate_keypress
- ) {
- //@todo set styles
- }
- pass = basis?.field.validate.test(val);
- }
- if (basis?.sync) browser && (await kv_basis.set(basis?.id, val));
- if (basis?.callback) await basis?.callback({ value: val, pass });
- } catch (e) {
- console.log(`(error) handle_on_input `, e);
- }
- };
-</script>
-
-<textarea
- bind:this={el}
- bind:value
- 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:focus={async ({ currentTarget: el }) => {
- if (basis.callback_focus) await basis.callback_focus({ el });
- }}
- on:keydown={async (ev) => {
- if (basis.callback_keydown)
- await basis.callback_keydown({
- key: ev.key,
- key_s: ev.key === `Enter`,
- el: ev.currentTarget,
- });
- }}
- {id}
- contenteditable="true"
- class={`${fmt_cl(basis.classes)} el-textarea w-full bg-layer-${layer}-surface text-layer-${layer}-glyph placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`}
- placeholder={basis.placeholder || ``}
-></textarea>
diff --git a/apps-lib/src/lib/el/toast.svelte b/apps-lib/src/lib/el/toast.svelte
@@ -1,55 +0,0 @@
-<script lang="ts">
- import {
- app_layout,
- fmt_cl,
- get_layout,
- Glyph,
- parse_layer,
- toast_layout_map,
- toast_style_map,
- type IToast,
- type IToastKind,
- } from "$lib";
-
- export let basis: IToast;
- $: basis = basis;
-
- let styles: IToastKind[] = basis.styles || [`simple`];
- $: styles = styles;
- $: layer = basis.layer ? parse_layer(basis.layer) : 1;
- $: layout = get_layout($app_layout);
-</script>
-
-<div
- class={`${fmt_cl(toast_layout_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(toast_style_map.get(style)?.outer)).join(` `))}`}
- >
- <div
- class={`absolute top-0 left-4 flex flex-row h-full items-center text-layer-${layer}-glyph`}
- >
- <Glyph
- basis={{
- key: `info`,
- weight: `regular`,
- dim: `md`,
- ...basis.glyph,
- }}
- ></Glyph>
- </div>
- <div
- class={`col-span-12 flex flex-row pl-1 ${fmt_cl(styles.map((style) => fmt_cl(toast_style_map.get(style)?.inner)).join(` `))}`}
- >
- {#if `value` in basis.label}
- <p
- class={`font-sans font-[500] truncate text-layer-${layer}-glyph -translate-y-[1px]`}
- >
- {basis.label.value}
- </p>
- {/if}
- </div>
- </div>
- </div>
-</div>
diff --git a/apps-lib/src/lib/feature/image-upload-add-photo.svelte b/apps-lib/src/lib/feature/image-upload-add-photo.svelte
@@ -1,36 +0,0 @@
-<script lang="ts">
- import { Glyph, lls, type CallbackPromiseReturn } from "$lib";
-
- export let bv_photo_path: string;
-
- export let basis: {
- lc_handle_photo_add: CallbackPromiseReturn<string | undefined>;
- };
-</script>
-
-<div class={`relative flex flex-row w-full justify-center items-center`}>
- <button
- class={`flex flex-row h-[5rem] w-[5rem] justify-center items-center bg-layer-1-surface/60 rounded-full`}
- on:click={async () => {
- const photo_path_add = await basis.lc_handle_photo_add();
- if (photo_path_add) bv_photo_path = photo_path_add;
- }}
- >
- <Glyph
- basis={{
- classes: `text-[40px] text-layer-2-glyph`,
- dim: `sm`,
- key: `camera`,
- }}
- ></Glyph>
- <div
- class={`absolute -bottom-[1.8rem] flex flex-row justify-start items-center`}
- >
- <p
- class={`font-arch font-[600] text-sm text-layer-0-glyph capitalize`}
- >
- {`${$lls(`icu.add_*`, { value: `${$lls(`common.photo`)}` })}`}
- </p>
- </div>
- </button>
-</div>
diff --git a/apps-lib/src/lib/feature/search-result-container.svelte b/apps-lib/src/lib/feature/search-result-container.svelte
@@ -1,16 +0,0 @@
-<script lang="ts">
- import type { CallbackPromise } from "$lib";
-
- export let basis: {
- callback: CallbackPromise;
- };
-</script>
-
-<button
- on:click={async () => {
- await basis.callback();
- }}
- class={`group flex flex-row h-[4rem] w-full px-4 justify-between items-center bg-layer-1-surface round-24 layer-1-active-surface el-re`}
->
- <slot />
-</button>
diff --git a/apps-lib/src/lib/feature/search-result-display.svelte b/apps-lib/src/lib/feature/search-result-display.svelte
@@ -1,216 +0,0 @@
-<script lang="ts">
- import {
- ascii,
- Glyph,
- lls,
- SearchResultContainer,
- type ISearchResultDisplayCallbacks,
- type SearchServiceResult,
- } from "$lib";
-
- export let basis: ISearchResultDisplayCallbacks & {
- result: SearchServiceResult;
- };
-</script>
-
-{#if `location_gcs` in basis.result && basis.result.location_gcs.id}
- <SearchResultContainer
- basis={{
- callback: async () => {
- await basis.lc_handle_result_location_gcs(
- basis.result.location_gcs,
- );
- },
- }}
- >
- <div class={`flex flex-row gap-4 justify-start items-center`}>
- <div
- class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-stone-500 round-24`}
- >
- <Glyph
- basis={{
- classes: `text-white`,
- dim: `xs`,
- weight: `bold`,
- key: `compass`,
- }}
- ></Glyph>
- </div>
- <div class={`flex flex-row gap-1 justify-start items-center`}>
- <div
- class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`}
- >
- <p
- class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
- >
- {`${$lls(`common.location`)}`}
- </p>
- </div>
- <p
- class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`}
- >
- {ascii.bullet}
- </p>
-
- {#if basis.result.location_gcs.kind === `farm_land`}
- <div
- class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-lime-500 rounded-md`}
- >
- <p
- class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
- >
- {`${$lls(`common.farm_land`)}`}
- </p>
- </div>
- {/if}
- </div>
- </div>
- <div
- class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`}
- >
- {#if basis.result.location_gcs.label}
- <p
- class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph`}
- >
- {`${basis.result.location_gcs.label}`}
- </p>
- {:else}
- <p
- class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph truncate`}
- >
- {`${basis.result.location_gcs.gc_name}, ${basis.result.location_gcs.gc_admin1_id}`}
- </p>
- {/if}
- </div>
- </SearchResultContainer>
-{:else if `nostr_profile` in basis && basis.result.nostr_profile.id}
- <SearchResultContainer
- basis={{
- callback: async () => {
- await basis.lc_handle_result_nostr_profile(
- basis.result.nostr_profile,
- );
- },
- }}
- >
- <div class={`flex flex-row gap-4 justify-start items-center`}>
- <div
- class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`}
- >
- <Glyph
- basis={{
- classes: `text-white`,
- dim: `xs`,
- weight: `bold`,
- key: `user`,
- }}
- ></Glyph>
- </div>
- <div class={`flex flex-row gap-1 justify-start items-center`}>
- <div
- class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`}
- >
- <p
- class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
- >
- {`${$lls(`common.profile`)}`}
- </p>
- </div>
- <p
- class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`}
- >
- {ascii.bullet}
- </p>
- <div
- class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-purple-400 rounded-md`}
- >
- <p
- class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
- >
- {#if basis.result.result_k === `name`}
- {`${$lls(`common.name`)}`}
- {:else}
- {`@todo`}
- {/if}
- </p>
- </div>
- </div>
- </div>
- <div
- class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`}
- >
- {#if basis.result.nostr_profile.name}
- <p
- class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`}
- >
- {#if basis.result.result_k === `name`}
- {`${basis.result.nostr_profile.name}`}
- {:else if basis.result.result_v}
- {`${basis.result.result_v}`}
- {:else}
- {`@todo`}
- {/if}
- </p>
- {/if}
- </div>
- </SearchResultContainer>
-{:else if `nostr_relay` in basis && basis.result.nostr_relay.id}
- <SearchResultContainer
- basis={{
- callback: async () => {
- await basis.lc_handle_result_nostr_relay(
- basis.result.nostr_relay,
- );
- },
- }}
- >
- <div class={`flex flex-row gap-4 justify-start items-center`}>
- <div
- class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`}
- >
- <Glyph
- basis={{
- classes: `text-white`,
- dim: `xs`,
- weight: `bold`,
- key: `user`,
- }}
- ></Glyph>
- </div>
- <div class={`flex flex-row gap-1 justify-start items-center`}>
- <div
- class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`}
- >
- <p
- class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
- >
- {`${$lls(`common.relay`)}`}
- </p>
- </div>
- <p
- class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`}
- >
- {ascii.bullet}
- </p>
- <div
- class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-yellow-400 rounded-md`}
- >
- <p
- class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
- >
- {`${$lls(`common.url`)}`}
- </p>
- </div>
- </div>
- </div>
- <div
- class={`flex flex-row flex-grow pr-2 justify-end items-center overflow-hidden`}
- >
- <p
- class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`}
- >
- {`${basis.result.nostr_relay.url}`}
- </p>
- </div>
- </SearchResultContainer>
-{/if}
diff --git a/apps-lib/src/lib/features/farm/farms-add-casli-detail.svelte b/apps-lib/src/lib/features/farm/farms-add-casli-detail.svelte
@@ -0,0 +1,73 @@
+<script lang="ts">
+ import { CarouselItem, FormLineLedger, lls } from "$root";
+ import { area_units, form_fields } from "@radroots/util";
+
+ let {
+ val_farmname = $bindable(``),
+ val_farmarea = $bindable(``),
+ val_farmarea_unit = $bindable(``),
+ val_farmcontact = $bindable(``),
+ farm_geop_lat,
+ farm_geop_lng,
+ }: {
+ val_farmname: string;
+ val_farmarea: string;
+ val_farmarea_unit: string;
+ val_farmcontact: string;
+ farm_geop_lat: string;
+ farm_geop_lng: string;
+ } = $props();
+</script>
+
+<CarouselItem>
+ <div
+ class={`flex flex-col h-[100vh] w-full px-6 gap-4 justify-start items-center`}
+ >
+ <FormLineLedger
+ basis={{
+ id: `farm_location`,
+ label: `${$lls(`common.farm_location`)}`,
+ display_value: `${farm_geop_lat}, ${farm_geop_lng}`,
+ }}
+ />
+ <FormLineLedger
+ bind:value={val_farmname}
+ basis={{
+ id: `farm_name`,
+ label: `${$lls(`common.farm_name`)}`,
+ input: {
+ placeholder: `${$lls(`icu.enter_*`, { value: `${$lls(`common.farm_name`)}`.toLowerCase() })}`,
+ },
+ }}
+ />
+ <FormLineLedger
+ bind:value={val_farmarea}
+ bind:value_label_sel={val_farmarea_unit}
+ basis={{
+ id: `farm_size`,
+ label: `${$lls(`common.farm_size`)}`,
+ label_select: {
+ label: `${$lls(`measurement.area.${val_farmarea_unit}_ab`)}`,
+ entries: area_units.map((i) => ({
+ value: i,
+ label: `${$lls(`measurement.area.${i}`)}`,
+ })),
+ },
+ input: {
+ placeholder: `${`${$lls(`icu.enter_*`, { value: `${$lls(`common.farm_size`)}`.toLowerCase() })}`} ${`${$lls(`measurement.area.${val_farmarea_unit}_pl`)}`.toLowerCase()}`,
+ field: form_fields.farm_size,
+ },
+ }}
+ />
+ <FormLineLedger
+ bind:value={val_farmcontact}
+ basis={{
+ id: `farm_contact`,
+ label: `${$lls(`common.farm_contact`)}`,
+ input: {
+ placeholder: `${$lls(`icu.enter_*`, { value: `${$lls(`common.contact_name`)}`.toLowerCase() })}`,
+ },
+ }}
+ />
+ </div>
+</CarouselItem>
diff --git a/apps-lib/src/lib/features/farm/farms-add-casli-map.svelte b/apps-lib/src/lib/features/farm/farms-add-casli-map.svelte
@@ -0,0 +1,86 @@
+<script lang="ts">
+ import {
+ CarouselItem,
+ Map,
+ MapMarkerArea,
+ WrapBorder,
+ handle_err,
+ } from "$root";
+ import {
+ type GeocoderReverseResult,
+ type GeolocationPoint,
+ type LcGeocodeCallback,
+ type LcGeocodeCurrentCallback,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let {
+ basis,
+ map_geoc = $bindable(undefined),
+ map_geop = $bindable(undefined),
+ farm_geop_lat,
+ farm_geop_lng,
+ }: {
+ basis: {
+ lc_geop_current: LcGeocodeCurrentCallback;
+ lc_geocode: LcGeocodeCallback;
+ };
+ map_geoc: GeocoderReverseResult | undefined;
+ map_geop: GeolocationPoint | undefined;
+ farm_geop_lat: string;
+ farm_geop_lng: string;
+ } = $props();
+
+ let map: maplibregl.Map | undefined = $state(undefined);
+
+ onMount(async () => {
+ try {
+ const geop = await basis.lc_geop_current();
+ if (!geop) return;
+ map_geop = { ...geop };
+ const geoc = await basis.lc_geocode(geop);
+ if (!geoc) return;
+ map_geoc = geoc;
+ if (map) map.setCenter([map_geop.lng, map_geop.lat]);
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
+ });
+</script>
+
+<CarouselItem>
+ <div
+ class={`flex flex-col h-[100vh] w-full px-6 gap-4 justify-start items-center`}
+ >
+ <WrapBorder basis={{ classes: `h-[24rem] m_1:h-[80%]` }}>
+ <Map bind:map>
+ {#if map_geop}
+ <MapMarkerArea
+ bind:map_geop
+ bind:map_geoc
+ basis={{
+ lc_geocode: basis.lc_geocode,
+ show_display: true,
+ }}
+ />
+ {/if}
+ </Map>
+ </WrapBorder>
+ {#if map_geop}
+ <div
+ class={`flex flex-col w-full gap-1 justify-center items-center`}
+ >
+ <div
+ class={`flex flex-row w-full gap-2 justify-center items-center`}
+ >
+ <p class={`font-sans font-[500] text-layer-0-glyph`}>
+ {farm_geop_lat}
+ </p>
+ <p class={`font-sans font-[500] text-layer-0-glyph`}>
+ {farm_geop_lng}
+ </p>
+ </div>
+ </div>
+ {/if}
+ </div>
+</CarouselItem>
diff --git a/apps-lib/src/lib/features/farm/farms-display-li-el.svelte b/apps-lib/src/lib/features/farm/farms-display-li-el.svelte
@@ -0,0 +1,122 @@
+<script lang="ts">
+ import { liblocale, lls, Map, MapMarkerArea } from "$root";
+ import {
+ geol_lat_fmt,
+ geol_lng_fmt,
+ lib_address_fmt,
+ parse_geom_point_tup,
+ parse_tup_geop_point,
+ type CallbackPromiseGeneric,
+ type GeolocationPointTuple,
+ type LcGeocodeCallback,
+ type ResolveFarmInfo,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let {
+ basis,
+ lc_geocode,
+ lc_handle_farm_view,
+ }: {
+ basis: ResolveFarmInfo;
+ lc_geocode: LcGeocodeCallback;
+ lc_handle_farm_view: CallbackPromiseGeneric<string>;
+ } = $props();
+
+ let map: maplibregl.Map | undefined = $state(undefined);
+ let map_center: GeolocationPointTuple = $state([0, 0]);
+
+ onMount(async () => {
+ if (map && basis.geolocation?.point) {
+ map_center = parse_geom_point_tup(basis.geolocation.point);
+ map.setCenter(map_center);
+ } else {
+ //@todo
+ }
+ });
+
+ const map_geop = $derived(parse_tup_geop_point(map_center));
+
+ const farm_addr_fmt = $derived(
+ basis.geolocation?.address
+ ? lib_address_fmt(basis.geolocation.address)
+ : ``,
+ );
+
+ const farm_geop_lat = $derived(
+ basis.geolocation?.point?.coordinates
+ ? geol_lat_fmt(
+ basis.geolocation.point.coordinates[1],
+ `dms`,
+ $liblocale,
+ 3,
+ )
+ : ``,
+ );
+
+ const farm_geop_lng = $derived(
+ basis.geolocation?.point?.coordinates
+ ? geol_lng_fmt(
+ basis.geolocation.point.coordinates[0],
+ `dms`,
+ $liblocale,
+ 3,
+ )
+ : ``,
+ );
+</script>
+
+<button
+ class={`z-10 relative flex flex-col w-full p-4 gap-3 justify-start items-center bg-layer-1-surface layer-1-active-raise-less layer-1-active-ring rounded-3xl el-re`}
+ onclick={async () => {
+ if (basis.id) await lc_handle_farm_view(basis.id);
+ }}
+>
+ <div class={`flex flex-col w-full gap-2 justify-center items-center`}>
+ <div class={`flex flex-row w-full justify-between items-center`}>
+ <p class={`font-sans font-[500] text-3xl text-layer-0-glyph`}>
+ {basis.name}
+ </p>
+
+ <div
+ class={`flex flex-row h-6 px-2 py-1 justify-center items-center bg-lime-400 rounded-lg`}
+ >
+ <p class={`font-sans font-[700] text-white`}>
+ {`${$lls(`common.farm`)}`}
+ </p>
+ </div>
+ </div>
+ <div class={`flex flex-col w-full justify-center items-center`}>
+ <div class={`flex flex-row w-full justify-start items-center`}>
+ <p class={`font-sans font-[500] text-lg text-layer-0-glyph`}>
+ {farm_addr_fmt}
+ </p>
+ </div>
+ <div class={`flex flex-row w-full justify-start items-center`}>
+ <p class={`font-sans font-[500] text-lg text-layer-0-glyph`}>
+ {farm_geop_lat && farm_geop_lng
+ ? `${farm_geop_lat}, ${farm_geop_lng}`
+ : ``}
+ </p>
+ </div>
+ </div>
+ </div>
+ <div
+ class={`flex flex-col h-[16rem] w-full justify-center items-center rounded-2xl overflow-hidden`}
+ >
+ <Map
+ bind:map
+ basis={{
+ interactive: false,
+ }}
+ >
+ <MapMarkerArea
+ {map_geop}
+ basis={{
+ no_drag: true,
+ lc_geocode,
+ }}
+ />
+ </Map>
+ </div>
+</button>
diff --git a/apps-lib/src/lib/features/farm/farms-products-review-card.svelte b/apps-lib/src/lib/features/farm/farms-products-review-card.svelte
@@ -0,0 +1,121 @@
+<script lang="ts">
+ import {
+ app_lo,
+ Glyph,
+ ImagePath,
+ lib_parse_currency_marker,
+ lls,
+ } from "$root";
+ import {
+ ascii,
+ type IViewFarmsProductsAddSubmission,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: {
+ data: IViewFarmsProductsAddSubmission | undefined;
+ };
+ } = $props();
+
+ //@todo
+</script>
+
+<div
+ class={`flex flex-col h-[20rem] w-lo_${$app_lo} justify-start items-start rounded-touch bg-layer-1-surface overflow-hidden`}
+>
+ <div class={`flex flex-row h-[10rem] w-full justify-center items-center`}>
+ {#if basis.data?.photos.length}
+ <ImagePath basis={{ path: basis.data.photos[0] }} />
+ {:else}
+ <div
+ class={`flex flex-row h-full w-full justify-center items-center bg-layer-2-surface`}
+ >
+ <div class={`flex flex-col justify-start items-center`}>
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph`,
+ dim: `sm`,
+ key: `image-broken`,
+ }}
+ />
+ <p
+ class={`font-sans font-[400] text-sm text-layer-0-glyph`}
+ >
+ {`No photo`}
+ </p>
+ </div>
+ </div>
+ {/if}
+ </div>
+ <div
+ class={`flex flex-col h-[10rem] w-full px-3 py-2 justify-start items-center`}
+ >
+ {#if basis.data}
+ <div class={`flex flex-row w-full justify-between items-center`}>
+ <div class={`flex flex-row gap-1 justify-start items-center`}>
+ <p
+ class={`font-sans font-[600] text-xl text-th-black capitalize`}
+ >
+ {basis.data.product}
+ </p>
+ </div>
+ <div
+ class={`flex flex-row gap-[2px] justify-start items-center`}
+ >
+ <p class={`font-sans font-[600] text-xl text-th-black`}>
+ {`${lib_parse_currency_marker(basis.data.price_currency)}${basis.data.price_amount}`}
+ </p>
+ <p class={`font-sans font-[600] text-xl text-th-black`}>
+ {`/`}
+ </p>
+ <p class={`font-sans font-[600] text-xl text-th-black`}>
+ {`${$lls(`measurement.mass.unit.${basis.data.price_quantity_unit}_ab`)}`}
+ </p>
+ </div>
+ </div>
+ <div class={`flex flex-row w-full justify-between items-center`}>
+ <div class={`flex flex-row gap-1 justify-start items-center`}>
+ <p
+ class={`font-sans font-[600] text-lg text-layer-1-glyph capitalize`}
+ >
+ {basis.data.process}
+ </p>
+ <p
+ class={`font-sans font-[600] text-xl text-layer-1-glyph`}
+ >
+ {ascii.bullet}
+ </p>
+ <p
+ class={`font-sans font-[600] text-lg text-layer-1-glyph`}
+ >
+ {`${basis.data.quantity_amount} ${$lls(`measurement.mass.unit.${basis.data.quantity_unit}_ab`)} ${basis.data.quantity_label}`}
+ </p>
+ </div>
+ </div>
+ <div class={`flex flex-row w-full justify-start items-center`}>
+ <p
+ class={`font-sans font-[400] text-sm text-th-black capitalize line-clamp-2 overflow-hidden text-ellipsis`}
+ >
+ {basis.data.description}
+ </p>
+ </div>
+ <div
+ class={`flex flex-row w-full pt-2 justify-between items-center`}
+ >
+ <div class={`flex flex-row gap-1 justify-start items-center`}>
+ <p class={`font-sans font-[600] text-th-black`}>
+ {`${basis.data.geolocation_address.primary}, ${basis.data.geolocation_address.admin}`}
+ </p>
+ <p class={`font-sans font-[600] text-th-black`}>
+ {ascii.bullet}
+ </p>
+ <p class={`font-sans font-[600] text-th-black`}>
+ {`${basis.data.geolocation_address.country}`}
+ </p>
+ </div>
+ </div>
+ {/if}
+ </div>
+</div>
diff --git a/apps-lib/src/lib/features/search/search-result-container.svelte b/apps-lib/src/lib/features/search/search-result-container.svelte
@@ -0,0 +1,21 @@
+<script lang="ts">
+ import type { ICb } from "@radroots/util";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis,
+ children,
+ }: {
+ basis: ICb;
+ children: Snippet;
+ } = $props();
+</script>
+
+<button
+ onclick={async () => {
+ await basis.callback();
+ }}
+ class={`group flex flex-row h-[4rem] w-full px-4 justify-between items-center bg-layer-1-surface round-24 layer-1-active-surface el-re`}
+>
+ {@render children()}
+</button>
diff --git a/apps-lib/src/lib/features/search/search-result-display.svelte b/apps-lib/src/lib/features/search/search-result-display.svelte
@@ -0,0 +1,225 @@
+<script lang="ts">
+ import { Glyph, lls, SearchResultContainer } from "$root";
+ import {
+ ascii,
+ type CallbackPromiseGeneric,
+ type ResolveGeolocationInfo,
+ type ResolveProfileInfo,
+ type SearchServiceResult,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: {
+ result: SearchServiceResult;
+ lc_handle_search_geolocation: CallbackPromiseGeneric<ResolveGeolocationInfo>;
+ lc_handle_search_profile: CallbackPromiseGeneric<ResolveProfileInfo>;
+ lc_handle_search_nostr_relay: CallbackPromiseGeneric<{
+ id: string;
+ }>;
+ };
+ } = $props();
+</script>
+
+{#if `location_gcs` in basis.result && basis.result.location_gcs.id}
+ <SearchResultContainer
+ basis={{
+ callback: async () => {
+ await basis.lc_handle_search_geolocation(
+ basis.result.location_gcs,
+ );
+ },
+ }}
+ >
+ <div class={`flex flex-row gap-4 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-stone-500 round-24`}
+ >
+ <Glyph
+ basis={{
+ classes: `text-white`,
+ dim: `xs`,
+
+ key: `compass`,
+ }}
+ />
+ </div>
+ <div class={`flex flex-row gap-1 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {`${$lls(`common.location`)}`}
+ </p>
+ </div>
+ <p
+ class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`}
+ >
+ {ascii.bullet}
+ </p>
+
+ {#if basis.result.location_gcs.kind === `farm_land`}
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-lime-500 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {`${$lls(`common.farm_land`)}`}
+ </p>
+ </div>
+ {/if}
+ </div>
+ </div>
+ <div
+ class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`}
+ >
+ {#if basis.result.location_gcs.label}
+ <p
+ class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph`}
+ >
+ {`${basis.result.location_gcs.label}`}
+ </p>
+ {:else}
+ <p
+ class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph truncate`}
+ >
+ {`${basis.result.location_gcs.gc_name}, ${basis.result.location_gcs.gc_admin1_id}`}
+ </p>
+ {/if}
+ </div>
+ </SearchResultContainer>
+{:else if `nostr_profile` in basis && basis.result.nostr_profile.id}
+ <SearchResultContainer
+ basis={{
+ callback: async () => {
+ await basis.lc_handle_search_profile(
+ basis.result.nostr_profile,
+ );
+ },
+ }}
+ >
+ <div class={`flex flex-row gap-4 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`}
+ >
+ <Glyph
+ basis={{
+ classes: `text-white`,
+ dim: `xs`,
+
+ key: `user`,
+ }}
+ />
+ </div>
+ <div class={`flex flex-row gap-1 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {`${$lls(`common.profile`)}`}
+ </p>
+ </div>
+ <p
+ class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`}
+ >
+ {ascii.bullet}
+ </p>
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-purple-400 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {#if basis.result.result_k === `name`}
+ {`${$lls(`common.name`)}`}
+ {:else}
+ {`@todo`}
+ {/if}
+ </p>
+ </div>
+ </div>
+ </div>
+ <div
+ class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`}
+ >
+ {#if basis.result.nostr_profile.name}
+ <p
+ class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`}
+ >
+ {#if basis.result.result_k === `name`}
+ {`${basis.result.nostr_profile.name}`}
+ {:else if basis.result.result_v}
+ {`${basis.result.result_v}`}
+ {:else}
+ {`@todo`}
+ {/if}
+ </p>
+ {/if}
+ </div>
+ </SearchResultContainer>
+{:else if `nostr_relay` in basis && basis.result.nostr_relay.id}
+ <SearchResultContainer
+ basis={{
+ callback: async () => {
+ await basis.lc_handle_search_nostr_relay(
+ basis.result.nostr_relay,
+ );
+ },
+ }}
+ >
+ <div class={`flex flex-row gap-4 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`}
+ >
+ <Glyph
+ basis={{
+ classes: `text-white`,
+ dim: `xs`,
+
+ key: `user`,
+ }}
+ />
+ </div>
+ <div class={`flex flex-row gap-1 justify-start items-center`}>
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {`${$lls(`common.relay`)}`}
+ </p>
+ </div>
+ <p
+ class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`}
+ >
+ {ascii.bullet}
+ </p>
+ <div
+ class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-yellow-400 rounded-md`}
+ >
+ <p
+ class={`font-sans font-[900] text-[0.7rem] text-white uppercase`}
+ >
+ {`${$lls(`common.url`)}`}
+ </p>
+ </div>
+ </div>
+ </div>
+ <div
+ class={`flex flex-row flex-grow pr-2 justify-end items-center overflow-hidden`}
+ >
+ <p
+ class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`}
+ >
+ {`${basis.result.nostr_relay.url}`}
+ </p>
+ </div>
+ </SearchResultContainer>
+{/if}
diff --git a/apps-lib/src/lib/index.ts b/apps-lib/src/lib/index.ts
@@ -1,98 +1,108 @@
-export { default as ButtonArrow } from "./component/button/button-arrow.svelte"
-export { default as ButtonLayoutPair } from "./component/button/button-layout-pair.svelte"
-export { default as ButtonLayout } from "./component/button/button-layout.svelte"
-export { default as CarouselItem } from "./component/carousel/carousel-item.svelte"
-export { default as Carousel } from "./component/carousel/carousel.svelte"
-export { default as EntryLine } from "./component/entry/entry-line.svelte"
-export { default as EntryWrap } from "./component/entry/entry-wrap.svelte"
-export { default as FloatPageButton } from "./component/float/float-page-button.svelte"
-export { default as FloatTabs } from "./component/float/float-tabs.svelte"
-export { default as GlyphButtonSimple } from "./component/glyph/glyph-button-simple.svelte"
-export { default as GlyphButton } from "./component/glyph/glyph-button.svelte"
-export { default as GlyphCircle } from "./component/glyph/glyph-circle.svelte"
-export { default as GlyphTitleSelectLabel } from "./component/glyph/glyph-title-select-label.svelte"
-export { default as LabelDisplay } from "./component/label/label-display.svelte"
-export { default as Empty } from "./component/lib/empty.svelte"
-export { default as Fade } from "./component/lib/fade.svelte"
-export { default as LoadScreen } from "./component/lib/load-screen.svelte"
-export { default as LogoCircleSm } from "./component/lib/logo-circle-sm.svelte"
-export { default as LogoCircle } from "./component/lib/logo-circle.svelte"
-export { default as SplashScreen } from "./component/lib/splash-screen.svelte"
-export { default as View } from "./component/lib/view.svelte"
-export { default as MapMarkerDot } from "./component/map/map-marker-dot.svelte"
-export { default as MapPointDisplay } from "./component/map/map-point-display.svelte"
-export { default as MapPointSelect } from "./component/map/map-point-select.svelte"
-export { default as MapPopupPointGeolocation } from "./component/map/map-popup-point-geolocation.svelte"
-export { default as NavOption } from "./component/nav/nav-option.svelte"
-export { default as Nav } from "./component/nav/nav.svelte"
-export { default as PageHeader } from "./component/page/page-header.svelte"
-export { default as PageToolbar } from "./component/page/page-toolbar.svelte"
-export { default as TrellisDefaultLabel } from "./component/trellis/trellis-default-label.svelte"
-export { default as TrellisEnd } from "./component/trellis/trellis-end.svelte"
-export { default as TrellisInput } from "./component/trellis/trellis-input.svelte"
-export { default as TrellisLine } from "./component/trellis/trellis-line.svelte"
-export { default as TrellisOffset } from "./component/trellis/trellis-offset.svelte"
-export { default as TrellisRowDisplayValue } from "./component/trellis/trellis-row-display-value.svelte"
-export { default as TrellisRowLabel } from "./component/trellis/trellis-row-label.svelte"
-export { default as TrellisSelect } from "./component/trellis/trellis-select.svelte"
-export { default as TrellisTitle } from "./component/trellis/trellis-title.svelte"
-export { default as TrellisTouch } from "./component/trellis/trellis-touch.svelte"
-export { default as Trellis } from "./component/trellis/trellis.svelte"
-export { default as Glyph } from "./el/glyph.svelte"
-export { default as ImageBlob } from "./el/image-blob.svelte"
-export { default as ImagePath } from "./el/image-path.svelte"
-export { default as Input } from "./el/input.svelte"
-export { default as LabelSwap } from "./el/label-swap.svelte"
-export { default as LoadSymbol } from "./el/load-symbol.svelte"
-export { default as SelectMenu } from "./el/select-menu.svelte"
-export { default as Select } from "./el/select.svelte"
-export { default as Styles } from "./el/styles.svelte"
-export { default as TextArea } from "./el/text-area.svelte"
-export { default as Toast } from "./el/toast.svelte"
-export { default as ImageUploadAddPhoto } from "./feature/image-upload-add-photo.svelte"
-export { default as SearchResultContainer } from "./feature/search-result-container.svelte"
-export { default as SearchResultDisplay } from "./feature/search-result-display.svelte"
-export { default as LayoutOverlayBlur } from "./layout/layout-overlay-blur.svelte"
-export { default as LayoutOverlayLoading } from "./layout/layout-overlay-loading.svelte"
-export { default as LayoutOverlaySplash } from "./layout/layout-overlay-splash.svelte"
-export { default as LayoutStyles } from "./layout/layout-styles.svelte"
-export { default as LayoutTrellis } from "./layout/layout-trellis.svelte"
-export { default as LayoutView } from "./layout/layout-view.svelte"
-export { default as LayoutWindow } from "./layout/layout-window.svelte"
-export * from "./locale/i18n"
-export * from "./service/search/lib"
-export * from "./service/search/types"
-export * from "./store/app"
-export * from "./store/client"
-export * from "./store/component"
-export * from "./store/ndk"
-export * from "./types/app"
-export * from "./types/component"
-export * from "./types/el"
-export * from "./types/feature"
-export * from "./types/interface"
-export * from "./types/util"
-export * from "./types/view"
-export * from "./util/app"
-export * from "./util/carousel"
-export * from "./util/casl"
-export * from "./util/component"
-export * from "./util/conf"
-export * from "./util/document"
-export * from "./util/error"
-export * from "./util/geolocation"
-export * from "./util/i18n"
-export * from "./util/kv"
-export * from "./util/styles"
-export * from "./util/view"
-export { default as FarmLandAdd } from "./view/farm-land-add.svelte"
-export { default as FarmLandView } from "./view/farm-land-view.svelte"
-export { default as FarmLand } from "./view/farm-land.svelte"
+export { default as ButtonHorizontalPairSlide } from "./components/button/button-horizontal-pair-slide.svelte"
+export { default as ButtonLabelDashed } from "./components/button/button-label-dashed.svelte"
+export { default as ButtonLabelGlyph } from "./components/button/button-label-glyph.svelte"
+export { default as ButtonLayoutPair } from "./components/button/button-layout-pair.svelte"
+export { default as ButtonLayout } from "./components/button/button-layout.svelte"
+export { default as ButtonRound } from "./components/button/button-round.svelte"
+export { default as ButtonSimpleGlyph } from "./components/button/button-simple-glyph.svelte"
+export { default as ButtonSimple } from "./components/button/button-simple.svelte"
+export { default as FloatPageButton } from "./components/button/float-page-button.svelte"
+export { default as GlyphButtonSimple } from "./components/button/glyph-button-simple.svelte"
+export { default as GlyphButton } from "./components/button/glyph-button.svelte"
+export { default as CarouselFlowItem } from "./components/carousel/carousel-flow-item.svelte"
+export { default as CarouselFlow } from "./components/carousel/carousel-flow.svelte"
+export { default as CarouselItem } from "./components/carousel/carousel-item.svelte"
+export { default as Carousel } from "./components/carousel/carousel.svelte"
+export { default as EnvelopeLowerFull } from "./components/envelope/envelope-lower-full.svelte"
+export { default as MapLocationSelectEnvelope } from "./components/envelope/map-location-select-envelope.svelte"
+export { default as EntryLineIdb } from "./components/form/entry-line-idb.svelte"
+export { default as EntryLine } from "./components/form/entry-line.svelte"
+export { default as EntryMultiline } from "./components/form/entry-multiline.svelte"
+export { default as EntrySelect } from "./components/form/entry-select.svelte"
+export { default as EntryWrap } from "./components/form/entry-wrap.svelte"
+export { default as FormEntryInput } from "./components/form/form-entry-input.svelte"
+export { default as FormEntryPriceQuantity } from "./components/form/form-entry-price-quantity.svelte"
+export { default as FormEntryPrice } from "./components/form/form-entry-price.svelte"
+export { default as FormEntryQuantity } from "./components/form/form-entry-quantity.svelte"
+export { default as FormEntrySelectInput } from "./components/form/form-entry-select-input.svelte"
+export { default as FormEntrySelect } from "./components/form/form-entry-select.svelte"
+export { default as FormEntryTextarea } from "./components/form/form-entry-textarea.svelte"
+export { default as FormLineLedgerLabelSelectLabel } from "./components/form/form-line-ledger-label-select-label.svelte"
+export { default as FormLineLedgerSelect } from "./components/form/form-line-ledger-select.svelte"
+export { default as FormLineLedger } from "./components/form/form-line-ledger.svelte"
+export { default as FormReviewDisplay } from "./components/form/form-review-display.svelte"
+export { default as TradeFieldDisplayKv } from "./components/form/trade_field_display_kv.svelte"
+export { default as GlyphCircle } from "./components/glyph/glyph-circle.svelte"
+export { default as Glyph } from "./components/glyph/glyph.svelte"
+export { default as LabelDisplay } from "./components/label/label-display.svelte"
+export { default as LayoutBottomButton } from "./components/layout/layout-bottom-button.svelte"
+export { default as LayoutPage } from "./components/layout/layout-page.svelte"
+export { default as LayoutTrellisLine } from "./components/layout/layout-trellis-line.svelte"
+export { default as LayoutTrellis } from "./components/layout/layout-trellis.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 Empty } from "./components/lib/empty.svelte"
+export { default as Fade } from "./components/lib/fade.svelte"
+export { default as ImageBlob } from "./components/lib/image-blob.svelte"
+export { default as ImagePath } from "./components/lib/image-path.svelte"
+export { default as InputValue } from "./components/lib/input-value.svelte"
+export { default as Input } from "./components/lib/input.svelte"
+export { default as LabelSwap } from "./components/lib/label-swap.svelte"
+export { default as LoadSymbolWhite } from "./components/lib/load-symbol-white.svelte"
+export { default as LoadSymbol } from "./components/lib/load-symbol.svelte"
+export { default as LogoCircleSm } from "./components/lib/logo-circle-sm.svelte"
+export { default as LogoLetters } from "./components/lib/logo-letters.svelte"
+export { default as LogoCircle } from "./components/lib/logo_circle.svelte"
+export { default as SelectMenu } from "./components/lib/select-menu.svelte"
+export { default as Select } from "./components/lib/select.svelte"
+export { default as TextArea } from "./components/lib/text-area.svelte"
+export { default as Theme } from "./components/lib/theme.svelte"
+export { default as Toast } from "./components/lib/toast.svelte"
+export { default as WrapBorder } from "./components/lib/wrap-border.svelte"
+export { default as MapMarkerAreaDisplay } from "./components/map/map-marker-area-display.svelte"
+export { default as MapMarkerArea } from "./components/map/map-marker-area.svelte"
+export { default as Map } from "./components/map/map.svelte"
+export { default as MarkerIndexedView } from "./components/marker/marker-indexed-view.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 TrellisDefaultLabel } from "./components/trellis/trellis-default-label.svelte"
+export { default as TrellisEnd } from "./components/trellis/trellis-end.svelte"
+export { default as TrellisInput } from "./components/trellis/trellis-input.svelte"
+export { default as TrellisLine } from "./components/trellis/trellis-line.svelte"
+export { default as TrellisOffset } from "./components/trellis/trellis-offset.svelte"
+export { default as TrellisRowDisplayValue } from "./components/trellis/trellis-row-display-value.svelte"
+export { default as TrellisRowLabel } from "./components/trellis/trellis-row-label.svelte"
+export { default as TrellisSelect } from "./components/trellis/trellis-select.svelte"
+export { default as TrellisTitle } from "./components/trellis/trellis-title.svelte"
+export { default as TrellisTouch } from "./components/trellis/trellis-touch.svelte"
+export { default as Trellis } from "./components/trellis/trellis.svelte"
+export { default as ImageUploadAddPhoto } from "./components/upload/image-upload-add-photo.svelte"
+export { default as ImageUploadButtonsAspect } from "./components/upload/image-upload-buttons-aspect.svelte"
+export { default as ImageUploadSimple } from "./components/upload/image-upload-simple.svelte"
+export { default as FarmsAddCasliDetail } from "./features/farm/farms-add-casli-detail.svelte"
+export { default as FarmsAddCasliMap } from "./features/farm/farms-add-casli-map.svelte"
+export { default as FarmsDisplayLiEl } from "./features/farm/farms-display-li-el.svelte"
+export { default as FarmsProductsReviewCard } from "./features/farm/farms-products-review-card.svelte"
+export { default as SearchResultContainer } from "./features/search/search-result-container.svelte"
+export { default as SearchResultDisplay } from "./features/search/search-result-display.svelte"
+export * from "./locale/i18n.js"
+export * from "./store/app.js"
+export * from "./store/client.js"
+export * from "./store/component.js"
+export * from "./store/ndk.js"
+export * from "./util/carousel.js"
+export * from "./util/casl.js"
+export * from "./util/idb.js"
+export * from "./util/lib.js"
+export { default as FarmsAdd } from "./view/farms-add.svelte"
+export { default as FarmsProductsAdd } from "./view/farms-products-add.svelte"
+export { default as FarmsProductsView } from "./view/farms-products-view.svelte"
+export { default as FarmsView } from "./view/farms-view.svelte"
+export { default as Farms } from "./view/farms.svelte"
export { default as Home } from "./view/home.svelte"
export { default as Notifications } from "./view/notifications.svelte"
+export { default as Profile } from "./view/profile.svelte"
export { default as Search } from "./view/search.svelte"
-export { default as SettingsNostr } from "./view/settings-nostr.svelte"
-export { default as SettingsProfileEdit } from "./view/settings-profile-edit.svelte"
-export { default as SettingsProfile } from "./view/settings-profile.svelte"
export { default as Settings } from "./view/settings.svelte"
-
diff --git a/apps-lib/src/lib/layout/layout-overlay-blur.svelte b/apps-lib/src/lib/layout/layout-overlay-blur.svelte
@@ -1,13 +0,0 @@
-<script lang="ts">
- import { app_blur, Empty } from "$lib";
-</script>
-
-<div
- class={`z-10 absolute top-0 left-0 modal modal-bottom ${$app_blur ? `modal-open` : ``} h-[100vh] w-full m-0 p-0 backdrop-blur-sm overflow-y-hidden el-re`}
->
- <div
- class={`modal-box h-full w-full m-0 p-0 bg-transparent overflow-hidden el-re`}
- >
- <Empty></Empty>
- </div>
-</div>
diff --git a/apps-lib/src/lib/layout/layout-overlay-loading.svelte b/apps-lib/src/lib/layout/layout-overlay-loading.svelte
@@ -1,7 +0,0 @@
-<script lang="ts">
- import { app_loading, LoadScreen } from "$lib";
-</script>
-
-{#if $app_loading}
- <LoadScreen />
-{/if}
diff --git a/apps-lib/src/lib/layout/layout-overlay-splash.svelte b/apps-lib/src/lib/layout/layout-overlay-splash.svelte
@@ -1,7 +0,0 @@
-<script lang="ts">
- import { app_splash, SplashScreen } from "$lib";
-</script>
-
-{#if $app_splash}
- <SplashScreen />
-{/if}
diff --git a/apps-lib/src/lib/layout/layout-styles.svelte b/apps-lib/src/lib/layout/layout-styles.svelte
@@ -1 +0,0 @@
-<div class="hidden -bottom-lo_view_mobile_base -bottom-lo_view_mobile_y -bottom-nav_mobile_base -bottom-nav_mobile_y -bottom-tabs_mobile_base -bottom-tabs_mobile_y -bottom-trellis_centered_mobile_base -bottom-trellis_centered_mobile_y -bottom-view_mobile_base -bottom-view_mobile_y -bottom-view_offset_mobile_base -bottom-view_offset_mobile_y -top-lo_view_mobile_base -top-lo_view_mobile_y -top-nav_mobile_base -top-nav_mobile_y -top-tabs_mobile_base -top-tabs_mobile_y -top-trellis_centered_mobile_base -top-trellis_centered_mobile_y -top-view_mobile_base -top-view_mobile_y -top-view_offset_mobile_base -top-view_offset_mobile_y -translate-y-h_lo_view_mobile_base -translate-y-h_lo_view_mobile_y -translate-y-h_nav_mobile_base -translate-y-h_nav_mobile_y -translate-y-h_tabs_mobile_base -translate-y-h_tabs_mobile_y -translate-y-h_trellis_centered_mobile_base -translate-y-h_trellis_centered_mobile_y -translate-y-h_view_mobile_base -translate-y-h_view_mobile_y -translate-y-h_view_offset_mobile_base -translate-y-h_view_offset_mobile_y active:bg-layer_0_glyph active:bg-layer_0_glyph_a active:bg-layer_0_glyph_hl active:bg-layer_0_glyph_hl_a active:bg-layer_0_glyph_label active:bg-layer_0_glyph_pl active:bg-layer_0_glyph_shade active:bg-layer_0_surface active:bg-layer_0_surface_a active:bg-layer_0_surface_blur active:bg-layer_0_surface_edge active:bg-layer_0_surface_w active:bg-layer_1_glyph active:bg-layer_1_glyph_a active:bg-layer_1_glyph_d active:bg-layer_1_glyph_hl active:bg-layer_1_glyph_hl_a active:bg-layer_1_glyph_label active:bg-layer_1_glyph_pl active:bg-layer_1_glyph_shade active:bg-layer_1_surface active:bg-layer_1_surface_a active:bg-layer_1_surface_edge active:bg-layer_1_surface_err active:bg-layer_1_surface_focus active:bg-layer_2_glyph active:bg-layer_2_glyph_a active:bg-layer_2_glyph_d active:bg-layer_2_glyph_hl active:bg-layer_2_glyph_hl_a active:bg-layer_2_glyph_pl active:bg-layer_2_glyph_shade active:bg-layer_2_surface active:bg-layer_2_surface_a active:bg-layer_2_surface_edge active:border-layer_0_glyph active:border-layer_0_glyph_a active:border-layer_0_glyph_hl active:border-layer_0_glyph_hl_a active:border-layer_0_glyph_label active:border-layer_0_glyph_pl active:border-layer_0_glyph_shade active:border-layer_0_surface active:border-layer_0_surface_a active:border-layer_0_surface_blur active:border-layer_0_surface_edge active:border-layer_0_surface_w active:border-layer_1_glyph active:border-layer_1_glyph_a active:border-layer_1_glyph_d active:border-layer_1_glyph_hl active:border-layer_1_glyph_hl_a active:border-layer_1_glyph_label active:border-layer_1_glyph_pl active:border-layer_1_glyph_shade active:border-layer_1_surface active:border-layer_1_surface_a active:border-layer_1_surface_edge active:border-layer_1_surface_err active:border-layer_1_surface_focus active:border-layer_2_glyph active:border-layer_2_glyph_a active:border-layer_2_glyph_d active:border-layer_2_glyph_hl active:border-layer_2_glyph_hl_a active:border-layer_2_glyph_pl active:border-layer_2_glyph_shade active:border-layer_2_surface active:border-layer_2_surface_a active:border-layer_2_surface_edge active:text-layer_0_glyph active:text-layer_0_glyph_a active:text-layer_0_glyph_hl active:text-layer_0_glyph_hl_a active:text-layer_0_glyph_label active:text-layer_0_glyph_pl active:text-layer_0_glyph_shade active:text-layer_0_surface active:text-layer_0_surface_a active:text-layer_0_surface_blur active:text-layer_0_surface_edge active:text-layer_0_surface_w active:text-layer_1_glyph active:text-layer_1_glyph_a active:text-layer_1_glyph_d active:text-layer_1_glyph_hl active:text-layer_1_glyph_hl_a active:text-layer_1_glyph_label active:text-layer_1_glyph_pl active:text-layer_1_glyph_shade active:text-layer_1_surface active:text-layer_1_surface_a active:text-layer_1_surface_edge active:text-layer_1_surface_err active:text-layer_1_surface_focus active:text-layer_2_glyph active:text-layer_2_glyph_a active:text-layer_2_glyph_d active:text-layer_2_glyph_hl active:text-layer_2_glyph_hl_a active:text-layer_2_glyph_pl active:text-layer_2_glyph_shade active:text-layer_2_surface active:text-layer_2_surface_a active:text-layer_2_surface_edge bg-layer_0_glyph bg-layer_0_glyph_a bg-layer_0_glyph_hl bg-layer_0_glyph_hl_a bg-layer_0_glyph_label bg-layer_0_glyph_pl bg-layer_0_glyph_shade bg-layer_0_surface bg-layer_0_surface_a bg-layer_0_surface_blur bg-layer_0_surface_edge bg-layer_0_surface_w bg-layer_1_glyph bg-layer_1_glyph_a bg-layer_1_glyph_d bg-layer_1_glyph_hl bg-layer_1_glyph_hl_a bg-layer_1_glyph_label bg-layer_1_glyph_pl bg-layer_1_glyph_shade bg-layer_1_surface bg-layer_1_surface_a bg-layer_1_surface_edge bg-layer_1_surface_err bg-layer_1_surface_focus bg-layer_2_glyph bg-layer_2_glyph_a bg-layer_2_glyph_d bg-layer_2_glyph_hl bg-layer_2_glyph_hl_a bg-layer_2_glyph_pl bg-layer_2_glyph_shade bg-layer_2_surface bg-layer_2_surface_a bg-layer_2_surface_edge border-layer_0_glyph border-layer_0_glyph_a border-layer_0_glyph_hl border-layer_0_glyph_hl_a border-layer_0_glyph_label border-layer_0_glyph_pl border-layer_0_glyph_shade border-layer_0_surface border-layer_0_surface_a border-layer_0_surface_blur border-layer_0_surface_edge border-layer_0_surface_w border-layer_1_glyph border-layer_1_glyph_a border-layer_1_glyph_d border-layer_1_glyph_hl border-layer_1_glyph_hl_a border-layer_1_glyph_label border-layer_1_glyph_pl border-layer_1_glyph_shade border-layer_1_surface border-layer_1_surface_a border-layer_1_surface_edge border-layer_1_surface_err border-layer_1_surface_focus border-layer_2_glyph border-layer_2_glyph_a border-layer_2_glyph_d border-layer_2_glyph_hl border-layer_2_glyph_hl_a border-layer_2_glyph_pl border-layer_2_glyph_shade border-layer_2_surface border-layer_2_surface_a border-layer_2_surface_edge bottom-lo_view_mobile_base bottom-lo_view_mobile_y bottom-nav_mobile_base bottom-nav_mobile_y bottom-tabs_mobile_base bottom-tabs_mobile_y bottom-trellis_centered_mobile_base bottom-trellis_centered_mobile_y bottom-view_mobile_base bottom-view_mobile_y bottom-view_offset_mobile_base bottom-view_offset_mobile_y focus:bg-layer_0_glyph focus:bg-layer_0_glyph_a focus:bg-layer_0_glyph_hl focus:bg-layer_0_glyph_hl_a focus:bg-layer_0_glyph_label focus:bg-layer_0_glyph_pl focus:bg-layer_0_glyph_shade focus:bg-layer_0_surface focus:bg-layer_0_surface_a focus:bg-layer_0_surface_blur focus:bg-layer_0_surface_edge focus:bg-layer_0_surface_w focus:bg-layer_1_glyph focus:bg-layer_1_glyph_a focus:bg-layer_1_glyph_d focus:bg-layer_1_glyph_hl focus:bg-layer_1_glyph_hl_a focus:bg-layer_1_glyph_label focus:bg-layer_1_glyph_pl focus:bg-layer_1_glyph_shade focus:bg-layer_1_surface focus:bg-layer_1_surface_a focus:bg-layer_1_surface_edge focus:bg-layer_1_surface_err focus:bg-layer_1_surface_focus focus:bg-layer_2_glyph focus:bg-layer_2_glyph_a focus:bg-layer_2_glyph_d focus:bg-layer_2_glyph_hl focus:bg-layer_2_glyph_hl_a focus:bg-layer_2_glyph_pl focus:bg-layer_2_glyph_shade focus:bg-layer_2_surface focus:bg-layer_2_surface_a focus:bg-layer_2_surface_edge focus:border-layer_0_glyph focus:border-layer_0_glyph_a focus:border-layer_0_glyph_hl focus:border-layer_0_glyph_hl_a focus:border-layer_0_glyph_label focus:border-layer_0_glyph_pl focus:border-layer_0_glyph_shade focus:border-layer_0_surface focus:border-layer_0_surface_a focus:border-layer_0_surface_blur focus:border-layer_0_surface_edge focus:border-layer_0_surface_w focus:border-layer_1_glyph focus:border-layer_1_glyph_a focus:border-layer_1_glyph_d focus:border-layer_1_glyph_hl focus:border-layer_1_glyph_hl_a focus:border-layer_1_glyph_label focus:border-layer_1_glyph_pl focus:border-layer_1_glyph_shade focus:border-layer_1_surface focus:border-layer_1_surface_a focus:border-layer_1_surface_edge focus:border-layer_1_surface_err focus:border-layer_1_surface_focus focus:border-layer_2_glyph focus:border-layer_2_glyph_a focus:border-layer_2_glyph_d focus:border-layer_2_glyph_hl focus:border-layer_2_glyph_hl_a focus:border-layer_2_glyph_pl focus:border-layer_2_glyph_shade focus:border-layer_2_surface focus:border-layer_2_surface_a focus:border-layer_2_surface_edge focus:text-layer_0_glyph focus:text-layer_0_glyph_a focus:text-layer_0_glyph_hl focus:text-layer_0_glyph_hl_a focus:text-layer_0_glyph_label focus:text-layer_0_glyph_pl focus:text-layer_0_glyph_shade focus:text-layer_0_surface focus:text-layer_0_surface_a focus:text-layer_0_surface_blur focus:text-layer_0_surface_edge focus:text-layer_0_surface_w focus:text-layer_1_glyph focus:text-layer_1_glyph_a focus:text-layer_1_glyph_d focus:text-layer_1_glyph_hl focus:text-layer_1_glyph_hl_a focus:text-layer_1_glyph_label focus:text-layer_1_glyph_pl focus:text-layer_1_glyph_shade focus:text-layer_1_surface focus:text-layer_1_surface_a focus:text-layer_1_surface_edge focus:text-layer_1_surface_err focus:text-layer_1_surface_focus focus:text-layer_2_glyph focus:text-layer_2_glyph_a focus:text-layer_2_glyph_d focus:text-layer_2_glyph_hl focus:text-layer_2_glyph_hl_a focus:text-layer_2_glyph_pl focus:text-layer_2_glyph_shade focus:text-layer_2_surface focus:text-layer_2_surface_a focus:text-layer_2_surface_edge group-active:bg-layer_0_glyph group-active:bg-layer_0_glyph_a group-active:bg-layer_0_glyph_hl group-active:bg-layer_0_glyph_hl_a group-active:bg-layer_0_glyph_label group-active:bg-layer_0_glyph_pl group-active:bg-layer_0_glyph_shade group-active:bg-layer_0_surface group-active:bg-layer_0_surface_a group-active:bg-layer_0_surface_blur group-active:bg-layer_0_surface_edge group-active:bg-layer_0_surface_w group-active:bg-layer_1_glyph group-active:bg-layer_1_glyph_a group-active:bg-layer_1_glyph_d group-active:bg-layer_1_glyph_hl group-active:bg-layer_1_glyph_hl_a group-active:bg-layer_1_glyph_label group-active:bg-layer_1_glyph_pl group-active:bg-layer_1_glyph_shade group-active:bg-layer_1_surface group-active:bg-layer_1_surface_a group-active:bg-layer_1_surface_edge group-active:bg-layer_1_surface_err group-active:bg-layer_1_surface_focus group-active:bg-layer_2_glyph group-active:bg-layer_2_glyph_a group-active:bg-layer_2_glyph_d group-active:bg-layer_2_glyph_hl group-active:bg-layer_2_glyph_hl_a group-active:bg-layer_2_glyph_pl group-active:bg-layer_2_glyph_shade group-active:bg-layer_2_surface group-active:bg-layer_2_surface_a group-active:bg-layer_2_surface_edge group-active:border-layer_0_glyph group-active:border-layer_0_glyph_a group-active:border-layer_0_glyph_hl group-active:border-layer_0_glyph_hl_a group-active:border-layer_0_glyph_label group-active:border-layer_0_glyph_pl group-active:border-layer_0_glyph_shade group-active:border-layer_0_surface group-active:border-layer_0_surface_a group-active:border-layer_0_surface_blur group-active:border-layer_0_surface_edge group-active:border-layer_0_surface_w group-active:border-layer_1_glyph group-active:border-layer_1_glyph_a group-active:border-layer_1_glyph_d group-active:border-layer_1_glyph_hl group-active:border-layer_1_glyph_hl_a group-active:border-layer_1_glyph_label group-active:border-layer_1_glyph_pl group-active:border-layer_1_glyph_shade group-active:border-layer_1_surface group-active:border-layer_1_surface_a group-active:border-layer_1_surface_edge group-active:border-layer_1_surface_err group-active:border-layer_1_surface_focus group-active:border-layer_2_glyph group-active:border-layer_2_glyph_a group-active:border-layer_2_glyph_d group-active:border-layer_2_glyph_hl group-active:border-layer_2_glyph_hl_a group-active:border-layer_2_glyph_pl group-active:border-layer_2_glyph_shade group-active:border-layer_2_surface group-active:border-layer_2_surface_a group-active:border-layer_2_surface_edge group-active:text-layer_0_glyph group-active:text-layer_0_glyph_a group-active:text-layer_0_glyph_hl group-active:text-layer_0_glyph_hl_a group-active:text-layer_0_glyph_label group-active:text-layer_0_glyph_pl group-active:text-layer_0_glyph_shade group-active:text-layer_0_surface group-active:text-layer_0_surface_a group-active:text-layer_0_surface_blur group-active:text-layer_0_surface_edge group-active:text-layer_0_surface_w group-active:text-layer_1_glyph group-active:text-layer_1_glyph_a group-active:text-layer_1_glyph_d group-active:text-layer_1_glyph_hl group-active:text-layer_1_glyph_hl_a group-active:text-layer_1_glyph_label group-active:text-layer_1_glyph_pl group-active:text-layer_1_glyph_shade group-active:text-layer_1_surface group-active:text-layer_1_surface_a group-active:text-layer_1_surface_edge group-active:text-layer_1_surface_err group-active:text-layer_1_surface_focus group-active:text-layer_2_glyph group-active:text-layer_2_glyph_a group-active:text-layer_2_glyph_d group-active:text-layer_2_glyph_hl group-active:text-layer_2_glyph_hl_a group-active:text-layer_2_glyph_pl group-active:text-layer_2_glyph_shade group-active:text-layer_2_surface group-active:text-layer_2_surface_a group-active:text-layer_2_surface_edge group-focus:bg-layer_0_glyph group-focus:bg-layer_0_glyph_a group-focus:bg-layer_0_glyph_hl group-focus:bg-layer_0_glyph_hl_a group-focus:bg-layer_0_glyph_label group-focus:bg-layer_0_glyph_pl group-focus:bg-layer_0_glyph_shade group-focus:bg-layer_0_surface group-focus:bg-layer_0_surface_a group-focus:bg-layer_0_surface_blur group-focus:bg-layer_0_surface_edge group-focus:bg-layer_0_surface_w group-focus:bg-layer_1_glyph group-focus:bg-layer_1_glyph_a group-focus:bg-layer_1_glyph_d group-focus:bg-layer_1_glyph_hl group-focus:bg-layer_1_glyph_hl_a group-focus:bg-layer_1_glyph_label group-focus:bg-layer_1_glyph_pl group-focus:bg-layer_1_glyph_shade group-focus:bg-layer_1_surface group-focus:bg-layer_1_surface_a group-focus:bg-layer_1_surface_edge group-focus:bg-layer_1_surface_err group-focus:bg-layer_1_surface_focus group-focus:bg-layer_2_glyph group-focus:bg-layer_2_glyph_a group-focus:bg-layer_2_glyph_d group-focus:bg-layer_2_glyph_hl group-focus:bg-layer_2_glyph_hl_a group-focus:bg-layer_2_glyph_pl group-focus:bg-layer_2_glyph_shade group-focus:bg-layer_2_surface group-focus:bg-layer_2_surface_a group-focus:bg-layer_2_surface_edge group-focus:border-layer_0_glyph group-focus:border-layer_0_glyph_a group-focus:border-layer_0_glyph_hl group-focus:border-layer_0_glyph_hl_a group-focus:border-layer_0_glyph_label group-focus:border-layer_0_glyph_pl group-focus:border-layer_0_glyph_shade group-focus:border-layer_0_surface group-focus:border-layer_0_surface_a group-focus:border-layer_0_surface_blur group-focus:border-layer_0_surface_edge group-focus:border-layer_0_surface_w group-focus:border-layer_1_glyph group-focus:border-layer_1_glyph_a group-focus:border-layer_1_glyph_d group-focus:border-layer_1_glyph_hl group-focus:border-layer_1_glyph_hl_a group-focus:border-layer_1_glyph_label group-focus:border-layer_1_glyph_pl group-focus:border-layer_1_glyph_shade group-focus:border-layer_1_surface group-focus:border-layer_1_surface_a group-focus:border-layer_1_surface_edge group-focus:border-layer_1_surface_err group-focus:border-layer_1_surface_focus group-focus:border-layer_2_glyph group-focus:border-layer_2_glyph_a group-focus:border-layer_2_glyph_d group-focus:border-layer_2_glyph_hl group-focus:border-layer_2_glyph_hl_a group-focus:border-layer_2_glyph_pl group-focus:border-layer_2_glyph_shade group-focus:border-layer_2_surface group-focus:border-layer_2_surface_a group-focus:border-layer_2_surface_edge group-focus:text-layer_0_glyph group-focus:text-layer_0_glyph_a group-focus:text-layer_0_glyph_hl group-focus:text-layer_0_glyph_hl_a group-focus:text-layer_0_glyph_label group-focus:text-layer_0_glyph_pl group-focus:text-layer_0_glyph_shade group-focus:text-layer_0_surface group-focus:text-layer_0_surface_a group-focus:text-layer_0_surface_blur group-focus:text-layer_0_surface_edge group-focus:text-layer_0_surface_w group-focus:text-layer_1_glyph group-focus:text-layer_1_glyph_a group-focus:text-layer_1_glyph_d group-focus:text-layer_1_glyph_hl group-focus:text-layer_1_glyph_hl_a group-focus:text-layer_1_glyph_label group-focus:text-layer_1_glyph_pl group-focus:text-layer_1_glyph_shade group-focus:text-layer_1_surface group-focus:text-layer_1_surface_a group-focus:text-layer_1_surface_edge group-focus:text-layer_1_surface_err group-focus:text-layer_1_surface_focus group-focus:text-layer_2_glyph group-focus:text-layer_2_glyph_a group-focus:text-layer_2_glyph_d group-focus:text-layer_2_glyph_hl group-focus:text-layer_2_glyph_hl_a group-focus:text-layer_2_glyph_pl group-focus:text-layer_2_glyph_shade group-focus:text-layer_2_surface group-focus:text-layer_2_surface_a group-focus:text-layer_2_surface_edge h-[12px] h-[16px] h-[17px] h-[18px] h-[20px] h-[22px] h-[24px] h-[28px] h-[36px] h-entry_bold h-entry_guide h-entry_line h-envelope_button h-envelope_top h-line h-line_label h-lo_view_mobile_base h-lo_view_mobile_y h-nav_mobile_base h-nav_mobile_y h-tabs_mobile_base h-tabs_mobile_y h-toast_min h-touch_bold h-touch_guide h-trellis_centered_mobile_base h-trellis_centered_mobile_y h-view_mobile_base h-view_mobile_y h-view_offset_mobile_base h-view_offset_mobile_y pb-h_lo_view_mobile_base pb-h_lo_view_mobile_y pb-h_nav_mobile_base pb-h_nav_mobile_y pb-h_tabs_mobile_base pb-h_tabs_mobile_y pb-h_trellis_centered_mobile_base pb-h_trellis_centered_mobile_y pb-h_view_mobile_base pb-h_view_mobile_y pb-h_view_offset_mobile_base pb-h_view_offset_mobile_y pt-h_lo_view_mobile_base pt-h_lo_view_mobile_y pt-h_nav_mobile_base pt-h_nav_mobile_y pt-h_tabs_mobile_base pt-h_tabs_mobile_y pt-h_trellis_centered_mobile_base pt-h_trellis_centered_mobile_y pt-h_view_mobile_base pt-h_view_mobile_y pt-h_view_offset_mobile_base pt-h_view_offset_mobile_y text-[12px] text-[15px] text-[16px] text-[18px] text-[19px] text-[20px] text-[21px] text-[23px] text-[24px] text-[26px] text-[27px] text-[28px] text-[30px] text-[36px] text-[40px] text-layer_0_glyph text-layer_0_glyph_a text-layer_0_glyph_hl text-layer_0_glyph_hl_a text-layer_0_glyph_label text-layer_0_glyph_pl text-layer_0_glyph_shade text-layer_0_surface text-layer_0_surface_a text-layer_0_surface_blur text-layer_0_surface_edge text-layer_0_surface_w text-layer_1_glyph text-layer_1_glyph_a text-layer_1_glyph_d text-layer_1_glyph_hl text-layer_1_glyph_hl_a text-layer_1_glyph_label text-layer_1_glyph_pl text-layer_1_glyph_shade text-layer_1_surface text-layer_1_surface_a text-layer_1_surface_edge text-layer_1_surface_err text-layer_1_surface_focus text-layer_2_glyph text-layer_2_glyph_a text-layer_2_glyph_d text-layer_2_glyph_hl text-layer_2_glyph_hl_a text-layer_2_glyph_pl text-layer_2_glyph_shade text-layer_2_surface text-layer_2_surface_a text-layer_2_surface_edge top-lo_view_mobile_base top-lo_view_mobile_y top-nav_mobile_base top-nav_mobile_y top-tabs_mobile_base top-tabs_mobile_y top-trellis_centered_mobile_base top-trellis_centered_mobile_y top-view_mobile_base top-view_mobile_y top-view_offset_mobile_base top-view_offset_mobile_y translate-y-h_lo_view_mobile_base translate-y-h_lo_view_mobile_y translate-y-h_nav_mobile_base translate-y-h_nav_mobile_y translate-y-h_tabs_mobile_base translate-y-h_tabs_mobile_y translate-y-h_trellis_centered_mobile_base translate-y-h_trellis_centered_mobile_y translate-y-h_view_mobile_base translate-y-h_view_mobile_y translate-y-h_view_offset_mobile_base translate-y-h_view_offset_mobile_y w-[12px] w-[16px] w-[17px] w-[18px] w-[20px] w-[22px] w-[24px] w-[28px] w-[36px] w-mobile_base w-mobile_y"></div>
-\ No newline at end of file
diff --git a/apps-lib/src/lib/layout/layout-trellis.svelte b/apps-lib/src/lib/layout/layout-trellis.svelte
@@ -1,11 +0,0 @@
-<script lang="ts">
- import { fmt_cl, type IBasisOpt, type IClOpt } from "$lib";
-
- export let basis: IBasisOpt<IClOpt> = undefined;
-</script>
-
-<div
- class={`${fmt_cl(basis?.classes)} flex flex-col pb-12 gap-4 justify-center items-center scroll-hide`}
->
- <slot />
-</div>
diff --git a/apps-lib/src/lib/layout/layout-view.svelte b/apps-lib/src/lib/layout/layout-view.svelte
@@ -1,55 +0,0 @@
-<script lang="ts">
- import {
- app_layout,
- fmt_cl,
- layout_view_cover,
- nav_blur,
- ph_blur,
- tabs_blur,
- type IBasisOpt,
- type IClOpt,
- } from "$lib";
- import { onDestroy, onMount } from "svelte";
-
- export let basis: IBasisOpt<IClOpt & { fade?: boolean }> = undefined;
- let el: HTMLElement | null;
-
- /*$: classes_resp = $layout_view_cover
- ? ``
- : $nav_visible
- ? `pt-h_nav_${$app_layout}`
- : `pt-h_lo_view_${$app_layout}`;
- */
- 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 overflow-y-scroll scroll-hide ${$layout_view_cover ? `` : `pt-h_lo_view_${$app_layout}`}`}
- class:fade-in={basis?.fade}
->
- <slot />
-</div>
diff --git a/apps-lib/src/lib/layout/layout-window.svelte b/apps-lib/src/lib/layout/layout-window.svelte
@@ -1,52 +0,0 @@
-<script>
- import {
- app_layout,
- app_tilt,
- app_toast,
- app_win,
- cfg_app,
- envelope_tilt,
- envelope_visible,
- LayoutOverlayBlur,
- LayoutOverlayLoading,
- LayoutOverlaySplash,
- LayoutStyles,
- Toast,
- } from "$lib";
- import { onMount } from "svelte";
-
- onMount(async () => {
- try {
- app_win.set({ h: window.innerHeight, w: window.innerWidth });
- app_toast.set(false);
- } catch (e) {
- console.log(`(layout mount) `, e);
- } finally {
- }
- });
-
- app_win.subscribe((_app_win) => {
- if (_app_win.h > cfg_app.layout.mobile_y.h) app_layout.set(`mobile_y`);
- });
-
- envelope_visible.subscribe(async (_envelope_visible) => {
- if (_envelope_visible && $envelope_tilt) app_tilt.set(true);
- else app_tilt.set(false);
- });
-</script>
-
-<div
- class={`relative flex flex-col h-[100vh] w-full overflow-x-hidden overflow-y-hidden bg-layer-0-surface ${$app_tilt ? `scale-y-[96%] translate-y-4 rounded-t-[3rem]` : ``} delay-75 duration-200 el-re`}
->
- <div class={`flex flex-col h-full w-full`}>
- <slot />
- </div>
- <slot name="overlay" />
- <LayoutOverlayBlur />
-</div>
-<LayoutOverlayLoading />
-<LayoutOverlaySplash />
-<LayoutStyles />
-{#if $app_toast}
- <Toast basis={$app_toast} />
-{/if}
diff --git a/apps-lib/src/lib/locale/en/common.json b/apps-lib/src/lib/locale/en/common.json
@@ -1,4 +1,14 @@
{
+ "farm_name": "Farm name",
+ "farm_contact": "Farm contact",
+ "farm_location": "Farm location",
+ "farm_size": "Farm size",
+ "contact_name": "Contact name",
+ "describe_your_product": "Describe your product",
+ "add_from_existing": "Add from existing",
+ "create_product": "Create product",
+ "logout": "Logout",
+ "farms": "Farms",
"loading": "Loading",
"disable_uploads": "Disable uploads",
"this_action_is_irreversible": "This action is irreversible",
diff --git a/apps-lib/src/lib/locale/en/farm.json b/apps-lib/src/lib/locale/en/farm.json
@@ -0,0 +1,15 @@
+{
+ "product": {
+ "validation": {
+ "key": {
+ "required": "Enter the product"
+ },
+ "process": {
+ "required": "Enter the product process"
+ },
+ "summary": {
+ "required": "Enter the product description"
+ }
+ }
+ }
+}
+\ No newline at end of file
diff --git a/apps-lib/src/lib/locale/en/measurement.json b/apps-lib/src/lib/locale/en/measurement.json
@@ -1,11 +1,17 @@
{
"area": {
"ac": "Acre",
+ "ac_pl": "Acres",
"ac_ab": "Ac.",
"ha": "Hectare",
+ "ha_pl": "Hectares",
"ha_ab": "Ha.",
"m2": "Square metre",
- "m2_ab": "Sq. M."
+ "m2_pl": "Square metres",
+ "m2_ab": "Sq. M.",
+ "ft2": "Square foot",
+ "ft2_pl": "Square feet",
+ "ft2_ab": "Sq. Ft."
},
"length": {
"ft": "Foot",
diff --git a/apps-lib/src/lib/locale/en/trade.json b/apps-lib/src/lib/locale/en/trade.json
@@ -10,6 +10,13 @@
"err_invalid": "Enter the price"
}
},
+ "default": {
+ "process": {
+ "natural": "Natural",
+ "dried": "Dried",
+ "roasted": "Roasted"
+ }
+ },
"key": {
"cacao": {
"name": "Cacao",
diff --git a/apps-lib/src/lib/locale/i18n.ts b/apps-lib/src/lib/locale/i18n.ts
@@ -19,7 +19,7 @@ const config: Config<LanguageConfig> = {
translations: libtranslations,
parser: parser(),
loaders: [
- ...Object.keys(libtranslations).map((locale) => [`common`, `icu`, `measurement`, `model`, `trade`].map(key => ({
+ ...Object.keys(libtranslations).map((locale) => [`common`, `farm`, `icu`, `measurement`, `model`, `trade`].map(key => ({
locale,
key,
loader: async () => (await import(`./${locale}/${key}.json`)).default
diff --git a/apps-lib/src/lib/service/search/lib.ts b/apps-lib/src/lib/service/search/lib.ts
@@ -1,67 +0,0 @@
-import { type ISearchService, type SearchServiceFlattenedData, type SearchServiceResult } from "$lib";
-
-export class SearchService implements ISearchService {
- private _flattened_data: SearchServiceFlattenedData[] = [];
- private _index_map: Map<string, Record<string, any>[]> = new Map();
-
- constructor(data: Record<string, any[]>) {
- this.flatten_data(data);
- this.index_data();
- }
-
- private get flattened_data() {
- return this._flattened_data;
- }
-
- private flatten_data(data: Record<string, any[]>) {
- Object.keys(data).forEach((list_group) => {
- const list = data[list_group];
- list.forEach((item) => {
- const flattened_item: SearchServiceFlattenedData = { ...item, group: list_group };
- this._flattened_data.push(flattened_item);
- });
- });
- }
-
- private index_data(): void {
- this.flattened_data.forEach((item) => {
- Object.keys(item).forEach((key) => {
- const key_lower = key.toLowerCase();
- const value = item[key];
- if (value != null) {
- if (!this._index_map.has(key_lower)) this._index_map.set(key_lower, []);
- this._index_map.get(key_lower)?.push(item);
- }
- });
- });
- }
-
- public search(query: string): SearchServiceResult[] {
- if (!query) return [];
- const search_query = query.toLowerCase().trim();
- let results: SearchServiceResult[] = [];
- const results_seen = new Set<string>();
- this._index_map.forEach((items) => {
- items.forEach((item) => {
- for (const [key, value] of Object.entries(item)) {
- if (key === `id` || key === `created_at` || key === `updated_at` || key === `public_key` || key === `group`) continue;
- if (value && value.toString().replace(/[()_-]/gi, ` `).toLowerCase().includes(search_query)) {
- const { group, ...rest } = item;
- if (!(`id` in item)) continue;
- const result_key = item.id;
- if (results_seen.has(result_key)) continue;
- results_seen.add(result_key);
- const reshaped_result: SearchServiceResult = {
- id: item.id,
- result_k: key,
- result_v: value,
- [group]: { ...rest },
- };
- results.push(reshaped_result);
- }
- };
- });
- });
- return results;
- }
-}
diff --git a/apps-lib/src/lib/service/search/types.ts b/apps-lib/src/lib/service/search/types.ts
@@ -1,6 +0,0 @@
-export type SearchServiceResult = Record<string, any> & { id: string, result_k: string; result_v: string; };
-export type SearchServiceFlattenedData = Record<string, any> & { list: string; };
-
-export type ISearchService = {
- search(input: string): SearchServiceResult[]
-};
-\ No newline at end of file
diff --git a/apps-lib/src/lib/store/app.ts b/apps-lib/src/lib/store/app.ts
@@ -1,8 +1,8 @@
-import type { AppConfigType, AppLayoutKey, IToast } from "$lib";
import type { ColorMode, ThemeKey } from "@radroots/theme";
+import type { AppConfigType, AppLayoutKey, IToast } from "@radroots/util";
import { writable } from "svelte/store";
-export const app_layout = writable<AppLayoutKey>(`mobile_base`);
+export const app_lo = writable<AppLayoutKey>();
export const app_cfg_type = writable<AppConfigType>(`personal`);
export const app_init = writable<boolean>(false);
export const app_tilt = writable<boolean>(false);
@@ -15,6 +15,8 @@ export const app_blur = writable<boolean>(false);
export const app_db = writable<boolean>(false);
export const app_geoc = writable<boolean>(false);
export const app_thc = writable<ColorMode>(`light`);
-export const app_th = writable<ThemeKey>(`os`);
+export const app_th = writable<ThemeKey>();
export const key_nostr = writable<string>(``);
export const app_nostr_profiles = writable<string[]>([]);
+
+export const search_results = writable<any[]>([]);
diff --git a/apps-lib/src/lib/store/client.ts b/apps-lib/src/lib/store/client.ts
@@ -1,4 +1,4 @@
-import { type NavigationPreviousParam } from "$lib";
+import type { NavigationPreviousParam } from "@radroots/util";
import { writable } from "svelte/store";
import { queryParam, queryParameters } from "sveltekit-search-params";
@@ -6,6 +6,7 @@ export const qp = queryParameters();
export const qp_nostr_pk = queryParam<string>("nostr_pk");
export const qp_rkey = queryParam<string>("rkey");
export const qp_id = queryParam<string>("id");
+export const qp_ref = queryParam<string>("ref");
export const qp_lat = queryParam<string>("lat");
export const qp_lng = queryParam<string>("lng");
diff --git a/apps-lib/src/lib/store/component.ts b/apps-lib/src/lib/store/component.ts
@@ -1,4 +1,4 @@
import { writable } from "svelte/store";
-export const casl_index = writable<number>(0);
-export const casl_index_max = writable<number>(0);
-\ No newline at end of file
+export const casl_i = writable<number>(0);
+export const casl_imax = writable<number>(0);
+\ No newline at end of file
diff --git a/apps-lib/src/lib/store/ndk.ts b/apps-lib/src/lib/store/ndk.ts
@@ -15,10 +15,10 @@ if (!ndk_client_name) throw new Error('Error: VITE_PUBLIC_NDK_CLIENT_NAME is req
let cacheAdapter: NDKCacheAdapter | undefined;
if (typeof window !== `undefined`) cacheAdapter = new NDKCacheAdapterDexie({ dbName: ndk_cache_name });
-const _ndk = new NDKSvelte({
+const ndk_svelte = new NDKSvelte({
cacheAdapter,
clientName: ndk_client_name,
});
-export const ndk = writable<NDKSvelte>(_ndk);
+export const ndk = writable<NDKSvelte>(ndk_svelte);
export const ndk_user = writable<NDKUser>();
diff --git a/apps-lib/src/lib/types/app.ts b/apps-lib/src/lib/types/app.ts
@@ -1,183 +0,0 @@
-//%types%
-export type NavigationRoute = string;
-//%types%
-
-export type INavigationRoutePreventRouteNav = {
- prevent_route?: {
- callback: CallbackPromise;
- };
-}
-
-export type INavigationRoutePreventRoute = {
- prevent_route: CallbackPromise;
-}
-export type INavigationRoute = {
- route: NavigationRoute | [NavigationRoute, NavigationParamTuple[]];
-};
-
-export type GlyphKey = |
- `images-square` |
- `bell` |
- `columns` |
- `bold` |
- `article` |
- `grid-four` |
- `link-simple` |
- `seal-check` |
- `selection-foreground` |
- `image-square` |
- `image-broken` |
- `funnel` |
- `users-three` |
- `note-blank` |
- `user-circle-plus` |
- `user-circle` |
- `receipt` |
- `invoice` |
- `note` |
- `arrow-left` |
- `arrows-down-up` |
- `basket` |
- `arrow-right` |
- `upload-simple` |
- `printer` |
- `download-simple` |
- `list` |
- `asterisk` |
- `asterisk-simple` |
- `subtitles-slash` |
- `cardholder` |
- `globe-x` |
- `exclamation-mark` |
- `network-x` |
- `x-circle` |
- `address-book-tabs` |
- `paper-plane-tilt` |
- `note-pencil` |
- `share-fat` |
- `folder` |
- `trash` |
- `plus-circle` |
- `currency-${GlyphKeyCurrency}` |
- `arrow-down` |
- `caret-circle-down` |
- `caret-circle-up` |
- `shopping-bag-open` |
- `coffee-bean` |
- `compass` |
- `map-pin-simple` |
- `handbag-simple` |
- `devices` |
- `lock-key` |
- `gear` |
- `gear-fine` |
- `bell-simple` |
- `envelope` |
- `house-line` |
- `arrows-left-right` |
- `list-plus` |
- `squares-four` |
- `list-plus` |
- `app-window` |
- `circle-notch` |
- `subtract-square` |
- `device-tablet-speaker` |
- `weather-cloud` |
- `warning` |
- `circle-notch` |
- `minus` |
- `key` |
- `arrow-u-up-left` |
- `arrow-counter-clockwise` |
- `circle` |
- `check-circle` |
- `circle-dashed` |
- `dots-three` |
- `cards-three` |
- `lightning` |
- `cards` |
- `note-pencil` |
- `tray` |
- `calendar-dots` |
- `notepad` |
- `network` |
- `calendar-blank` |
- `chats-circle` |
- `plant` |
- `farm` |
- `magnifying-glass` |
- `chat-circle-dots` |
- `dots-three-outline` |
- `copy` |
- `circles-four` |
- `waveform` |
- `film-strip` |
- `arrow-up` |
- `arrow-circle-up` |
- `plus` |
- `funnel-simple` |
- `user` |
- `camera` |
- `check` |
- `file` |
- `share-network` |
- `question` |
- `minus-circle` |
- `globe-simple` |
- `globe` |
- `warning-circle` |
- `x` |
- `info` |
- `caret-${GeometryCardinalDirection}` |
- `caret-up-down`;
-export type GlyphKeyCurrency = `dollar` | `eur`;
-
-export type AppConfigType = `farmer` | `personal`
-export type AppLayoutKey = 'mobile_base' | 'mobile_y';
-
-export type CallbackPromiseFull<Ti, Tr> = (value: Ti) => Promise<Tr>;
-export type CallbackPromiseGeneric<T> = (value: T) => Promise<void>;
-export type CallbackPromiseReturn<T> = () => Promise<T>;
-export type CallbackPromise = () => Promise<void>;
-
-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 EntryStyle = `guide` | `line`;
-
-export type GeometryScreenPositionHorizontal = `left` | `center` | `right`;
-export type GeometryScreenPositionVertical = `top` | `center` | `bottom`;
-export type GeometryScreenPosition = `${GeometryScreenPositionVertical}-${GeometryScreenPositionHorizontal}`;
-export type GeometryCardinalDirection = `up` | `down` | `left` | `right`;
-export type GeometryDimension =
- `xs` |
- `sm` |
- `md` |
- `lg` |
- `xl`;
-export type GeometryGlyphDimension =
- | `${GeometryDimension}`
- | `${GeometryDimension}-`
- | `${GeometryDimension}--`
- | `${GeometryDimension}+`;
-
-export type GlyphWeight = `light` | `regular` | `fill` | `bold`; // `thin` `duotone`
-
-export type LoadingBlades = 8 | 12;
-export type LoadingDimension = GeometryDimension | `glyph-send-button`; //@todo remove
-
-export type LayerGlyphBasisKind = `_a` | `_d` | `_pl`;
-
-export type NavigationRouteParamNostrPublicKey = `nostr_pk`;
-export type NavigationRouteParamRecordKey = `rkey`;
-export type NavigationRouteParamId = `id`;
-export type NavigationRouteParamLat = `lat`;
-export type NavigationRouteParamLng = `lng`;
-export type NavigationRouteParamKey = NavigationRouteParamNostrPublicKey | NavigationRouteParamId | NavigationRouteParamRecordKey | NavigationRouteParamLat | NavigationRouteParamLng;
-export type NavigationParamTuple = [NavigationRouteParamKey, string];
-export type NavigationPreviousParam<T extends string> = { route: T, label?: string; params?: NavigationParamTuple[] }
-
-export type CallbackRoute = CallbackPromise | INavigationRoute;
-\ No newline at end of file
diff --git a/apps-lib/src/lib/types/component.ts b/apps-lib/src/lib/types/component.ts
@@ -1,156 +0,0 @@
-import type { CallbackPromise, CallbackRoute, GlyphKey, ICbG, ICbGOpt, ICbOpt, IClOpt, IDisabledOpt, IGl, IGlOpt, IGlyph, IInput, ILabel, ILabelOpt, ILabelTup, ILoadingOpt, ILy, INavigationRoute, INavigationRoutePreventRouteNav, ISelect } from "$lib";
-
-export type IBasisOpt<T extends object> = T | undefined;
-
-export type IPageToolbar = ICbOpt & {
- header?: IPageHeader;
-};
-
-export type IPageHeader = {
- label: string;
- callback_route?: CallbackRoute;
-};
-
-export type IGlyphCircle = {
- classes_wrap: string;
- glyph: IGlyph
-};
-
-export type ITrellis = ILy &
- IClOpt &
- ITrellisStyles & {
- id?: string;
- view?: string;
- title?: ITrellisTitle;
- description?: ITrellisDescription;
- default_el?: ITrellisDefault;
- list?: (ITrellisKind | undefined)[];
- hide_offset?: true;
- };
-
-export type ITrellisStyles = {
- hide_rounded?: boolean;
- hide_border_top?: boolean;
- hide_border_bottom?: boolean;
- set_title_background?: boolean;
- set_default_background?: boolean;
-};
-
-export type ITrellisTitle = ICbOpt &
- IClOpt & {
- mod?: ITrellisBasisOffsetMod,
- value: string | true;
- link?: ICbOpt &
- IClOpt &
- IGlOpt & ILabelOpt;
- };
-
-export type ITrellisDescription = string | true;
-
-export type ITrellisBasisOffsetModKey = 'sm' | 'glyph';
-export type ITrellisBasisOffsetMod = ITrellisBasisOffsetModKey | (({ glyph: IGlyph } | { glyph_circle: IGlyphCircle }) & {
- loading?: boolean;
-});
-
-export type ITrellisDefault = {
- labels?: ITrellisDefaultLabel[];
- show_title?: boolean;
-};
-
-export type ITrellisDefaultLabel = ICbOpt & {
- label: string;
- classes?: string;
-};
-
-export type ITrellisKind = (
- | ITrellisKindTouch
- | ITrellisKindInput
- | ITrellisKindSelect
-);
-
-export type ITrellisBasis = {
- loading?: boolean;
- hide_active?: boolean;
- hide_field?: boolean;
- offset?: ITrellisBasisOffset;
- full_rounded?: boolean;
-};
-
-export type ITrellisBasisOffset = ICbGOpt<MouseEvent> &
- IClOpt & {
- mod?: ITrellisBasisOffsetMod;
- classes?: string;
- hide_space?: boolean;
- hide_offset?: boolean;
- };
-
-export type ITrellisKindDisplay = {
- display?: ITrellisKindDisplayValue;
-}
-export type ITrellisKindDisplayValue = ICbGOpt<MouseEvent> & ILoadingOpt &
- (ITrellisKindDisplayValueIcon | ILabel);
-
-
-export type ITrellisKindDisplayValueIcon = {
- icon: {
- classes?: string;
- key: GlyphKey;
- };
-};
-export type ITrellisKindTouch = ITrellisBasis & {
- touch: ITrellisBasisTouch;
-};
-
-export type ITrellisBasisTouch = ICbGOpt<MouseEvent> &
- ILabelTup & ITrellisKindDisplay & {
- end?: ITrellisBasisTouchEnd;
- };
-
-export type ITrellisKindInput = ITrellisBasis & {
- input: ITrellisBasisInput;
-};
-
-export type ITrellisBasisInput = {
- basis: IInput<string>;
- line_label?: {
- classes?: string;
- value: string;
- };
- action?: {
- visible: boolean;
- loading?: boolean;
- callback?: CallbackPromise;
- glyph?: IGlyph
- };
-};
-
-export type ITrellisKindSelect = ITrellisBasis & {
- select: ITrellisBasisSelect;
-};
-
-export type ITrellisBasisSelect = ICbGOpt<MouseEvent> &
- ILabelTup & ITrellisKindDisplay & ILoadingOpt & {
- end?: ITrellisBasisTouchEnd;
- el: ISelect & { value: string; };
- };
-
-export type ITrellisBasisTouchEnd = ICbGOpt<MouseEvent> & IGl;
-
-export type INavBasisPrev = IClOpt & ICbG<
- HTMLLabelElement | null
-> & IGlOpt & ILabelOpt & IDisabledOpt & {
- loading?: boolean;
-};
-export type INavBasisOption = IClOpt & ICbG<
- HTMLLabelElement | null
-> & IGlOpt & ILabelOpt & IDisabledOpt & {
- loading?: boolean;
-};
-export type INavBasis = {
- prev: ICbOpt & ILoadingOpt & INavigationRoute & INavigationRoutePreventRouteNav & {
- label?: string;
- kind?: 'arrow'
- };
- title?: ICbOpt & ILabel;
- option?: INavBasisOption;
-};
diff --git a/apps-lib/src/lib/types/el.ts b/apps-lib/src/lib/types/el.ts
@@ -1,56 +0,0 @@
-import type { CallbackPromiseGeneric, ElementCallbackMount, ElementCallbackValue, ElementCallbackValueBlur, ElementCallbackValueKeydown, FormField, GeometryGlyphDimension, GeometryScreenPosition, GlyphKey, GlyphWeight, ICbGOpt, ICbOpt, IClOpt, IDisabledOpt, IGlOpt, IId, IIdGOpt, IIdOpt, ILabel, ILyOpt } from "$lib";
-
-export type IToastKind = `simple`;
-
-export type IToast = IClOpt &
- ILabel & IGlOpt & ILyOpt & {
- styles?: IToastKind[];
- position?: GeometryScreenPosition;
- };
-
-export type IGlyph = ICbOpt & IIdOpt & ILyOpt & IClOpt & {
- weight?: GlyphWeight;
- key: GlyphKey;
- dim?: GeometryGlyphDimension;
-};
-
-export type IInput<T extends string> = IIdGOpt<T> & IClOpt & ILyOpt & {
- placeholder?: string;
- label?: string;
- hidden?: boolean;
- validate?: RegExp;
- sync?: boolean;
- field?: FormField;
- callback?: ElementCallbackValue,
- callback_keydown?: ElementCallbackValueKeydown<HTMLInputElement>,
- callback_blur?: ElementCallbackValueBlur<HTMLInputElement>;
- callback_focus?: ElementCallbackValueBlur<HTMLInputElement>;
- callback_mount?: ElementCallbackMount<HTMLInputElement>;
-};
-
-export type ISelectOption<T extends string> = IDisabledOpt & {
- value: T;
- label: string;
-};
-
-export type ISelect = IIdOpt & IClOpt & ILyOpt &
- ICbGOpt<ISelectOption<string>> & {
- sync?: boolean;
- sync_init?: boolean;
- options: { group?: string | true; entries: ISelectOption<string>[] }[];
- show_arrows?: 'l' | 'r';
- };
-
-export type ITextAreaElement = IId & IClOpt & ILyOpt & {
- placeholder?: string;
- label?: string;
- hidden?: boolean;
- validate?: RegExp;
- sync?: true;
- field?: FormField;
- 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>;
-};
-\ No newline at end of file
diff --git a/apps-lib/src/lib/types/feature.ts b/apps-lib/src/lib/types/feature.ts
@@ -1,8 +0,0 @@
-import type { CallbackPromiseGeneric } from "./app";
-import type { ModelLocationGcs, ModelNostrProfile, ModelNostrRelay } from "./model";
-
-export type ISearchResultDisplayCallbacks = {
- lc_handle_result_location_gcs: CallbackPromiseGeneric<ModelLocationGcs>;
- lc_handle_result_nostr_profile: CallbackPromiseGeneric<ModelNostrProfile>;
- lc_handle_result_nostr_relay: CallbackPromiseGeneric<ModelNostrRelay>;
-}
-\ No newline at end of file
diff --git a/apps-lib/src/lib/types/interface.ts b/apps-lib/src/lib/types/interface.ts
@@ -1,156 +0,0 @@
-import type { CallbackPromise, CallbackPromiseGeneric, EntryStyle, GlyphKey, IGlyph, IInput, LayerGlyphBasisKind, LoadingBlades, LoadingDimension } from "$lib";
-import type { ThemeLayer } from "@radroots/theme";
-import type { TransitionConfig } from "svelte/transition";
-
-export type IDisabledOpt = {
- disabled?: boolean | never;
-}
-
-export type IBasis<T> = {
- basis: T;
-};
-
-export type ICb = {
- callback: CallbackPromise | never;
-};
-
-export type ICbOpt = Partial<ICb>;
-
-export type ICbG<T> = {
- callback: CallbackPromiseGeneric<T> | never;
-};
-
-export type ICbGOpt<T> = Partial<ICbG<T>>;
-
-export type ICl = {
- classes: string | never;
-};
-
-export type IClOpt = Partial<ICl>;
-
-
-export type IId = {
- id: string | never;
-};
-
-export type IIdOpt = Partial<IId>;
-
-export type IGl = {
- glyph: IGlyph | never;
-};
-
-export type IGlOpt = Partial<IGl>;
-
-export type IGlyphKey = {
- glyph: GlyphKey
-};
-
-export type ILy = {
- layer: ThemeLayer | never;
-};
-
-export type ILyOpt = Partial<ILy>;
-
-export type ILableFieldsSwap = {
- toggle: boolean;
- on: IClOpt & {
- value: string;
- },
- off: IClOpt & {
- value: string;
- },
-};
-
-export type ILabelSwap = {
- swap: ILableFieldsSwap;
-}
-
-export type ILabelTupFields = {
- left?: ILableFields[];
- right?: ILableFields[];
-};
-
-export type ILabelTup = {
- label: ILabelTupFields;
-};
-
-export type LabelFieldKind = `link` | `on` | `shade`;
-
-export type ILableFields = & {
- classes_wrap?: string
- classes?: string;
- kind?: LayerGlyphBasisKind;
- hide_truncate?: boolean;
- hide_active?: boolean;
-} & (
- ({
- value: string;
- } | ILabelSwap)
- | IGl
- );
-
-export type ILabel = {
- label: ILableFields;
-};
-
-export type ILabelOpt = Partial<ILabel>;
-
-export type ILoadSymbol = IClOpt & {
- color?: 'white';
- blades?: LoadingBlades;
- dim?: LoadingDimension;
-};
-
-export type IIdG<T extends string> = {
- id: T | never;
-};
-
-export type FormField = {
- validate: RegExp;
- charset: RegExp;
- validate_keypress?: boolean;
-};
-
-export type IIdGOpt<T extends string> = Partial<IIdG<T>>;
-
-
-export type IIdWrap = {
- id_wrap: string | never;
-};
-
-export type IIdWrapOpt = Partial<IIdWrap>;
-
-export type ILabelValue = {
- label: IClOpt & {
- value: string;
- };
-};
-
-export type ILabelDisplay = IIdWrapOpt & IClOpt & ILabelValue & ILyOpt & {
- style?: EntryStyle;
-};
-
-
-export type ILoading = {
- loading: boolean | never;
-};
-
-export type ILoadingOpt = Partial<ILoading>;
-
-export type IEntryWrap = IClOpt & IIdOpt & ILyOpt & {
- style?: EntryStyle;
- style_a?: true;
- no_pad?: true;
- fade?: {
- in?: TransitionConfig;
- out?: TransitionConfig;
- };
-}
-
-export type IEntryLine = ILoadingOpt & {
- wrap?: IEntryWrap;
- el: IInput<string>;
- notify_inline?: {
- glyph: GlyphKey | IGlyph;
- };
-};
diff --git a/apps-lib/src/lib/types/model.ts b/apps-lib/src/lib/types/model.ts
@@ -1,34 +0,0 @@
-export type ModelNostrProfile = {
- id: string;
- name?: string;
- display_name?: string;
- about?: string;
- picture?: string;
-}
-
-export type ModelNostrRelay = {
- id: string;
-}
-
-export type ModelLocationGcs = {
- id: string;
- lat: number;
- lng: number;
- geohash: string;
- kind: string;
- label?: string;
- area?: number;
- elevation?: number;
- soil?: string;
- climate?: string;
- gc_id?: string;
- gc_name?: string;
- gc_admin1_id?: string;
- gc_admin1_name?: string;
- gc_country_id?: string;
- gc_country_name?: string;
-}
-
-export type ModelTradeProduct = {
- id: string;
-}
-\ No newline at end of file
diff --git a/apps-lib/src/lib/types/util.ts b/apps-lib/src/lib/types/util.ts
@@ -1,24 +0,0 @@
-import { type CallbackPromiseFull } from "$lib";
-
-export type GeolocationLatitudeFmtOption = 'dms' | 'd' | 'dm';
-
-export type GeolocationPoint = {
- lat: number;
- lng: number;
-};
-
-export type GeocoderReverseResult = {
- id: number;
- name: string;
- admin1_id: string | number;
- admin1_name: string;
- country_id: string;
- country_name: string;
- latitude: number;
- longitude: number;
-};
-
-export type ILcGeocodeCallback = CallbackPromiseFull<
- GeolocationPoint,
- GeocoderReverseResult | undefined
->
-\ No newline at end of file
diff --git a/apps-lib/src/lib/types/view.ts b/apps-lib/src/lib/types/view.ts
@@ -1,41 +0,0 @@
-import { type CallbackPromise, type CallbackPromiseFull, type CallbackPromiseReturn, type IBasisOpt } from "..";
-import type { ModelLocationGcs, ModelNostrProfile, ModelNostrRelay, ModelTradeProduct } from "./model";
-
-export type ViewBasis<T extends object> = {
- kv_init_prevent?: boolean;
- lc_on_mount?: CallbackPromise;
- lc_on_destroy?: CallbackPromise;
- lc_gui_alert?: CallbackPromiseFull<string, boolean>;
- lc_gui_confirm?: CallbackPromiseFull<string, boolean>;
-} & T;
-
-export type ViewBasisLoadData<TView extends object, TLoadData extends object> = ViewBasis<TView> & {
- lc_load_data: CallbackPromiseReturn<TLoadData>;
- lc_handle_back?: CallbackPromise;
-};
-
-export type IFarmLoadData = IBasisOpt<{
- location_gcs: ModelLocationGcs[];
-}>;
-
-export type IFarmViewLoadData = IBasisOpt<{
- location_gcs: ModelLocationGcs;
-}>;
-
-export type ISearchLoadData = IBasisOpt<{
- location_gcs: ModelLocationGcs[];
- nostr_profile: ModelNostrProfile[];
- nostr_relay: ModelNostrRelay[];
- trade_product: ModelTradeProduct[];
-}>;
-
-export type ISettingsNostrProfileLoadData = IBasisOpt<{
- nostr_profile: ModelNostrProfile;
-}>;
-
-export type ISettingsNostrProfileEditLoadData = IBasisOpt<{
- nostr_profile: ModelNostrProfile;
- field_key: string;
- field_val?: string;
-}>;
-
diff --git a/apps-lib/src/lib/util/app.ts b/apps-lib/src/lib/util/app.ts
@@ -1,143 +0,0 @@
-import { goto } from "$app/navigation";
-import { nav_prev, type AppLayoutKey, type CallbackPromise, type CallbackRoute, type INavigationRoute, type INavigationRoutePreventRoute, type NavigationParamTuple } from "$lib";
-import type { ColorMode, ThemeKey, ThemeLayer } from "@radroots/theme";
-import { get } from "svelte/store";
-
-export const get_store = get;
-
-export const sleep = async (ms: number): Promise<void> => {
- await new Promise((resolve) => setTimeout(resolve, ms));
-};
-
-export const theme_set = (theme_key: ThemeKey, color_mode: ColorMode): void => {
- const data_theme = `${theme_key}_${color_mode}`;
- document.documentElement.setAttribute("data-theme", data_theme);
-};
-
-export const fmt_id = (id?: string): string => {
- const pref = location.pathname.slice(1, -1).replaceAll(`-`, `_`).replaceAll(`/`, `-`).replaceAll(`--`, `-`);
- return `*${pref}${id ? `-${id}` : ``}`
-};
-
-export const fmt_cl = (classes?: string): string => {
- return classes ? classes : ``;
-};
-
-export const parse_layer = (layer?: number, layer_default?: ThemeLayer): ThemeLayer => {
- switch (layer) {
- case 0:
- case 1:
- case 2:
- return layer;
- default:
- return layer_default ? layer_default : 0;
- };
-};
-
-export const encode_qp = (params_list?: NavigationParamTuple[]): string => {
- const params = (params_list || []).filter(i => i[0] && i[1])
- if (!params.length) return ``;
- return params.map(([k, v], index) => `${index === 0 ? `?` : ``}&${k.trim()}=${encodeURI(v.trim())}`).join(``).trim();
-};
-
-export const encode_qp_route = <T extends string>(route: T, params_list?: NavigationParamTuple[]): string => {
- return `${route}/${encode_qp(params_list)}`.replaceAll(`//`, `/`)
-};
-
-export const exe_iter = async (callback: CallbackPromise, num: number = 1, delay: number = 400): Promise<void> => {
- try {
- const iter_fn = (count: number) => {
- if (count > 0) {
- callback();
- if (count > 1) {
- setTimeout(() => {
- iter_fn(count - 1);
- }, delay);
- }
- }
- };
- iter_fn(num);
- } catch (e) {
- console.log(`(error) exe_iter `, e);
- }
-};
-
-export const value_constrain = (regex_charset: RegExp, value: string): string => {
- return value
- .split(``)
- .filter((char) => regex_charset.test(char))
- .join(``);
-};
-
-export const value_constrain_textarea = (regex_charset: RegExp, value: string): string => {
- return value
- .replace(/\u00A0/g, ` `)
- .split(/[\n]/)
- .map(line => line
- .split(``)
- .filter((char) => regex_charset.test(char))
- .join(``)
- )
- .join("\n");
-};
-
-export const fmt_textarea_value = (value: string): string => {
- return value.replace(/ /g, `\u00A0`);
-};
-
-export const route_prev = async (opts: (INavigationRoute | INavigationRoutePreventRoute)): Promise<void> => {
- let route_to = `/`
- if (`prevent_route` in opts && opts.prevent_route) return void await opts.prevent_route();
- else if (`route` in opts) {
- const $nav_prev = get_store(nav_prev);
- console.log(JSON.stringify($nav_prev, null, 4), `$nav_prev`)
- let route_to =
- typeof opts.route === `string`
- ? opts.route
- : encode_qp_route(opts.route[0], opts.route[1]);
- if ($nav_prev.length) {
- const nav_prev_li = $nav_prev[$nav_prev.length - 1];
- console.log(JSON.stringify(nav_prev_li, null, 4), `nav_prev_li`)
- nav_prev.set([...$nav_prev.slice(0, -1)]);
- if (nav_prev_li)
- route_to = encode_qp_route(
- nav_prev_li.route,
- nav_prev_li.params,
- );
- console.log(JSON.stringify($nav_prev, null, 4), `$nav_prev`)
- }
- }
- console.log(JSON.stringify(route_to, null, 4), `route_to`)
- await goto(route_to);
-};
-
-export const callback_route = async (callback_route: CallbackRoute): Promise<void> => {
- if (`route` in callback_route) {
- if (typeof callback_route.route === `string`) return void await goto(callback_route.route);
- else return void await goto(
- encode_qp_route(
- callback_route.route[0],
- callback_route.route[1],
- ),
- );
- }
- return void await callback_route();
-};
-
-export const get_layout = (val: string | false): AppLayoutKey => {
- switch (val) {
- case `mobile_base`:
- case `mobile_y`:
- return val;
- default:
- return `mobile_base`;
- };
-};
-
-export const debounce_input = (func: Function, delay: number) => {
- let timer: ReturnType<typeof setTimeout>;
- return function (this: any, ...args: any) {
- clearTimeout(timer);
- timer = setTimeout(() => func.apply(this, args), delay);
- };
-};
diff --git a/apps-lib/src/lib/util/carousel.ts b/apps-lib/src/lib/util/carousel.ts
@@ -3,9 +3,10 @@ import {
carousel_index,
carousel_index_max,
carousel_num,
- exe_iter,
get_store
-} from "$lib";
+} from "$root";
+import { exe_iter } from "@radroots/util";
+
const CAROUSEL_DELAY_MS = 150;
const get_slide_container = <T extends string>(
diff --git a/apps-lib/src/lib/util/casl.ts b/apps-lib/src/lib/util/casl.ts
@@ -1,19 +1,20 @@
-import { casl_index, casl_index_max } from "$lib/store/component";
-import { get_store } from "./app";
+import { casl_i, casl_imax, get_store } from "$root";
-export const casl_inc = async (opts?: 'reflow'): Promise<void> => {
- const $casl_index = get_store(casl_index);
- const $casl_index_max = get_store(casl_index_max);
- casl_index.set(($casl_index + 1) % $casl_index_max);
+export const casl_inc = async (opts?: 'noflow'): Promise<void> => {
+ const $casl_i = get_store(casl_i);
+ const $casl_imax = get_store(casl_imax);
+ if (opts === 'noflow' && $casl_i < $casl_imax) casl_i.set($casl_i + 1);
+ else casl_i.set(($casl_i + 1) % ($casl_imax + 1));
};
-export const casl_dec = async (opts?: 'reflow'): Promise<void> => {
- const $casl_index = get_store(casl_index);
- const $casl_index_max = get_store(casl_index_max);
- casl_index.set(($casl_index - 1 + $casl_index_max) % $casl_index_max);
+export const casl_dec = async (opts?: 'noflow'): Promise<void> => {
+ const $casl_i = get_store(casl_i);
+ const $casl_imax = get_store(casl_imax);
+ if (opts === 'noflow' && $casl_i > 0) casl_i.set($casl_i - 1);
+ else casl_i.set(($casl_i - 1 + ($casl_imax + 1)) % ($casl_imax + 1));
};
export const casl_init = (index_curr: number, index_max: number): void => {
- casl_index.set(index_curr);
- casl_index_max.set(index_max);
-};
-\ No newline at end of file
+ casl_i.set(index_curr);
+ casl_imax.set(index_max);
+};
diff --git a/apps-lib/src/lib/util/component.ts b/apps-lib/src/lib/util/component.ts
@@ -1,10 +0,0 @@
-import { type LabelFieldKind } from "$lib";
-import type { ThemeLayer } from "@radroots/theme";
-
-export const fmt_trellis = (hide_border_t: boolean, hide_border_b: boolean): string => {
- return `${hide_border_t ? `group-first:border-t-0` : `group-first:border-t-line`} ${hide_border_b ? `group-last:border-b-0` : `group-last:border-b-line`}`;
-};
-
-export const get_label_classes_kind = (layer: ThemeLayer, label_kind: LabelFieldKind | undefined, hide_active: boolean): string => {
- return `text-layer-${layer}-glyph${label_kind ? `-${label_kind}` : ``} ${hide_active ? `` : `group-active:text-layer-${layer}-glyph${label_kind ? `-${label_kind}_a` : `_a`}`}`
-};
-\ No newline at end of file
diff --git a/apps-lib/src/lib/util/conf.ts b/apps-lib/src/lib/util/conf.ts
@@ -1,51 +0,0 @@
-import { type AppLayoutKey } from "$lib";
-
-export const ascii = {
- bullet: '•',
- dash: `—`,
- up: `↑`,
- down: `↓`
-}
-
-type ConfigWindow = {
- layout: Record<AppLayoutKey, {
- h: number;
- }>;
- debounce: {
- search: number;
- }
-};
-
-export const cfg_app: ConfigWindow = {
- layout: {
- mobile_base: {
- h: 600
- },
- mobile_y: {
- h: 750
- }
- },
- debounce: {
- search: 200
- },
-};
-
-export const cfg_map = {
- styles: {
- base: {
- light: `https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json`,
- dark: `https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json`
- }
- },
- popup: {
- dot: {
- offset: [0, -10] as [number, number]
- }
- },
- coords: {
- default: {
- lat: 0,
- lng: 0,
- }
- }
-};
-\ No newline at end of file
diff --git a/apps-lib/src/lib/util/document.ts b/apps-lib/src/lib/util/document.ts
@@ -1,52 +0,0 @@
-import { browser } from "$app/environment";
-
-export const el_id = (id: string): HTMLElement | undefined => {
- const el = document.getElementById(id);
- return el ? el : undefined;
-};
-
-export const el_toggle = (id: string, toggle_class: string): void => {
- const el = document.getElementById(id);
- if (el) el.classList.toggle(toggle_class);
-};
-
-export const els_id_pref = (id_pref: string): Element[] | undefined => {
- const els = document.querySelectorAll(`[id^="${id_pref}"]`);
- if (els && els.length) return Array.from(els);
- return undefined;
-};
-
-export const els_id_pref_index = (id_pref: string, num_index: number, orientation: `greater` | `lesser` | `not` = `greater`, inclusive: boolean = true): Element[] | undefined => {
- const els = document.querySelectorAll(`[id^="${`${id_pref}-`.replaceAll(`--`, `-`)}"]`);
- if (els && els.length) return Array.from(els).filter(el => {
- const match = el.id.match(/(?<=^|\-)[0-9]\d*(?=\-)/)
- if (match) {
- const num = parseInt(match[0], 10);
- switch (orientation) {
- case `greater`: {
- if (inclusive) return num >= num_index;
- else return num > num_index;
- }
- case `lesser`: {
- if (inclusive) return num <= num_index;
- else return num < num_index;
- }
- case `not`: {
- return num !== num_index;
- }
- }
- }
- return false;
- });
- return undefined;
-};
-
-export const view_effect = <T extends string>(view: T): void => {
- console.log(`view_effect `, view)
- if (!browser) return;
- for (const el of document.querySelectorAll(`[data-view]`)) {
- if (el.getAttribute(`data-view`) !== view) el.classList.add(`hidden`)
- else el.classList.remove(`hidden`)
- }
-};
-
diff --git a/apps-lib/src/lib/util/error.ts b/apps-lib/src/lib/util/error.ts
@@ -1,36 +0,0 @@
-import { page } from "$app/stores";
-import { get_store } from "$lib";
-
-export type IErrorCatchCallback = {
- name: string;
- message: string;
- stack: string;
- url: string;
- func: string;
-};
-
-export const catch_err = async (e: unknown, func: string, callback: (opts: IErrorCatchCallback) => Promise<void>): Promise<void> => {
- const $page = get_store(page) as any;
- let name = ``;
- let message = ``;
- let stack = ``;
- let url = ``;
- if (e instanceof Error) {
- name = e.name;
- message = e.message;
- stack = e.stack;
- url = $page.url.pathname;
- }
- await callback({ name, message, stack, url, func });
-};
-
-export const handle_err = async (e: unknown, fcall: string): Promise<void> => {
- try {
- return void await catch_err(e, fcall, async (opts) => {
- console.log(`handle_err e `, e)
- console.log(JSON.stringify(opts, null, 4), `handle_err opts`)
- });
- } catch (e) {
- console.log(`(handle_err) `, e)
- }
-};
-\ No newline at end of file
diff --git a/apps-lib/src/lib/util/geolocation.ts b/apps-lib/src/lib/util/geolocation.ts
@@ -1,47 +0,0 @@
-import { get_store, liblocale, type GeolocationLatitudeFmtOption } from "$lib";
-
-export const geol_lat_fmt = (lat: number, fmt_opt: GeolocationLatitudeFmtOption, precision: number = 5): string => {
- const $locale = get_store(liblocale)
- const options: Intl.NumberFormatOptions = {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- };
- const fmt_deg = new Intl.NumberFormat($locale, { maximumFractionDigits: 0 });
- const fmt_min = new Intl.NumberFormat($locale, options);
- const fmt_sec = new Intl.NumberFormat($locale, options);
- if (fmt_opt === 'dms') {
- const deg = Math.floor(Math.abs(lat));
- const min = Math.floor((Math.abs(lat) - deg) * 60);
- const sec = ((Math.abs(lat) - deg - min / 60) * 3600);
- return `${fmt_deg.format(deg)}° ${fmt_min.format(min)}' ${fmt_sec.format(sec)}" ${lat >= 0 ? 'N' : 'S'}`;
- } else if (fmt_opt === 'dm') {
- const deg = Math.floor(Math.abs(lat));
- const min = (Math.abs(lat) - deg) * 60;
- return `${fmt_deg.format(deg)}° ${fmt_min.format(min)}' ${lat >= 0 ? 'N' : 'S'}`;
- } else {
- return `${lat.toLocaleString($locale, { maximumFractionDigits: precision })}° ${lat >= 0 ? 'N' : 'S'}`;
- }
-};
-
-export const geol_lng_fmt = (lng: number, fmt_opt: GeolocationLatitudeFmtOption, precision: number = 5): string => {
- const $locale = get_store(liblocale)
- const options: Intl.NumberFormatOptions = {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- };
- const fmt_deg = new Intl.NumberFormat($locale, { maximumFractionDigits: 0 });
- const fmt_min = new Intl.NumberFormat($locale, options);
- const fmt_sec = new Intl.NumberFormat($locale, options);
- if (fmt_opt === 'dms') {
- const degrees = Math.floor(Math.abs(lng));
- const minutes = Math.floor((Math.abs(lng) - degrees) * 60);
- const seconds = ((Math.abs(lng) - degrees - minutes / 60) * 3600);
- return `${fmt_deg.format(degrees)}° ${fmt_min.format(minutes)}' ${fmt_sec.format(seconds)}" ${lng >= 0 ? 'E' : 'W'}`;
- } else if (fmt_opt === 'dm') {
- const degrees = Math.floor(Math.abs(lng));
- const minutes = (Math.abs(lng) - degrees) * 60;
- return `${fmt_deg.format(degrees)}° ${fmt_min.format(minutes)}' ${lng >= 0 ? 'E' : 'W'}`;
- } else {
- return `${lng.toLocaleString($locale, { maximumFractionDigits: precision })}° ${lng >= 0 ? 'E' : 'W'}`;
- }
-};
-\ No newline at end of file
diff --git a/apps-lib/src/lib/util/i18n.ts b/apps-lib/src/lib/util/i18n.ts
@@ -1,22 +0,0 @@
-import i18n, { type Loader } from '@sveltekit-i18n/base';
-import type { Config, Parser } from '@sveltekit-i18n/parser-icu';
-import parser from '@sveltekit-i18n/parser-icu';
-
-type LanguageConfig = {
- default?: string;
- value?: string;
-};
-export const i18n_conf = <T extends string>(opts: {
- default_locale: T;
- translations: Record<T, any>;
- loaders: Loader.LoaderModule[]
-}): i18n<Parser.Params<LanguageConfig>> => {
- const { default_locale: initLocale, translations, loaders } = opts;
- const config: Config<LanguageConfig> = {
- initLocale,
- translations,
- parser: parser(),
- loaders,
- };
- return new i18n(config);
-};
-\ No newline at end of file
diff --git a/apps-lib/src/lib/util/idb.ts b/apps-lib/src/lib/util/idb.ts
@@ -0,0 +1,54 @@
+import { browser } from "$app/environment";
+import { fmt_id } from "$root";
+
+//@ts-ignore
+const kv_name = import.meta.env.VITE_PUBLIC_KV_NAME;
+if (!kv_name) throw new Error('Error: VITE_PUBLIC_KV_NAME is required');
+
+export let idb: Keyva;
+if (browser) idb = new Keyva({ name: kv_name });
+
+export const kv_init = async (): Promise<void> => {
+ if (!browser) return;
+ const range = Keyva.prefix(`*`);
+ const kv_list = await idb.each({ range }, `keys`);
+ await Promise.all(kv_list.map((i) => idb.delete(i)));
+};
+
+export const kv_init_page = async (): Promise<void> => {
+ if (!browser) return;
+ const kv_pref = fmt_id();
+ const range = Keyva.prefix(kv_pref);
+ const kv_list = await idb.each({ range }, `keys`);
+ await Promise.all(kv_list.map((i) => idb.delete(i)));
+};
+
+export const kv_sync = async (list: [string, string][]): Promise<void> => {
+ if (!browser) return;
+ for (const [key, val] of list) await idb.set(key, val);
+};
+
+export class KvLib<T extends string> {
+ private _kv: Keyva;
+
+ constructor(kv: Keyva) {
+ this._kv = kv;
+ }
+ public init = async () => {
+ await kv_init_page();
+ }
+
+ public save = async (key: T, value: string) => {
+ await this._kv.set(fmt_id(key), value);
+ }
+
+ public read = async (key: T): Promise<string | undefined> => {
+ const result = await this._kv.get<string>(fmt_id(key));
+ if (result) return result;
+ return undefined;
+ }
+
+ public del = async (key: T) => {
+ await this._kv.delete(fmt_id(key));
+ }
+}
+\ No newline at end of file
diff --git a/apps-lib/src/lib/util/kv.ts b/apps-lib/src/lib/util/kv.ts
@@ -1,54 +0,0 @@
-import { browser } from "$app/environment";
-import { fmt_id } from "$lib";
-
-//@ts-ignore
-const kv_name = import.meta.env.VITE_PUBLIC_KV_NAME;
-if (!kv_name) throw new Error('Error: VITE_PUBLIC_KV_NAME is required');
-
-export let kv_basis: Keyva;
-if (browser) kv_basis = new Keyva({ name: kv_name });
-
-export const kv_init = async (): Promise<void> => {
- if (!browser) return;
- const range = Keyva.prefix(`*`);
- const kv_list = await kv_basis.each({ range }, `keys`);
- await Promise.all(kv_list.map((i) => kv_basis.delete(i)));
-};
-
-export const kv_init_page = async (): Promise<void> => {
- if (!browser) return;
- const kv_pref = fmt_id();
- const range = Keyva.prefix(kv_pref);
- const kv_list = await kv_basis.each({ range }, `keys`);
- await Promise.all(kv_list.map((i) => kv_basis.delete(i)));
-};
-
-export const kv_sync = async (list: [string, string][]): Promise<void> => {
- if (!browser) return;
- for (const [key, val] of list) await kv_basis.set(key, val);
-};
-
-export class KvLib<T extends string> {
- private _kv: Keyva;
-
- constructor(kv: Keyva) {
- this._kv = kv;
- }
- public init = async () => {
- await kv_init_page();
- }
-
- public save = async (key: T, value: string) => {
- await this._kv.set(fmt_id(key), value);
- }
-
- public read = async (key: T): Promise<string | undefined> => {
- const result = await this._kv.get<string>(fmt_id(key));
- if (result) return result;
- return undefined;
- }
-
- public del = async (key: T) => {
- await this._kv.delete(fmt_id(key));
- }
-}
-\ No newline at end of file
diff --git a/apps-lib/src/lib/util/lib.ts b/apps-lib/src/lib/util/lib.ts
@@ -0,0 +1,85 @@
+import { browser } from "$app/environment";
+import { goto } from "$app/navigation";
+import { page } from "$app/state";
+import { liblocale } from "$root";
+import type { ColorMode, ThemeKey } from "@radroots/theme";
+import { encode_qp_route, fmt_geometry_point_coords, fmt_price, parse_currency_marker, type CallbackRoute, type GeometryPoint, type IErrorCatchCallback } from "@radroots/util";
+import { get } from "svelte/store";
+
+export const get_store = get;
+
+export const theme_set = (theme_key: ThemeKey, color_mode: ColorMode): void => {
+ const data_theme = `${theme_key}_${color_mode}`;
+ document.documentElement.setAttribute("data-theme", data_theme);
+};
+
+export const fmt_id = (id?: string): string => {
+ if (!browser) return ``;
+ const pref = location?.pathname.slice(1, -1).replaceAll(`-`, `_`).replaceAll(`/`, `-`).replaceAll(`--`, `-`);
+ return `*${pref}${id ? `-${id}` : ``}`
+};
+
+export const catch_err = async (e: unknown, func: string, callback: (opts: IErrorCatchCallback) => Promise<void>): Promise<void> => {
+ let name = ``;
+ let message = ``;
+ let stack = ``;
+ let url = ``;
+ if (e instanceof Error) {
+ name = e.name;
+ message = e.message;
+ stack = e.stack || ``;
+ url = page.url.pathname;
+ }
+ await callback({ name, message, stack, url, func });
+};
+
+export const handle_err = async (e: unknown, fcall: string): Promise<void> => {
+ try {
+ return void await catch_err(e, fcall, async (opts) => {
+ console.log(`handle_err e `, e)
+ console.log(JSON.stringify(opts, null, 4), `handle_err opts`)
+ });
+ } catch (e) {
+ console.log(`(handle_err) `, e)
+ }
+};
+
+export const callback_route = async <T extends string>(callback_route: CallbackRoute<T>): Promise<void> => {
+ if (`route` in callback_route) {
+ if (typeof callback_route.route === `string`) return void await goto(callback_route.route);
+ else return void await goto(
+ encode_qp_route(
+ callback_route.route[0],
+ callback_route.route[1],
+ ),
+ );
+ }
+ return void await callback_route();
+};
+
+export const query_params_clear = async (): Promise<void> => {
+ page.url && await goto(page.url.pathname, { replaceState: true })
+};
+
+export const lib_fmt_price = (value: string, currency: string): string => {
+ const $locale = get_store(liblocale);
+ return fmt_price($locale, value, currency);
+};
+
+export const lib_parse_currency_marker = (currency: string): string => {
+ const $locale = get_store(liblocale);
+ return parse_currency_marker($locale, currency);
+};
+
+export const lib_fmt_geometry_point_coords = (point: GeometryPoint): string => {
+ const $locale = get_store(liblocale);
+ return fmt_geometry_point_coords(point, $locale);
+};
+
+export const view_effect = <T extends string>(view: T): void => {
+ if (!browser) return;
+ for (const el of document.querySelectorAll(`[data-view]`)) {
+ if (el.getAttribute(`data-view`) !== view) el.classList.add(`hidden`)
+ else el.classList.remove(`hidden`)
+ }
+};
+\ No newline at end of file
diff --git a/apps-lib/src/lib/util/styles.ts b/apps-lib/src/lib/util/styles.ts
@@ -1,42 +0,0 @@
-import type { AppLayoutKey, GeometryGlyphDimension, IToastKind, LoadingDimension } from "$lib";
-
-export const glyph_style_map: Map<GeometryGlyphDimension, { gl_1: number; dim_1?: number; }> = new Map([
- ["xs--", { gl_1: 12 }],
- ["xs-", { gl_1: 12, dim_1: 17 }],
- ["xs", { gl_1: 15, dim_1: 18 }],
- ["xs+", { gl_1: 18, dim_1: 20 }],
- ["sm-", { gl_1: 19, dim_1: 22 }],
- ["sm", { gl_1: 20, dim_1: 24 }],
- ["sm+", { gl_1: 21 }],
- ["md-", { gl_1: 23 }],
- ["md", { gl_1: 24 }],
- ["md+", { gl_1: 26 }],
- ["lg-", { gl_1: 27 }],
- ["lg", { gl_1: 28 }],
- ["xl", { gl_1: 30 }],
- ["xl+", { gl_1: 40 }],
-]);
-
-export const loading_style_map: Map<LoadingDimension, { dim_1: number; gl_2: number }> = new Map([
- ["glyph-send-button", { dim_1: 20, gl_2: 20 }],
- ["xs", { dim_1: 12, gl_2: 12 }],
- ["sm", { dim_1: 16, gl_2: 16 }],
- ["md", { dim_1: 20, gl_2: 20 }],
- ["lg", { dim_1: 28, gl_2: 28 }],
- ["xl", { dim_1: 36, gl_2: 36 }],
-]);
-
-export const toast_layout_map: Map<AppLayoutKey, string> = new Map([
- [`mobile_base`, `pt-8`],
- [`mobile_y`, `pt-16`],
-]);
-
-export const toast_style_map: Map<IToastKind, { inner: string; outer: string }> = new Map([
- [
- `simple`,
- {
- inner: `justify-center`,
- outer: `min-h-toast_min w-full px-4 rounded-2xl shadow-sm`,
- },
- ],
-]);
-\ No newline at end of file
diff --git a/apps-lib/src/lib/util/view.ts b/apps-lib/src/lib/util/view.ts
@@ -1,7 +0,0 @@
-import { casl_init, view_effect } from "$lib";
-
-export const handle_view = <T extends string>(view: T, casl_rec: Record<T, { max_index: number }>): T => {
- casl_init(0, casl_rec[view].max_index);
- view_effect<T>(view);
- return view;
-};
-\ No newline at end of file
diff --git a/apps-lib/src/lib/view/farm-land-add.svelte b/apps-lib/src/lib/view/farm-land-add.svelte
@@ -1,422 +0,0 @@
-<script lang="ts">
- import {
- Carousel,
- casl_dec,
- casl_inc,
- casl_index,
- FloatTabs,
- fmt_id,
- geol_lat_fmt,
- geol_lng_fmt,
- GlyphButtonSimple,
- GlyphTitleSelectLabel,
- handle_err,
- handle_view,
- Input,
- kv_init_page,
- LayoutView,
- lls,
- MapPointSelect,
- PageToolbar,
- SelectMenu,
- type CallbackPromiseReturn,
- type GeocoderReverseResult,
- type GeolocationPoint,
- type ILcGeocodeCallback,
- type ViewBasis,
- } from "$lib";
- import CarouselItem from "$lib/component/carousel/carousel-item.svelte";
- import View from "$lib/component/lib/view.svelte";
- import { onDestroy, onMount } from "svelte";
-
- const casl_rec: Record<View, { max_index: number }> = {
- map_init: {
- max_index: 2,
- },
- loc_form: {
- max_index: 1,
- },
- };
-
- type View = `map_init` | `loc_form`;
- let view: View = `map_init`;
-
- export let bv_map_geol_p: GeolocationPoint | undefined = undefined;
- export let bv_map_geoc_r: GeocoderReverseResult | undefined = undefined;
-
- let lgcs_label = ``;
- let lgcs_area = ``;
- let lgcs_area_unit = `ha`;
- let lgcs_elevation = ``;
- let lgcs_elevation_unit = `m`;
- let lgcs_climate = ``;
-
- export let basis: ViewBasis<{
- lc_geocode: ILcGeocodeCallback;
- lc_submit: CallbackPromiseReturn<{ id: string } | void>;
- }>;
-
- onMount(async () => {
- if (!basis.kv_init_prevent) await kv_init_page();
- if (basis.lc_on_mount) await basis.lc_on_mount();
- handle_view(view, casl_rec);
- });
-
- onDestroy(async () => {
- if (basis.lc_on_destroy) await basis.lc_on_destroy();
- });
-
- const handle_dec = async (): Promise<void> => {
- await casl_dec();
- };
-
- const handle_inc = async (): Promise<void> => {
- await casl_inc();
- };
-
- const submit = async (): Promise<void> => {
- try {
- const result = await basis.lc_submit();
- if (result) view = handle_view(`loc_form`, casl_rec);
- } catch (e) {
- await handle_err(e, `submit`);
- }
- };
-</script>
-
-<LayoutView>
- <PageToolbar
- basis={{
- header: {
- label: `${$lls(`icu.add_*`, { value: `${$lls(`common.farm_land`)}` })}`,
- callback_route: {
- route: `/farm/land`,
- },
- },
- }}
- >
- <div
- slot="header-option"
- class={`flex flex-row gap-4 justify-start items-center`}
- >
- <div
- class={`${$casl_index > 0 ? `fade-in` : `hidden`} flex flex-row justify-start items-center`}
- >
- <GlyphButtonSimple
- basis={{
- classes: `gap-1`,
- kind: `neutral`,
- label: `${$lls(`common.map`)}`,
- glyph: `arrow-left`,
- callback: handle_dec,
- }}
- />
- </div>
- <GlyphButtonSimple
- basis={{
- label: `${$lls(`common.add`)}`,
- callback: async () => {
- if (view === `map_init` && $casl_index === 0) {
- if (!(bv_map_geol_p && bv_map_geoc_r))
- return void (await basis.lc_gui_alert(
- `Please select a location.`, //@todo
- ));
- return void (await handle_inc());
- } else if (view === `map_init` && $casl_index === 1) {
- if (bv_map_geol_p && bv_map_geoc_r)
- return void (await submit());
- }
- },
- }}
- />
- </div>
- </PageToolbar>
- <View key={`map_init`}>
- <Carousel>
- <CarouselItem>
- <div
- class={`flex flex-col w-full px-4 justify-center items-center`}
- >
- <div
- class={`relative flex flex-col w-full justify-start items-center rounded-[20px] overflow-hidden border-x-[3px] border-y-[5px] border-white/30 shadow-sm`}
- >
- <div
- class={`flex flex-row h-[32rem] w-full justify-center items-center bg-base-100/60 overflow-hidden rounded-2xl`}
- >
- <MapPointSelect
- bind:map_point={bv_map_geol_p}
- bind:map_point_geoc_r={bv_map_geoc_r}
- basis={{
- lc_geocode: basis.lc_geocode,
- }}
- />
- </div>
- </div>
- </div>
- </CarouselItem>
- <CarouselItem>
- <div
- class={`flex flex-col w-full pt-4 px-4 gap-4 justify-center items-center`}
- >
- <div
- class={`flex flex-col w-full px-2 gap-4 justify-start items-center`}
- >
- {#if bv_map_geoc_r && bv_map_geol_p}
- <div
- class={`flex flex-col w-full gap-1 justify-start items-start`}
- >
- <div
- class={`flex flex-row w-full justify-start items-center`}
- >
- <p
- class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}
- >
- {`${$lls(`common.location`)}`}
- </p>
- </div>
- <div
- class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`}
- >
- <p
- class={`font-sans font-[400] text-[1.1rem] text-layer-0-glyph`}
- >
- {`${bv_map_geoc_r.name}, ${bv_map_geoc_r.admin1_id}, ${bv_map_geoc_r.country_name}`}
- </p>
- </div>
- </div>
- <div
- class={`flex flex-col w-full gap-1 justify-start items-start`}
- >
- <p
- class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}
- >
- {`${$lls(`common.coordinates`)}`}
- </p>
- <div
- class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`}
- >
- <p
- class={`font-sans font-[400] text-[1.1rem] text-layer-0-glyph`}
- >
- {`${geol_lat_fmt(
- bv_map_geol_p.lat,
- `d`,
- 4,
- )}, ${geol_lng_fmt(
- bv_map_geol_p.lng,
- `d`,
- 4,
- )}`}
- </p>
- </div>
- </div>
- <div
- class={`flex flex-col w-full gap-1 justify-start items-start`}
- >
- <div
- class={`flex flex-row w-full justify-start items-center`}
- >
- <p
- class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}
- >
- {`${$lls(`common.farm`)}/${`${$lls(`common.estate`)}`}`}
- </p>
- </div>
- <div
- class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`}
- >
- <Input
- bind:value={lgcs_label}
- basis={{
- id: fmt_id(`label`),
- sync: true,
- layer: 0,
- classes: `h-10 placeholder:text-[1.1rem]`,
- placeholder: `${$lls(`common.name_of_farm_or_estate`)}`,
- /*field: {
- charset:
- location_gcs_form_fields
- .label.charset,
- validate:
- location_gcs_form_fields
- .label.validation,
- validate_keypress: true,
- },*/
- }}
- />
- </div>
- </div>
- <div
- class={`flex flex-col w-full gap-1 justify-start items-start`}
- >
- <div
- class={`flex flex-row w-full gap-1 justify-start items-center`}
- >
- <p
- class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}
- >
- {`${$lls(`common.area`)}`}
- </p>
- <SelectMenu
- bind:value={lgcs_area_unit}
- basis={{
- layer: 0,
- options: [
- {
- entries: [
- {
- label: `${$lls(`measurement.area.ac`)}`,
- value: `ac`,
- },
- {
- label: `${$lls(`measurement.area.ha`)}`,
- value: `ha`,
- },
- {
- label: `${$lls(`measurement.area.m2`)}`,
- value: `m2`,
- },
- ],
- },
- ],
- }}
- >
- <svelte:fragment slot="element">
- <GlyphTitleSelectLabel
- basis={{
- label: `${$lls(`measurement.area.${lgcs_area_unit}_ab`)}`,
- }}
- />
- </svelte:fragment>
- </SelectMenu>
- </div>
- <div
- class={`relative flex flex-row h-12 w-full justify-between items-center border-y-line border-layer-0-surface-edge`}
- >
- <Input
- bind:value={lgcs_area}
- basis={{
- id: fmt_id(`area`),
- sync: true,
- layer: 0,
- classes: `h-10 placeholder:text-[1.1rem]`,
- placeholder: `${$lls(`common.land_area`)}`,
- /*field: {
- charset: regex.num,
- validate: regex.num,
- validate_keypress: true,
- },*/
- }}
- />
- </div>
- </div>
- <div
- class={`flex flex-col w-full gap-1 justify-start items-start`}
- >
- <div
- class={`flex flex-row w-full gap-1 justify-start items-center`}
- >
- <p
- class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}
- >
- {`${$lls(`common.elevation`)}`}
- </p>
- <SelectMenu
- bind:value={lgcs_elevation_unit}
- basis={{
- layer: 0,
- options: [
- {
- entries: [
- {
- label: `${$lls(`measurement.length.m`)}`,
- value: `m`,
- },
- {
- label: `${$lls(`measurement.length.ft`)}`,
- value: `ft`,
- },
- ],
- },
- ],
- }}
- >
- <svelte:fragment slot="element">
- <GlyphTitleSelectLabel
- basis={{
- label: `${$lls(`measurement.length.${lgcs_elevation_unit}_ab`)}`,
- }}
- />
- </svelte:fragment>
- </SelectMenu>
- </div>
- <div
- class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`}
- >
- <Input
- bind:value={lgcs_elevation}
- basis={{
- id: fmt_id(`elevation`),
- sync: true,
- layer: 0,
- classes: `h-10 placeholder:text-[1.1rem]`,
- placeholder: `${$lls(`common.elevation`)}`,
- /*field: {
- charset: regex.num,
- validate: regex.num,
- validate_keypress: true,
- },*/
- }}
- />
- </div>
- </div>
- <div
- class={`flex flex-col w-full gap-1 justify-start items-start`}
- >
- <div
- class={`flex flex-row w-full justify-start items-center`}
- >
- <p
- class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}
- >
- {`${$lls(`common.climate`)}`}
- </p>
- </div>
- <div
- class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`}
- >
- <Input
- bind:value={lgcs_climate}
- basis={{
- id: fmt_id(`climate`),
- sync: true,
- layer: 0,
- classes: `h-10 placeholder:text-[1.1rem]`,
- placeholder: `${$lls(`common.climate`)}`,
- /*field: {
- charset: regex.description,
- validate: regex.description_ch,
- validate_keypress: true,
- },*/
- }}
- />
- </div>
- </div>
- {/if}
- </div>
- </div>
- </CarouselItem>
- </Carousel>
- </View>
- <View key={`loc_form`}>
- <button
- class={`flex flex-row justify-center items-center`}
- on:click={async () => {
- view = handle_view(`map_init`, casl_rec);
- }}
- >
- <p class={`font-sans font-[400] text-layer-0-glyph`}>{`two`}</p>
- </button>
- </View>
-</LayoutView>
-<FloatTabs />
diff --git a/apps-lib/src/lib/view/farm-land-view.svelte b/apps-lib/src/lib/view/farm-land-view.svelte
@@ -1,82 +0,0 @@
-<script lang="ts">
- import {
- app_notify,
- GlyphButtonSimple,
- kv_init_page,
- LayoutView,
- lls,
- MapPointDisplay,
- PageToolbar,
- qp_id,
- type IFarmViewLoadData,
- type ViewBasisLoadData,
- } from "$lib";
- import { onDestroy, onMount } from "svelte";
- type LoadData = IFarmViewLoadData;
-
- export let basis: ViewBasisLoadData<{}, LoadData>;
- let load_data: LoadData = undefined;
-
- onMount(async () => {
- if (!basis.kv_init_prevent) await kv_init_page();
- if (basis.lc_on_mount) await basis.lc_on_mount();
- if (!$qp_id)
- return void app_notify.set(
- `${$lls(`error.page.load.query_param`)}`,
- );
- load_data = await basis.lc_load_data();
- });
-
- onDestroy(async () => {
- if (basis.lc_on_destroy) await basis.lc_on_destroy();
- });
-</script>
-
-<LayoutView>
- <PageToolbar
- basis={{
- header: {
- label: `${$lls(`icu.add_*`, { value: `${$lls(`common.farm_land`)}` })}`,
- callback_route: {
- route: `/farm/land`,
- },
- },
- }}
- >
- <div
- slot="header-option"
- class={`flex flex-row gap-4 justify-start items-center`}
- >
- <GlyphButtonSimple
- basis={{
- label: `${$lls(`common.edit`)}`,
- callback: async () => {
- alert(`@todo!`);
- },
- }}
- />
- </div>
- </PageToolbar>
- {#if load_data?.location_gcs}
- <div class={`flex flex-col w-full px-4 justify-center items-center`}>
- <div
- class={`flex flex-row h-[20rem] w-full justify-center items-center bg-layer-2-surface round-44 overflow-hidden`}
- >
- <MapPointDisplay
- basis={{
- zoom: 12,
- point: {
- lat: load_data.location_gcs.lat,
- lng: load_data.location_gcs.lng,
- },
- }}
- />
- </div>
- <div class={`flex flex-col w-full justify-center items-center`}>
- <div class={`flex flex-row w-full justify-start items-center`}>
- <p class={`font-sans font-[400] text-layer-0-glyph`}>hi</p>
- </div>
- </div>
- </div>
- {/if}
-</LayoutView>
diff --git a/apps-lib/src/lib/view/farm-land.svelte b/apps-lib/src/lib/view/farm-land.svelte
@@ -1,141 +0,0 @@
-<script lang="ts">
- import {
- Fade,
- FloatTabs,
- geol_lat_fmt,
- geol_lng_fmt,
- GlyphButtonSimple,
- kv_init_page,
- LayoutView,
- lls,
- MapPointDisplay,
- PageToolbar,
- type CallbackPromise,
- type IFarmLoadData,
- type ViewBasisLoadData,
- } from "$lib";
- import { onDestroy, onMount } from "svelte";
- type LoadData = IFarmLoadData;
-
- export let basis: ViewBasisLoadData<
- {
- lc_handle_farm_land_add: CallbackPromise;
- lc_handle_farm_land_view: CallbackPromise;
- },
- LoadData
- >;
- let load_data: LoadData = undefined;
-
- onMount(async () => {
- if (!basis.kv_init_prevent) await kv_init_page();
- if (basis.lc_on_mount) await basis.lc_on_mount();
- load_data = await basis.lc_load_data();
- });
-
- onDestroy(async () => {
- if (basis.lc_on_destroy) await basis.lc_on_destroy();
- });
-</script>
-
-<LayoutView>
- <PageToolbar
- basis={{
- header: { label: `${$lls(`common.farm_land`)}` },
- }}
- >
- <div
- slot="header-option"
- class={`flex flex-row justify-start items-center`}
- >
- <Fade>
- <GlyphButtonSimple
- basis={{
- label: `${$lls(`common.add`)}`,
- callback: basis.lc_handle_farm_land_add,
- }}
- ></GlyphButtonSimple>
- </Fade>
- </div>
- </PageToolbar>
- <div
- class={`flex flex-col w-full pt-2 px-4 gap-5 justify-start items-center`}
- >
- {#if load_data && load_data.location_gcs.length}
- {#each load_data.location_gcs.filter((i) => i.kind === `farm_land`) as li}
- <button
- class={`group flex flex-row h-[5rem] w-full px-8 gap-8 justify-start items-center bg-layer-1-surface layer-1-active-surface round-36 el-re`}
- on:click={basis.lc_handle_farm_land_view}
- >
- <div
- class={`flex flex-col h-[4rem] w-[4rem] justify-start items-center bg-layer-2-surface round-24`}
- >
- <MapPointDisplay
- basis={{
- point: {
- lat: li.lat,
- lng: li.lng,
- },
- }}
- />
- </div>
- <div
- class={`flex flex-col flex-grow h-[3.25rem] justify-between items-start`}
- >
- <div
- class={`flex flex-row w-full justify-start items-center`}
- >
- <p
- class={`font-sans font-[500] text-layer-0-glyph`}
- >
- {`${
- li.label ||
- `${geol_lat_fmt(
- li.lat,
- `d`,
- 4,
- )}, ${geol_lng_fmt(li.lng, `d`, 4)}`
- }`}
- </p>
- </div>
- <div
- class={`flex flex-row w-full gap-2 justify-start items-center`}
- >
- {#if li.kind === `farm_land`}
- <div
- class={`flex flex-row h-5 px-2 justify-center items-center bg-layer-2-surface rounded-md`}
- >
- <p
- class={`font-sans font-[700] text-[0.8rem] text-white`}
- >
- {`${$lls(`common.farm`)}`}
- </p>
- </div>
- {/if}
- <p
- class={`font-sansd font-[500] text-layer-0-glyph`}
- >
- {`${li.gc_name}, ${li.gc_admin1_id}, ${li.gc_country_id}`}
- </p>
- </div>
- </div>
- </button>
- {/each}
- {:else}
- <div class={`flex flex-col w-full justify-center items-center`}>
- <div class={`flex flex-row w-full justify-center items-center`}>
- <button
- class={`flex flex-row justify-center items-center`}
- on:click={basis.lc_handle_farm_land_add}
- >
- <p
- class={`font-sans font-[400] text-lg text-layer-0-glyph`}
- >
- {`- Add land -`}
- </p>
- </button>
- </div>
- </div>
- {/if}
- </div>
-</LayoutView>
-<FloatTabs />
diff --git a/apps-lib/src/lib/view/farms-add.svelte b/apps-lib/src/lib/view/farms-add.svelte
@@ -0,0 +1,165 @@
+<script lang="ts">
+ import {
+ ButtonLayoutPair,
+ Carousel,
+ casl_dec,
+ casl_i,
+ casl_inc,
+ casl_init,
+ FarmsAddCasliDetail,
+ FarmsAddCasliMap,
+ handle_err,
+ LayoutView,
+ liblocale,
+ lls,
+ PageToolbar,
+ } from "$root";
+ import {
+ geol_lat_fmt,
+ geol_lng_fmt,
+ parse_float,
+ parse_geocode_address,
+ vs_view_farms_add_submission,
+ type CallbackPromiseGeneric,
+ type CallbackRoute,
+ type GeocoderReverseResult,
+ type GeolocationAddress,
+ type GeolocationPoint,
+ type IViewFarmsAddSubmission,
+ type LcGeocodeCallback,
+ type LcGeocodeCurrentCallback,
+ type LcGuiAlertCallback,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let {
+ basis,
+ }: {
+ basis: {
+ callback_route?: CallbackRoute<string>;
+ lc_gui_alert: LcGuiAlertCallback;
+ lc_geop_current: LcGeocodeCurrentCallback;
+ lc_geocode: LcGeocodeCallback;
+ lc_submit: CallbackPromiseGeneric<{
+ data_s: IViewFarmsAddSubmission;
+ }>;
+ };
+ } = $props();
+
+ let loading = $state(false);
+
+ let map_geop: GeolocationPoint = $state({ lat: 0, lng: 0 });
+ let map_geoc: GeocoderReverseResult | undefined = $state(undefined);
+
+ let val_farmname = $state(``);
+ let val_farmcontact = $state(``);
+ let val_farmarea = $state(``);
+ let val_farmarea_unit = $state(`ac`);
+
+ const disabled_submit = $derived($casl_i === 1 && !val_farmname);
+
+ onMount(async () => {
+ try {
+ casl_init(0, 2);
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
+ });
+
+ const farm_geop_lat = $derived(
+ map_geop ? geol_lat_fmt(map_geop.lat, `dms`, $liblocale, 3) : ``,
+ );
+
+ const farm_geop_lng = $derived(
+ map_geop ? geol_lng_fmt(map_geop.lng, `dms`, $liblocale, 3) : ``,
+ );
+
+ const farm_geolocation_address: GeolocationAddress | undefined = $derived(
+ parse_geocode_address(map_geoc),
+ );
+
+ const handle_continue_1 = async (): Promise<void> => {
+ if (!map_geop) return; //@todo
+ if (!farm_geolocation_address) return; //@todo
+
+ const vp_obj_submit = vs_view_farms_add_submission.safeParse({
+ farm_name: val_farmname,
+ farm_area: parse_float(val_farmarea),
+ farm_area_unit: val_farmarea_unit,
+ farm_contact_name: val_farmcontact,
+ geolocation_point: map_geop,
+ geolocation_address: farm_geolocation_address,
+ } satisfies IViewFarmsAddSubmission);
+
+ if (!vp_obj_submit.success) {
+ return void basis.lc_gui_alert(
+ `Please correct the following errors: ${vp_obj_submit.error}`,
+ );
+ }
+ loading = true;
+ await basis.lc_submit({ data_s: vp_obj_submit.data });
+ loading = false;
+ };
+
+ const handle_continue = async (): Promise<void> => {
+ switch ($casl_i) {
+ case 1:
+ return handle_continue_1();
+ default:
+ return await casl_inc();
+ }
+ };
+
+ const handle_back = async (): Promise<void> => {
+ await casl_dec();
+ };
+</script>
+
+<LayoutView>
+ <PageToolbar
+ basis={{
+ header: {
+ label: `Farm / Add`,
+ callback_route: basis.callback_route,
+ },
+ }}
+ />
+ <Carousel>
+ <FarmsAddCasliMap
+ bind:map_geop
+ bind:map_geoc
+ {farm_geop_lat}
+ {farm_geop_lng}
+ basis={{
+ lc_geocode: basis.lc_geocode,
+ lc_geop_current: basis.lc_geop_current,
+ }}
+ />
+ <FarmsAddCasliDetail
+ bind:val_farmname
+ bind:val_farmcontact
+ bind:val_farmarea
+ bind:val_farmarea_unit
+ {farm_geop_lat}
+ {farm_geop_lng}
+ />
+ </Carousel>
+ <div
+ class="absolute bottom-4 left-0 flex flex-row w-full gap-1 justify-center items-center"
+ >
+ <ButtonLayoutPair
+ basis={{
+ continue: {
+ label: `${$lls(`common.continue`)}`,
+ disabled: disabled_submit,
+ callback: handle_continue,
+ },
+ back: {
+ label: `${$lls(`common.back`)}`,
+ visible: $casl_i > 0,
+ callback: handle_back,
+ },
+ }}
+ />
+ </div>
+</LayoutView>
diff --git a/apps-lib/src/lib/view/farms-products-add.svelte b/apps-lib/src/lib/view/farms-products-add.svelte
@@ -0,0 +1,648 @@
+<script lang="ts">
+ import {
+ app_lo,
+ ButtonHorizontalPairSlide,
+ ButtonRound,
+ Carousel,
+ CarouselItem,
+ casl_i,
+ casl_imax,
+ casl_inc,
+ casl_init,
+ FarmsProductsReviewCard,
+ fmt_id,
+ FormEntryPrice,
+ FormEntryQuantity,
+ FormEntrySelect,
+ FormEntrySelectInput,
+ FormEntryTextarea,
+ handle_err,
+ idb,
+ ImageUploadSimple,
+ LayoutBottomButton,
+ LayoutView,
+ lib_fmt_geometry_point_coords,
+ lls,
+ MapLocationSelectEnvelope,
+ MarkerIndexedView,
+ PageToolbar,
+ } from "$root";
+ import {
+ el_id,
+ fmt_geocode_address,
+ fmt_geolocation_address,
+ form_fields,
+ obj_keys_maxnum,
+ parse_float,
+ parse_geocode_address,
+ parse_geolocation_address,
+ parse_geolocation_point,
+ parse_geop_point,
+ parse_int,
+ parse_trade_key,
+ sleep,
+ str_trunc,
+ trade,
+ trade_keys,
+ vs_view_farms_products_add_submission,
+ type CallbackPromiseFull,
+ type CallbackPromiseGeneric,
+ type CallbackRoute,
+ type GeocoderReverseResult,
+ type GeolocationAddress,
+ type GeolocationPoint,
+ type ISelectOption,
+ type IViewBasis,
+ type IViewFarmsProductsAddSubmission,
+ type LcGeocodeCallback,
+ type LcGeocodeCurrentCallback,
+ type LcGuiAlertCallback,
+ type LcGuiConfirmCallback,
+ type LcPhotoAddMultipleCallback,
+ type ResolveFarmInfo,
+ type ResolveGeolocationInfo,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ const casl_param: Record<
+ "default",
+ {
+ index_map: Record<number, { label_desc: string }>;
+ }
+ > = {
+ default: {
+ index_map: {
+ 0: {
+ label_desc: `Add product information`,
+ },
+ 1: {
+ label_desc: `Upload images`,
+ },
+ 2: {
+ label_desc: `Product Price`,
+ },
+ 3: {
+ label_desc: `Add Location`,
+ },
+ 4: {
+ label_desc: `Review`,
+ },
+ },
+ },
+ };
+
+ let {
+ basis,
+ }: {
+ basis: IViewBasis<{
+ data: ResolveFarmInfo;
+ callback_route?: CallbackRoute<string>;
+ lc_gui_alert: LcGuiAlertCallback;
+ lc_gui_confirm: LcGuiConfirmCallback;
+ lc_geocode: LcGeocodeCallback;
+ lc_handle_farm_lot_add: CallbackPromiseGeneric<string>;
+ lc_handle_photo_add: LcPhotoAddMultipleCallback;
+ lc_handle_photo_envelope_edit: CallbackPromiseGeneric<number>;
+ lc_img_bin: CallbackPromiseFull<string, Uint8Array | undefined>;
+ lc_handle_tradepr_key_toggle: CallbackPromiseFull<boolean, string>;
+ lc_geop_current: LcGeocodeCurrentCallback;
+ lc_submit: CallbackPromiseGeneric<{
+ data_s: IViewFarmsProductsAddSubmission;
+ farm_id: string;
+ farm_lot_id?: string;
+ geolocation_id?: string;
+ }>;
+ }>;
+ } = $props();
+
+ let loading_submit = $state(false);
+
+ let data_s: IViewFarmsProductsAddSubmission | undefined = $state(undefined);
+
+ let photo_paths = $state([]);
+
+ let toggle_opt_addexisting = $state(false);
+
+ let product_key_sel = $state(`cacao`);
+ let product_key_sel_input = $state(``);
+ let product_key_sel_toggle = $state(false);
+
+ let product_process_sel = $state(`raw`);
+ let product_process_sel_input = $state(``);
+ let product_process_sel_toggle = $state(false);
+
+ let product_description_input = $state(`cool description`);
+
+ let product_location_sel = $state(``);
+ let product_location_map_toggle = $state(false);
+
+ let product_price_cur_sel = $state(`usd`);
+ let product_price_input = $state(`4.50`);
+ let product_price_qty_unit_sel = $state(`lb`);
+
+ let product_quantity_input = $state(`60`);
+ let product_quantity_unit_sel = $state(`kg`);
+ let product_quantity_label_sel = $state(`bag`);
+
+ let tradepr_lgc_map_point: GeolocationPoint | undefined = $state(undefined);
+ let tradepr_lgc_map_geoc: GeocoderReverseResult | undefined =
+ $state(undefined);
+
+ const tradepr_key_parsed = $derived(parse_trade_key(product_key_sel));
+
+ const product_process_list = $derived(
+ tradepr_key_parsed ? trade.key[tradepr_key_parsed].process : [],
+ );
+
+ const list_farm_geolocations: ResolveGeolocationInfo[] = $derived(
+ [
+ basis.data?.geolocation ? basis.data?.geolocation : undefined,
+ ...(basis.data?.farm_lots?.length
+ ? [...basis.data?.farm_lots.map((i) => i.geolocation)]
+ : []),
+ ].filter((i) => typeof i !== `undefined`),
+ );
+
+ const product_farm_lot_id: string | undefined = $derived(
+ product_location_sel === `*map-location` ||
+ !basis.data?.farm_lots?.length
+ ? undefined
+ : basis.data?.farm_lots.find(
+ (i) => i.geolocation.id === product_location_sel,
+ )?.id || undefined,
+ );
+
+ const product_geolocation_id: string | undefined = $derived(
+ product_location_sel === `*map-location`
+ ? undefined
+ : list_farm_geolocations.find((i) => i.id === product_location_sel)
+ ?.id || undefined,
+ );
+
+ const product_geolocation: ResolveGeolocationInfo | undefined = $derived(
+ list_farm_geolocations.find((i) => i.id === product_location_sel) ||
+ undefined,
+ );
+
+ const product_geolocation_point: GeolocationPoint | undefined = $derived(
+ product_location_sel === `*map-location`
+ ? tradepr_lgc_map_point
+ : parse_geolocation_point(product_geolocation?.point) || undefined,
+ );
+
+ const product_geolocation_address: GeolocationAddress | undefined =
+ $derived(
+ product_location_sel === `*map-location`
+ ? parse_geocode_address(tradepr_lgc_map_geoc)
+ : parse_geolocation_address(product_geolocation?.address) ||
+ undefined,
+ );
+
+ const entries_farm_location: ISelectOption<string>[] = $derived(
+ basis.data?.geolocation
+ ? [
+ {
+ value: basis.data.geolocation.id,
+ label:
+ fmt_geolocation_address(
+ basis.data.geolocation.address,
+ ) ||
+ lib_fmt_geometry_point_coords(
+ basis.data.geolocation.point,
+ ) ||
+ ``,
+ },
+ ]
+ : [],
+ );
+ const entries_farm_lots_locations: ISelectOption<string>[] = $derived(
+ basis.data?.farm_lots?.length
+ ? basis.data.farm_lots
+ .filter(
+ (i) =>
+ i.geolocation.address &&
+ !entries_farm_location
+ .map((i) => i.value)
+ .includes(i.geolocation.id),
+ )
+ .map(({ geolocation }) => ({
+ value: geolocation.id,
+ label: `${geolocation.address.primary}, ${geolocation.address.admin}, ${geolocation.address.country}`,
+ }))
+ : [],
+ );
+
+ onMount(async () => {
+ try {
+ casl_init(0, obj_keys_maxnum(casl_param.default.index_map));
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
+ });
+
+ const handle_sel_key = async (value: string): Promise<void> => {
+ el_id(fmt_id(`key_wrap`))?.classList.remove(`layer-1-ring-apply`);
+ if (value === `*other`) {
+ await handle_tradepr_key_toggle(true);
+ }
+ };
+
+ const handle_tradepr_key_toggle = async (
+ input_visible: boolean,
+ ): Promise<void> => {
+ product_key_sel_toggle = input_visible;
+ if (input_visible) {
+ product_key_sel_input = ``;
+ } else {
+ product_key_sel = ``;
+ product_key_sel_input = ``;
+ }
+ };
+
+ const handle_sel_process = async (value: string): Promise<void> => {
+ el_id(fmt_id(`process_wrap`))?.classList.remove(`layer-1-ring-apply`);
+ if (value === `*choose-product`) {
+ product_process_sel = ``;
+ el_id(fmt_id(`key_wrap`))?.classList.add(`layer-1-ring-apply`);
+ await sleep(1000);
+ el_id(fmt_id(`key_wrap`))?.classList.remove(`layer-1-ring-apply`);
+ }
+ };
+
+ const handle_product_process_toggle = async (
+ input_visible: boolean,
+ ): Promise<void> => {
+ product_process_sel_toggle = input_visible;
+ if (input_visible) {
+ product_process_sel = ``;
+ product_process_sel_input = ``;
+ }
+ };
+
+ const handle_product_location_sel_map = async (
+ value: string,
+ ): Promise<void> => {
+ el_id(fmt_id(`location_wrap`))?.classList.remove(`layer-1-ring-apply`);
+ if (value === `*map`) {
+ if (tradepr_lgc_map_point) tradepr_lgc_map_point = undefined;
+ if (tradepr_lgc_map_geoc) tradepr_lgc_map_geoc = undefined;
+ const geop = await basis.lc_geop_current();
+ if (!geop) {
+ return void (await basis.lc_gui_alert(
+ `${$lls(`icu.failure_*`, { value: `${$lls(`icu.reading_*`, { value: `${$lls(`common.geocode`)}`.toLowerCase() })}` })}`,
+ ));
+ }
+ const geoc = await basis.lc_geocode(geop);
+ if (geoc) tradepr_lgc_map_geoc = geoc;
+ tradepr_lgc_map_point = parse_geop_point(geop);
+ product_location_map_toggle = true;
+ await sleep(1000);
+ product_location_sel = ``;
+ }
+ };
+
+ const continue_0 = async (): Promise<void> => {
+ for (const kv_id of [`key`, `process`, `description`]) {
+ const val = await idb.get(fmt_id(kv_id));
+ if (!val)
+ return void basis.lc_gui_alert(
+ `${$lls(`farm.product.validation.${kv_id}.required`)}`,
+ );
+ }
+ await casl_inc();
+ };
+
+ const continue_1 = async (): Promise<void> => {
+ if (!photo_paths.length)
+ return void basis.lc_gui_alert(
+ `Upload a product photo`, //@todo
+ );
+ await casl_inc();
+ };
+
+ const continue_2 = async (): Promise<void> => {
+ await casl_inc();
+ };
+
+ const continue_3 = async (): Promise<void> => {
+ if (!product_geolocation_point || !product_geolocation_address)
+ return void (await basis.lc_gui_alert(
+ `Select the product location`, //@todo
+ ));
+ const vp_data_s = vs_view_farms_products_add_submission.safeParse({
+ product: product_key_sel_input || product_key_sel,
+ process: product_process_sel_input || product_process_sel,
+ description: product_description_input,
+ price_amount: parse_float(product_price_input, 1.0),
+ price_currency: product_price_cur_sel,
+ price_quantity_unit: product_price_qty_unit_sel,
+ quantity_amount: parse_int(product_quantity_input, 1),
+ quantity_unit: product_quantity_unit_sel,
+ quantity_label: product_quantity_label_sel,
+ photos: photo_paths,
+ geolocation_point: product_geolocation_point,
+ geolocation_address: product_geolocation_address,
+ } satisfies IViewFarmsProductsAddSubmission);
+ if (!vp_data_s.success) {
+ return void basis.lc_gui_alert(
+ `Please correct the following errors: ${vp_data_s.error}`,
+ );
+ }
+ data_s = vp_data_s.data;
+ await casl_inc();
+ };
+
+ const continue_4 = async (): Promise<void> => {
+ if (!data_s) return;
+ loading_submit = true;
+ await basis.lc_submit({
+ data_s,
+ farm_id: basis.data.id,
+ farm_lot_id: product_farm_lot_id,
+ geolocation_id: product_geolocation_id,
+ });
+ loading_submit = false;
+ };
+
+ const handle_continue = async (num_i?: number): Promise<void> => {
+ try {
+ const num = num_i ?? $casl_i;
+ switch (num) {
+ case 0:
+ return await continue_0();
+ case 1:
+ return await continue_1();
+ case 2:
+ return await continue_2();
+ case 3:
+ return await continue_3();
+ case 4:
+ return await continue_4();
+ }
+ } catch (e) {
+ await handle_err(e, `handle_continue`);
+ }
+ };
+</script>
+
+<LayoutView basis={{ classes: `pb-h_lo_bottom_button_${$app_lo}` }}>
+ <PageToolbar
+ basis={{
+ header: {
+ label: `${`${$lls(`common.farm`)}`} / ${`${$lls(`common.product`)}`}`,
+ callback_route: basis.callback_route || { route: `/farms` },
+ },
+ }}
+ />
+ <div class={`flex flex-col w-full pt-4 px-4 justify-center items-center`}>
+ <div class={`flex flex-row w-full gap-2 justify-center items-center`}>
+ <ButtonHorizontalPairSlide
+ bind:toggle={toggle_opt_addexisting}
+ basis={{
+ label_l: `${$lls(`common.create_product`)}`,
+ label_r: `${$lls(`common.add_from_existing`)}`,
+ }}
+ />
+ </div>
+ </div>
+ {#if !toggle_opt_addexisting}
+ <LayoutBottomButton>
+ <ButtonRound
+ basis={{
+ classes: `text-layer-0-glyph bg-layer-1-surface active:bg-layer-1-surface_a`,
+ loading: loading_submit,
+ label:
+ $casl_i === $casl_imax
+ ? `${$lls(`common.post`)}`
+ : `${$lls(`common.continue`)}`,
+ callback: async () => {
+ handle_continue();
+ },
+ }}
+ />
+ </LayoutBottomButton>
+ <div
+ class={`flex flex-col w-full p-4 gap-4 justify-center items-center`}
+ >
+ <div class={`flex flex-row h-2 w-full justify-start items-center`}>
+ <p
+ class={`font-sans font-[500] text-sm text-layer-0-glyph/60 capitalize`}
+ >
+ {casl_param.default.index_map[$casl_i]?.label_desc}
+ </p>
+ </div>
+ <MarkerIndexedView
+ basis={{
+ index_max: $casl_imax + 1,
+ index_curr: $casl_i,
+ callback_index: async (index) => {
+ casl_i.set(index);
+ },
+ }}
+ />
+ </div>
+
+ <Carousel>
+ <CarouselItem basis={{ classes: `gap-4` }}>
+ <FormEntrySelectInput
+ bind:val_sel={product_key_sel}
+ bind:val_sel_input={product_key_sel_input}
+ basis={{
+ id: `key`,
+ entry_label: `${$lls(`common.product`)}`,
+ visible_input: product_key_sel_toggle,
+ input_placeholder: `${$lls(`icu.enter_the_*`, { value: `${$lls(`icu.*_name`, { value: `${$lls(`common.product`)}` })}`.toLowerCase() })}`,
+ input_field: form_fields.product_key,
+ callback_visible: handle_tradepr_key_toggle,
+ callback_select: async ({ value }) =>
+ await handle_sel_key(value),
+ select_entries: [
+ {
+ value: ``,
+ label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.product`)}`.toLowerCase() })}`,
+ disabled: true,
+ },
+ ...trade_keys.map((i) => ({
+ value: i,
+ label: `${$lls(`trade.product.key.${i}.name`)}`,
+ })),
+ {
+ value: `*other`,
+ label: `${$lls(`common.other`)}`,
+ },
+ ],
+ }}
+ />
+ <FormEntrySelectInput
+ bind:val_sel={product_process_sel}
+ bind:val_sel_input={product_process_sel_input}
+ basis={{
+ id: `process`,
+ entry_label: `${$lls(`common.process`)}`,
+ visible_input: product_process_sel_toggle,
+ input_placeholder: `${$lls(`icu.enter_the_*`, { value: `${$lls(`common.process`)}`.toLowerCase() })}`,
+ input_field: form_fields.product_process,
+ callback_visible: handle_product_process_toggle,
+ callback_select: async ({ value }) =>
+ await handle_sel_process(value),
+ select_entries: product_process_list.length
+ ? [
+ {
+ value: ``,
+ label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.process`)}`.toLowerCase() })}`,
+ disabled: true,
+ },
+ ...product_process_list.map((i) => ({
+ value: i,
+ label: `${$lls(`trade.product.key.${tradepr_key_parsed}.process.${i}`)}`,
+ })),
+ {
+ value: `*other`,
+ label: `${$lls(`common.other`)}`,
+ },
+ ]
+ : [
+ {
+ value: ``,
+ label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.process`)}`.toLowerCase() })}`,
+ disabled: true,
+ },
+ {
+ value: `*choose-product`,
+ label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.product`)}`.toLowerCase() })}`,
+ },
+ ].concat(
+ product_key_sel === `*other`
+ ? [
+ ...trade.default.process.map(
+ (i) => ({
+ value: i,
+ label: `${$lls(`trade.product.default.process.${i}`)}`,
+ }),
+ ),
+ {
+ value: `*other`,
+ label: `${$lls(`common.other`)}`,
+ },
+ ]
+ : [],
+ ),
+ }}
+ />
+ <FormEntryTextarea
+ bind:val={product_description_input}
+ basis={{
+ id: `description`,
+ entry_label: `${$lls(`common.description`)}`,
+ field: form_fields.product_description,
+ placeholder: `${$lls(`common.describe_your_product`)}`,
+ }}
+ />
+ </CarouselItem>
+ <CarouselItem basis={{ classes: `px-4` }}>
+ <ImageUploadSimple
+ bind:photo_paths
+ basis={{
+ lc_handle_photo_add: basis.lc_handle_photo_add,
+ }}
+ />
+ </CarouselItem>
+ <CarouselItem basis={{ classes: `gap-4` }}>
+ <FormEntryPrice
+ bind:val_input_price={product_price_input}
+ bind:val_sel_currency={product_price_cur_sel}
+ bind:val_sel_quantity_unit={product_price_qty_unit_sel}
+ basis={{
+ id: `price`,
+ entry_label: `price`,
+ input_placeholder: `Enter price`,
+ }}
+ />
+ <FormEntryQuantity
+ bind:val_input_quantity={product_quantity_input}
+ bind:val_sel_quantity_unit={product_quantity_unit_sel}
+ bind:val_sel_quantity_label={product_quantity_label_sel}
+ basis={{
+ id: `quantity`,
+ entry_label: `quantity`,
+ input_placeholder: `Enter quantity`,
+ }}
+ />
+ </CarouselItem>
+ <CarouselItem>
+ <FormEntrySelect
+ bind:val={product_location_sel}
+ basis={{
+ id: `location`,
+ entry_label: `${$lls(`common.location`)}`,
+ callback: async ({ value }) =>
+ await handle_product_location_sel_map(value),
+ entries: tradepr_lgc_map_geoc
+ ? [
+ {
+ value: ``,
+ label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.location`)}`.toLowerCase() })}`,
+ disabled: true,
+ },
+ {
+ value: `*map`,
+ label: `${$lls(`icu.choose_on_*`, { value: `${$lls(`common.map`)}`.toLowerCase() })}`,
+ },
+ {
+ value: `*map-location`,
+ label: `${str_trunc(fmt_geocode_address(tradepr_lgc_map_geoc))} (map)`,
+ },
+ ...entries_farm_location,
+ ...entries_farm_lots_locations,
+ ]
+ : [
+ {
+ value: ``,
+ label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.location`)}`.toLowerCase() })}`,
+ disabled: true,
+ },
+ ...entries_farm_location,
+ ...entries_farm_lots_locations,
+ {
+ value: `*map`,
+ label: `${$lls(`icu.choose_on_*`, { value: `${$lls(`common.map`)}`.toLowerCase() })}`,
+ },
+ ],
+ }}
+ />
+ </CarouselItem>
+ <CarouselItem>
+ <div class={`flex flex-col w-full justify-center items-center`}>
+ <FarmsProductsReviewCard
+ basis={{
+ data: data_s,
+ }}
+ />
+ </div>
+ </CarouselItem>
+ </Carousel>
+ {:else}
+ <p class={`font-sans font-[400] text-layer-0-glyph`}>
+ {`Coming soon!`}
+ </p>
+ {/if}
+</LayoutView>
+<MapLocationSelectEnvelope
+ bind:map_geop={tradepr_lgc_map_point}
+ bind:map_geoc={tradepr_lgc_map_geoc}
+ basis={{
+ visible: product_location_map_toggle,
+ farm_info: basis.data,
+ lc_geocode: basis.lc_geocode,
+ lc_submit: async ({ map_geoc, map_geop }) => {
+ try {
+ if (map_geoc && map_geop)
+ product_location_sel = `*map-location`;
+ else product_location_sel = ``;
+ product_location_map_toggle = false;
+ } catch (e) {
+ await handle_err(e, `lc_submit`);
+ }
+ },
+ }}
+/>
diff --git a/apps-lib/src/lib/view/farms-products-view.svelte b/apps-lib/src/lib/view/farms-products-view.svelte
@@ -0,0 +1,54 @@
+<script lang="ts">
+ import {
+ ButtonLabelDashed,
+ LayoutPage,
+ LayoutView,
+ PageToolbar,
+ lls,
+ } from "$root";
+ import {
+ type CallbackPromise,
+ type CallbackRoute,
+ type IViewBasis,
+ type ResolveFarmProductInfo,
+ } from "@radroots/util";
+
+ let {
+ basis,
+ }: {
+ basis: IViewBasis<{
+ data: ResolveFarmProductInfo[];
+ callback_route?: CallbackRoute<string>;
+ lc_handle_farm_product_add: CallbackPromise;
+ }>;
+ } = $props();
+</script>
+
+<LayoutView>
+ <PageToolbar
+ basis={{
+ header: {
+ label: `${$lls(`common.products`)}`,
+ callback_route: basis.callback_route || { route: `/farms` },
+ },
+ }}
+ />
+ <LayoutPage>
+ {#if basis.data?.length}
+ {#each basis.data as li}
+ <div class={`flex flex-row justify-start items-center`}>
+ {JSON.stringify(li)}
+ </div>
+ {/each}
+ {:else}
+ <ButtonLabelDashed
+ basis={{
+ label: `Add product`,
+ callback: async () => {
+ await basis.lc_handle_farm_product_add();
+ },
+ }}
+ />
+ {/if}
+ </LayoutPage>
+</LayoutView>
diff --git a/apps-lib/src/lib/view/farms-view.svelte b/apps-lib/src/lib/view/farms-view.svelte
@@ -0,0 +1,264 @@
+<script lang="ts">
+ import Empty from "$lib/components/lib/empty.svelte";
+ import {
+ ButtonSimple,
+ Glyph,
+ handle_err,
+ LayoutPage,
+ LayoutView,
+ liblocale,
+ lls,
+ Map,
+ MapMarkerArea,
+ PageToolbar,
+ } from "$root";
+ import {
+ geol_lat_fmt,
+ geol_lng_fmt,
+ lib_address_fmt,
+ parse_geom_point_tup,
+ parse_tup_geop_point,
+ type CallbackPromiseGeneric,
+ type CallbackRoute,
+ type GeolocationPointTuple,
+ type IViewBasis,
+ type LcGeocodeCallback,
+ type ResolveFarmInfo,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let {
+ basis,
+ }: {
+ basis: IViewBasis<{
+ data: ResolveFarmInfo;
+ callback_route?: CallbackRoute<string>;
+ lc_geocode: LcGeocodeCallback;
+ lc_handle_farm_lot_add: CallbackPromiseGeneric<string>;
+ lc_handle_farm_products_view: CallbackPromiseGeneric<string>;
+ lc_handle_farm_orders_view: CallbackPromiseGeneric<string>;
+ }>;
+ } = $props();
+
+ let map: maplibregl.Map | undefined = $state(undefined);
+ let map_center: GeolocationPointTuple = $state([0, 0]);
+
+ onMount(async () => {
+ try {
+ if (map && basis.data?.geolocation?.point) {
+ map_center = parse_geom_point_tup(basis.data.geolocation.point);
+ map.setCenter(map_center);
+ map.setZoom(12);
+ }
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
+ });
+
+ const map_geop = $derived(parse_tup_geop_point(map_center));
+
+ const info_addr_fmt = $derived(
+ basis.data?.geolocation?.address
+ ? lib_address_fmt(basis.data.geolocation.address)
+ : ``,
+ );
+
+ const info_geop_lat = $derived(
+ basis.data?.geolocation?.point?.coordinates
+ ? geol_lat_fmt(
+ basis.data.geolocation.point.coordinates[1],
+ `dms`,
+ $liblocale,
+ 3,
+ )
+ : ``,
+ );
+
+ const info_geop_lng = $derived(
+ basis.data?.geolocation?.point?.coordinates
+ ? geol_lng_fmt(
+ basis.data.geolocation.point.coordinates[0],
+ `dms`,
+ $liblocale,
+ 3,
+ )
+ : ``,
+ );
+</script>
+
+<LayoutView>
+ <PageToolbar
+ basis={{
+ header: {
+ label: `${$lls(`common.farms`)} / ${basis.data.name || ``}`,
+ callback_route: basis.callback_route,
+ },
+ }}
+ />
+ <LayoutPage>
+ <div
+ class={`flex flex-row h-[12rem] w-full justify-start items-center`}
+ >
+ <div
+ class={`flex flex-col basis-1/2 h-full p-4 gap-2 justify-start items-center`}
+ >
+ <div class={`flex flex-col w-full justify-start items-center`}>
+ <div
+ class={`flex flex-row w-full justify-start items-center`}
+ >
+ <p
+ class={`font-sans font-[500] text-lg text-layer-0-glyph`}
+ >
+ {info_addr_fmt}
+ </p>
+ </div>
+ <div
+ class={`flex flex-row w-full justify-start items-center`}
+ >
+ <p
+ class={`font-sans font-[500] text-lg text-layer-0-glyph tracking-tight`}
+ >
+ {info_geop_lat && info_geop_lng
+ ? `${info_geop_lat}, ${info_geop_lng}`
+ : ``}
+ </p>
+ </div>
+ </div>
+ </div>
+ <div
+ class={`flex flex-col basis-1/2 h-full justify-start items-center`}
+ >
+ <div
+ class={`flex flex-col h-full w-full p-4 gap-4 justify-start items-center bg-layer-1-surface rounded-2xl`}
+ >
+ <p
+ class={`font-sans font-[500] text-sm text-layer-0-glyph`}
+ >
+ {`Farm Info`}
+ </p>
+ <div
+ class={`flex flex-col w-full gap-1 justify-start items-center`}
+ >
+ <div
+ class={`flex flex-row w-full gap-4 justify-between items-center`}
+ >
+ <p
+ class={`font-sans font-[400] text-layer-0-glyph`}
+ >
+ {`Farm Size:`}
+ </p>
+
+ {#if basis.data.area && basis.data.area_unit}
+ <p
+ class={`font-sans font-[400] text-layer-0-glyph`}
+ >
+ {`${basis.data.area} ${basis.data.area_unit}`}
+ </p>
+ {:else}
+ <div
+ class={`flex flex-row gap-line justify-start items-center`}
+ >
+ <p
+ class={`font-sans font-[400] text-layer-0-glyph_pl`}
+ >
+ {`Add`}
+ </p>
+ <Glyph
+ basis={{
+ classes: `text-layer-0-glyph_pl`,
+ dim: `xs`,
+ key: `caret-right`,
+ }}
+ />
+ </div>
+ {/if}
+ </div>
+ <div
+ class={`flex flex-row w-full gap-4 justify-between items-center`}
+ >
+ <p
+ class={`font-sans font-[400] text-layer-0-glyph`}
+ >
+ {`Farm Lots:`}
+ </p>
+ <p
+ class={`font-sans font-[400] text-layer-0-glyph`}
+ >
+ {`${basis.data.farm_lots?.length || 0}`}
+ </p>
+ </div>
+ <div
+ class={`flex flex-row w-full gap-4 justify-between items-center`}
+ >
+ <p
+ class={`font-sans font-[400] text-layer-0-glyph`}
+ >
+ {`Products:`}
+ </p>
+ <p
+ class={`font-sans font-[400] text-layer-0-glyph`}
+ >
+ {`${0}`}
+ </p>
+ </div>
+ <div
+ class={`flex flex-row w-full gap-4 justify-between items-center`}
+ >
+ <p
+ class={`font-sans font-[400] text-layer-0-glyph`}
+ >
+ {`Orders:`}
+ </p>
+ <p
+ class={`font-sans font-[400] text-layer-0-glyph`}
+ >
+ {`${0}`}
+ </p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class={`flex flex-col w-full gap-3 justify-center items-center`}>
+ <ButtonSimple
+ basis={{
+ label: `View Products`,
+ callback: async () => {
+ await basis.lc_handle_farm_products_view(basis.data.id);
+ },
+ }}
+ />
+ <ButtonSimple
+ basis={{
+ label: `View Orders`,
+ callback: async () => {
+ await basis.lc_handle_farm_orders_view(basis.data.id);
+ },
+ }}
+ />
+ </div>
+ <div
+ class={`flex flex-col flex-shrink-0 h-[16rem] w-full justify-center items-center rounded-2xl overflow-hidden`}
+ >
+ <Map
+ bind:map
+ basis={{
+ interactive: false,
+ }}
+ >
+ <MapMarkerArea
+ {map_geop}
+ basis={{
+ no_drag: true,
+ lc_geocode: basis.lc_geocode,
+ }}
+ />
+ </Map>
+ </div>
+ <div
+ class={`flex flex-col h-[12rem] w-full justify-center items-center`}
+ >
+ <Empty />
+ </div>
+ </LayoutPage>
+</LayoutView>
diff --git a/apps-lib/src/lib/view/farms.svelte b/apps-lib/src/lib/view/farms.svelte
@@ -0,0 +1,89 @@
+<script lang="ts">
+ import ButtonLabelDashed from "$lib/components/button/button-label-dashed.svelte";
+ import {
+ Fade,
+ FarmsDisplayLiEl,
+ GlyphButtonSimple,
+ handle_err,
+ kv_init_page,
+ LayoutPage,
+ LayoutView,
+ lls,
+ PageToolbar,
+ } from "$root";
+ import {
+ type CallbackPromise,
+ type CallbackPromiseGeneric,
+ type CallbackRoute,
+ type IViewBasis,
+ type LcGeocodeCallback,
+ type ResolveFarmInfo,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let {
+ basis,
+ }: {
+ basis: IViewBasis<{
+ data?: ResolveFarmInfo[];
+ callback_route?: CallbackRoute<string>;
+ lc_geocode: LcGeocodeCallback;
+ lc_handle_farm_add: CallbackPromise;
+ lc_handle_farm_view: CallbackPromiseGeneric<string>;
+ }>;
+ } = $props();
+
+ onMount(async () => {
+ try {
+ if (!basis.kv_init_prevent) await kv_init_page();
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
+ });
+</script>
+
+<LayoutView>
+ <PageToolbar
+ basis={{
+ header: {
+ label: `${$lls(`common.farms`)}`,
+ callback_route: basis.callback_route,
+ },
+ }}
+ >
+ {#snippet header_option()}
+ {#if basis.data?.length}
+ <Fade>
+ <GlyphButtonSimple
+ basis={{
+ label: `${$lls(`icu.add_*`, { value: `${$lls(`common.farm`)}` })}`,
+ callback: async () => {
+ await basis.lc_handle_farm_add();
+ },
+ }}
+ ></GlyphButtonSimple>
+ </Fade>
+ {/if}
+ {/snippet}
+ </PageToolbar>
+ <LayoutPage>
+ {#if basis.data?.length}
+ {#each basis.data as li}
+ <FarmsDisplayLiEl
+ basis={li}
+ lc_geocode={basis.lc_geocode}
+ lc_handle_farm_view={basis.lc_handle_farm_view}
+ />
+ {/each}
+ {:else}
+ <ButtonLabelDashed
+ basis={{
+ label: `Add farm`,
+ callback: async () => {
+ await basis.lc_handle_farm_add();
+ },
+ }}
+ />
+ {/if}
+ </LayoutPage>
+</LayoutView>
diff --git a/apps-lib/src/lib/view/home.svelte b/apps-lib/src/lib/view/home.svelte
@@ -1,26 +1,37 @@
<script lang="ts">
import {
- FloatTabs,
+ ButtonSimple,
+ handle_err,
kv_init_page,
+ LayoutPage,
LayoutView,
lls,
+ NavigationTabs,
PageToolbar,
+ } from "$root";
+ import {
type CallbackPromise,
- type ViewBasis,
- } from "$lib";
- import { onDestroy, onMount } from "svelte";
+ type IViewBasis,
+ type ResolveAccountInfo,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
- export let basis: ViewBasis<{
- lc_handle_farm: CallbackPromise;
- }>;
+ let {
+ basis,
+ }: {
+ basis: IViewBasis<{
+ data?: ResolveAccountInfo;
+ lc_handle_farms: CallbackPromise;
+ lc_handle_products: CallbackPromise;
+ }>;
+ } = $props();
onMount(async () => {
- if (!basis.kv_init_prevent) await kv_init_page();
- if (basis.lc_on_mount) await basis.lc_on_mount();
- });
-
- onDestroy(async () => {
- if (basis.lc_on_destroy) await basis.lc_on_destroy();
+ try {
+ if (!basis.kv_init_prevent) await kv_init_page();
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
});
</script>
@@ -32,19 +43,25 @@
},
}}
/>
- <div class={`flex flex-col w-full px-4 gap-4 justify-center items-center`}>
- <div class={`flex flex-col w-full gap-5 justify-center items-center`}>
- <button
- class={`group flex flex-row h-[3.5rem] w-full justify-center items-center rounded-touch bg-layer-1-surface layer-1-active-surface layer-1-active-ring`}
- on:click={basis.lc_handle_farm}
- >
- <p
- class={`font-sans font-[700] text-xl text-layer-0-glyph capitalize tracking-wider opacity-active`}
- >
- {`${$lls(`common.farm_land`)}`}
- </p>
- </button>
- </div>
- </div>
+ <LayoutPage>
+ <ButtonSimple
+ basis={{
+ label: `${$lls(`common.farms`)}`,
+ callback: async () => {
+ await basis.lc_handle_farms();
+ },
+ }}
+ />
+ {#if basis.data?.farms?.length}
+ <ButtonSimple
+ basis={{
+ label: `${$lls(`common.products`)}`,
+ callback: async () => {
+ await basis.lc_handle_products();
+ },
+ }}
+ />
+ {/if}
+ </LayoutPage>
</LayoutView>
-<FloatTabs />
+<NavigationTabs />
diff --git a/apps-lib/src/lib/view/notifications.svelte b/apps-lib/src/lib/view/notifications.svelte
@@ -1,5 +1,5 @@
<script lang="ts">
- import { FloatTabs, LayoutView, lls, PageToolbar } from "$lib";
+ import { LayoutView, lls, NavigationTabs, PageToolbar } from "$root";
let notifications: any[] = [];
</script>
@@ -28,4 +28,4 @@
{/if}
</div>
</LayoutView>
-<FloatTabs />
+<NavigationTabs />
diff --git a/apps-lib/src/lib/view/profile.svelte b/apps-lib/src/lib/view/profile.svelte
@@ -0,0 +1,249 @@
+<script lang="ts">
+ import { lls } from "$lib/locale/i18n";
+ import {
+ FloatPageButton,
+ Glyph,
+ handle_err,
+ ImagePath,
+ ImageUploadAddPhoto,
+ kv_init_page,
+ NavigationTabs,
+ } from "$root";
+ import {
+ ascii,
+ type CallbackPromise,
+ type CallbackPromiseFull,
+ type CallbackPromiseGeneric,
+ type CallbackPromiseReturn,
+ type IViewBasis,
+ type LcPhotoAddCallback,
+ type ResolveProfileInfo,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
+
+ let {
+ basis,
+ photo_path_opt = $bindable(``),
+ loading_photo_upload = $bindable(false),
+ }: {
+ basis: IViewBasis<{
+ data?: ResolveProfileInfo;
+ lc_handle_back: CallbackPromiseGeneric<string>;
+ lc_handle_photo_add: LcPhotoAddCallback;
+ lc_handle_photo_options: CallbackPromise;
+ lc_fs_read_bin: CallbackPromiseFull<string, Uint8Array | undefined>;
+ lc_handle_edit_profile_name: CallbackPromise;
+ lc_handle_edit_profile_name_confirm: CallbackPromiseReturn<boolean>;
+ lc_handle_edit_profile_display_name: CallbackPromise;
+ lc_handle_edit_profile_about: CallbackPromise;
+ }>;
+ photo_path_opt: string;
+ loading_photo_upload: boolean;
+ } = $props();
+
+ type ViewDisplay = `photos` | `following` | `followers`;
+ let view_display: ViewDisplay = $state(`photos`);
+
+ onMount(async () => {
+ try {
+ if (!basis.kv_init_prevent) await kv_init_page();
+ if (basis.lc_on_mount) await basis.lc_on_mount();
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
+ });
+
+ const profile_photo = $derived(
+ basis.data?.profile_photos?.find((i) => i.primary),
+ );
+
+ const photo_overlay_visible = $derived(!!(profile_photo || photo_path_opt));
+
+ const classes_photo_overlay_glyph = $derived(
+ photo_overlay_visible ? `text-white` : `text-layer-0-glyph`,
+ );
+
+ const classes_photo_overlay_glyph_opt = $derived(
+ photo_overlay_visible ? `text-gray-300` : `text-layer-0-glyph`,
+ );
+
+ const classes_photo_overlay_glyph_opt_selected = $derived(
+ photo_overlay_visible ? `text-white` : `text-layer-1-glyph_d`,
+ );
+</script>
+
+<div
+ class={`relative flex flex-col min-h-[525px] h-[525px] w-full justify-center items-center bg-layer-2-surface fade-in`}
+>
+ <FloatPageButton
+ basis={{
+ posx: `left`,
+ glyph: `arrow-left`,
+ loading: loading_photo_upload,
+ callback: async () => {
+ if (basis.data) await basis.lc_handle_back(basis.data.id);
+ },
+ }}
+ />
+ <FloatPageButton
+ basis={{
+ posx: `right`,
+ glyph: `images-square`,
+ loading: loading_photo_upload,
+ callback: basis.lc_handle_photo_options,
+ }}
+ />
+ {#if profile_photo}
+ <ImagePath
+ basis={{
+ path: profile_photo.media_image.url,
+ }}
+ />
+ {:else if photo_path_opt}
+ <ImagePath basis={{ path: photo_path_opt }} />
+ {:else}
+ <div class={`flex flex-row justify-start items-center -translate-y-8`}>
+ <ImageUploadAddPhoto
+ bind:photo_path={photo_path_opt}
+ basis={{
+ lc_handle_photo_add: basis.lc_handle_photo_add,
+ }}
+ />
+ </div>
+ {/if}
+ <div
+ class={`absolute bottom-0 left-0 flex flex-col h-[calc(100%-100%/1.618)] w-full px-6 gap-2 justify-end items-center`}
+ >
+ <div
+ class={`flex flex-col w-full gap-[2px] justify-center items-center`}
+ >
+ <div class={`flex flex-row h-10 w-full justify-start items-center`}>
+ <button
+ class={`group flex flex-row justify-center items-center`}
+ onclick={basis.lc_handle_edit_profile_display_name}
+ >
+ <p
+ class={`font-sansd font-[600] text-[2rem] ${classes_photo_overlay_glyph} ${basis.data?.name ? `` : `capitalize opacity-active`} el-re`}
+ >
+ {basis.data?.name
+ ? basis.data.name
+ : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.profile_name`)}` })}`}`}
+ </p>
+ </button>
+ </div>
+ <div
+ class={`flex flex-row w-full gap-[6px] justify-start items-center`}
+ >
+ <button
+ class={`group flex flex-row justify-center items-center`}
+ onclick={async () => {
+ if (basis.data?.name) {
+ const confirm =
+ basis.lc_handle_edit_profile_name_confirm();
+ if (!confirm) return;
+ }
+ await basis.lc_handle_edit_profile_name();
+ }}
+ >
+ <p
+ class={`font-sansd font-[600] text-[1.1rem] ${classes_photo_overlay_glyph} ${basis.data?.name ? `` : `capitalize opacity-active`} el-re`}
+ >
+ {basis.data?.name
+ ? `@${basis.data.name}`
+ : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.username`)}` })}`}`}
+ </p>
+ </button>
+ <p
+ class={`font-sans font-[400] ${classes_photo_overlay_glyph}`}
+ >
+ {ascii.bullet}
+ </p>
+ <button
+ class={`flex flex-row justify-center items-center`}
+ onclick={async () => {
+ alert(`@todo!`);
+ }}
+ >
+ <Glyph
+ basis={{
+ classes: `${classes_photo_overlay_glyph}`,
+ dim: `xs`,
+
+ key: `link-simple`,
+ }}
+ />
+ </button>
+ </div>
+ <div class={`flex flex-row w-full justify-start items-center`}>
+ <button
+ class={`group flex flex-row justify-center items-center`}
+ onclick={basis.lc_handle_edit_profile_about}
+ >
+ <p
+ class={`font-sansd font-[400] text-[1.1rem] ${classes_photo_overlay_glyph} ${basis.data?.about ? `` : `capitalize opacity-active`}`}
+ >
+ {basis.data?.about
+ ? `@${basis.data.about}`
+ : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.bio`)}` })}`}`}
+ </p>
+ </button>
+ </div>
+ </div>
+ <div
+ class={`flex flex-row w-full pt-2 pb-6 gap-2 justify-start items-center`}
+ >
+ <button
+ class={`flex flex-row justify-center items-center`}
+ onclick={async () => {
+ view_display = `photos`;
+ }}
+ >
+ <p
+ class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `photos` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`}
+ >
+ {`photos`}
+ </p>
+ </button>
+ <button
+ class={`flex flex-row justify-center items-center`}
+ onclick={async () => {
+ view_display = `following`;
+ }}
+ >
+ <p
+ class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `following` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`}
+ >
+ {`following`}
+ </p>
+ </button>
+ <button
+ class={`flex flex-row justify-center items-center`}
+ onclick={async () => {
+ view_display = `followers`;
+ }}
+ >
+ <p
+ class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `followers` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`}
+ >
+ {`followers`}
+ </p>
+ </button>
+ </div>
+ </div>
+</div>
+<div class={`flex flex-col w-full min-h-[500px] justify-start items-center`}>
+ {#if view_display === `photos`}
+ <p class={`font-sans font-[400] text-layer-0-glyph`}>
+ {view_display}
+ </p>
+ {:else if view_display === `following`}
+ <p class={`font-sans font-[400] text-layer-0-glyph`}>
+ {view_display}
+ </p>
+ {:else if view_display === `followers`}
+ <p class={`font-sans font-[400] text-layer-0-glyph`}>
+ {view_display}
+ </p>
+ {/if}
+</div>
+<NavigationTabs />
diff --git a/apps-lib/src/lib/view/search.svelte b/apps-lib/src/lib/view/search.svelte
@@ -1,51 +1,76 @@
<script lang="ts">
import {
- cfg_app,
- debounce_input,
- FloatTabs,
fmt_id,
Glyph,
+ handle_err,
Input,
kv_init_page,
+ LayoutPage,
LayoutView,
lls,
+ NavigationTabs,
PageToolbar,
SearchResultDisplay,
+ } from "$root";
+ import {
+ cfg_app,
+ debounce_callback,
SearchService,
- type ISearchLoadData,
- type ISearchResultDisplayCallbacks,
+ type CallbackPromise,
+ type CallbackPromiseGeneric,
+ type IViewBasis,
+ type IViewSearchData,
+ type ResolveGeolocationInfo,
+ type ResolveProfileInfo,
type SearchServiceResult,
- type ViewBasisLoadData,
- } from "$lib";
+ } from "@radroots/util";
import { onDestroy, onMount } from "svelte";
- type LoadData = ISearchLoadData;
+ let {
+ basis,
+ }: {
+ basis: IViewBasis<{
+ data: IViewSearchData;
+ lc_handle_back: CallbackPromise;
+ lc_handle_search_geolocation: CallbackPromiseGeneric<ResolveGeolocationInfo>;
+ lc_handle_search_profile: CallbackPromiseGeneric<ResolveProfileInfo>;
+ lc_handle_search_nostr_relay: CallbackPromiseGeneric<{
+ id: string;
+ }>;
+ }>;
+ } = $props();
- export let basis: ViewBasisLoadData<
- ISearchResultDisplayCallbacks & {},
- LoadData
- >;
- let load_data: LoadData = undefined;
+ $effect(() => {
+ console.log(JSON.stringify(basis.data, null, 4), `basis.data`);
+ });
- let client_search: SearchService | undefined = undefined;
- let search_val = ``;
- let search_results: SearchServiceResult[] = [];
+ let search_service: SearchService | undefined = undefined;
+ let search_val = $state(``);
+ let search_results: SearchServiceResult[] = $state([]);
onMount(async () => {
- search_val = ``;
- if (!basis.kv_init_prevent) await kv_init_page();
- if (basis.lc_on_mount) await basis.lc_on_mount();
- load_data = await basis.lc_load_data();
- if (load_data) client_search = new SearchService(load_data);
+ try {
+ search_val = ``;
+ if (!basis.kv_init_prevent) await kv_init_page();
+ if (basis.lc_on_mount) await basis.lc_on_mount();
+ search_service = new SearchService(basis.data);
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
});
+ const handle_search_input = debounce_callback((val: string) => {
+ console.log(`val `, val);
+ if (search_service) search_results = search_service.search(val);
+ }, cfg_app.debounce.search);
+
onDestroy(async () => {
- if (basis.lc_on_destroy) await basis.lc_on_destroy();
+ try {
+ if (basis.lc_on_destroy) await basis.lc_on_destroy();
+ } catch (e) {
+ handle_err(e, `on_destroy`);
+ }
});
-
- const handle_search_input = debounce_input((val: string) => {
- if (client_search) search_results = client_search.search(val);
- }, cfg_app.debounce.search);
</script>
<LayoutView>
@@ -55,7 +80,7 @@
callback: basis.lc_handle_back,
}}
/>
- <div class={`flex flex-col w-full px-4 gap-6 justify-center items-center`}>
+ <LayoutPage>
<div
class={`relative flex flex-row w-full justify-center items-center bg-layer-1-surface rounded-2xl`}
>
@@ -63,10 +88,10 @@
basis={{
classes: `absolute left-4 text-layer-0-glyph-shade`,
dim: `sm`,
- weight: `bold`,
+
key: `magnifying-glass`,
}}
- ></Glyph>
+ />
<Input
bind:value={search_val}
basis={{
@@ -76,23 +101,23 @@
placeholder: `Enter search query`,
callback: async ({ value }) => handle_search_input(value),
}}
- ></Input>
+ />
</div>
<div class={`flex flex-col w-full gap-4 justify-center items-center`}>
{#each search_results as li (li.id)}
<SearchResultDisplay
basis={{
result: li,
- lc_handle_result_location_gcs:
- basis.lc_handle_result_location_gcs,
- lc_handle_result_nostr_profile:
- basis.lc_handle_result_nostr_profile,
- lc_handle_result_nostr_relay:
- basis.lc_handle_result_nostr_relay,
+ lc_handle_search_geolocation:
+ basis.lc_handle_search_geolocation,
+ lc_handle_search_profile:
+ basis.lc_handle_search_profile,
+ lc_handle_search_nostr_relay:
+ basis.lc_handle_search_nostr_relay,
}}
/>
{/each}
</div>
- </div>
+ </LayoutPage>
</LayoutView>
-<FloatTabs />
+<NavigationTabs />
diff --git a/apps-lib/src/lib/view/settings-nostr.svelte b/apps-lib/src/lib/view/settings-nostr.svelte
@@ -1,134 +0,0 @@
-<script lang="ts">
- import {
- LayoutTrellis,
- LayoutView,
- PageToolbar,
- Trellis,
- ascii,
- handle_err,
- lls,
- type ISelectOption,
- } from "$lib";
- import { onMount } from "svelte";
-
- const page_param: {
- select_options: ISelectOption<string>[];
- } = {
- select_options: [
- {
- value: ascii.bullet,
- label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.photo_hosting`)}`.toLowerCase() })}`,
- disabled: true,
- },
- {
- value: `^radroots`,
- label: `https://radroots.market`,
- },
- {
- value: `*add`,
- label: `${$lls(`icu.add_*`, { value: `${$lls(`common.upload_url`)}`.toLowerCase() })}`,
- },
- {
- value: `*disable`,
- label: `${$lls(`common.disable_uploads`)}`,
- },
- ],
- };
-
- let nostr_photohosting_sel_val = ``;
- let nostr_photohosting_sel_label = ``;
-
- onMount(async () => {
- try {
- await init_page();
- } catch (e) {
- } finally {
- }
- });
-
- const init_page = async (): Promise<void> => {
- try {
- nostr_photohosting_sel_val = `^radroots`; //@todo
- } catch (e) {
- await handle_err(e, `init_page`);
- }
- };
-
- $: nostr_photohosting_sel_label = nostr_photohosting_sel_val
- ? page_param.select_options.filter(
- (i) => i.value === nostr_photohosting_sel_val,
- )?.[0].label
- : ``;
-
- const handle_select_option = async (
- option_value: string,
- ): Promise<void> => {
- try {
- if (!option_value.startsWith(`*`)) {
- nostr_photohosting_sel_val = option_value;
- return;
- }
- nostr_photohosting_sel_val = ``;
- alert(`@todo!`);
- nostr_photohosting_sel_val = `^radroots`;
- } catch (e) {
- await handle_err(e, `handle_select_option`);
- }
- };
-</script>
-
-<LayoutView>
- <PageToolbar
- basis={{
- header: {
- label: `${$lls(`common.nostr`)}`,
- },
- }}
- />
- <LayoutTrellis>
- <Trellis
- basis={{
- args: {
- layer: 1,
- list: [
- {
- hide_active: true,
- select: {
- label: {
- left: [
- {
- value: `Photo Hosting`,
- classes: `capitalize`,
- },
- ],
- },
- display: {
- loading: !nostr_photohosting_sel_val,
- label: {
- value: nostr_photohosting_sel_label,
- },
- },
- el: {
- value: nostr_photohosting_sel_val,
- options: [
- {
- entries: page_param.select_options,
- },
- ],
- callback: async ({ value }) => {
- await handle_select_option(value);
- },
- },
- end: {
- glyph: {
- key: `caret-right`,
- },
- },
- },
- },
- ],
- },
- }}
- />
- </LayoutTrellis>
-</LayoutView>
diff --git a/apps-lib/src/lib/view/settings-profile-edit.svelte b/apps-lib/src/lib/view/settings-profile-edit.svelte
@@ -1,138 +0,0 @@
-<script lang="ts">
- import {
- app_notify,
- fmt_id,
- Input,
- kv_init_page,
- LayoutView,
- lls,
- Nav,
- qp_nostr_pk,
- qp_rkey,
- TextArea,
- type CallbackPromise,
- type ISettingsNostrProfileEditLoadData,
- type ViewBasisLoadData,
- } from "$lib";
- import { onDestroy, onMount } from "svelte";
-
- type LoadData = ISettingsNostrProfileEditLoadData;
-
- export let basis: ViewBasisLoadData<
- {
- lc_submit: CallbackPromise;
- },
- LoadData
- >;
- let load_data: LoadData = undefined;
-
- let val_input_init = ``;
- let val_input = ``;
-
- onMount(async () => {
- if (!basis.kv_init_prevent) await kv_init_page();
- if (basis.lc_on_mount) await basis.lc_on_mount();
- if (!$qp_rkey || !$qp_nostr_pk)
- return void app_notify.set(
- `${$lls(`error.page.load.query_param`)}`,
- );
- load_data = await basis.lc_load_data();
- if (load_data?.field_val) {
- val_input = load_data.field_val;
- val_input_init = load_data.field_val;
- }
- });
-
- onDestroy(async () => {
- if (basis.lc_on_destroy) await basis.lc_on_destroy();
- });
-
- $: translated_field_key = load_data?.field_key
- ? `${$lls(`models.nostr_profile.fields.${load_data.field_key}.label`)}`
- : ``;
- $: val_input_delta = val_input_init !== val_input;
-</script>
-
-<LayoutView>
- {#if load_data}
- <div
- class={`flex flex-col w-full pt-4 px-4 gap-1 justify-start items-center fade-in`}
- >
- <div class={`flex flex-row w-full pl-2 justify-start items-center`}>
- <p
- class={`font-sans text-trellis_ti text-layer-0-glyph-label uppercase`}
- >
- {translated_field_key.replace(/profile /i, ``)}
- </p>
- </div>
- {#if load_data.field_key === `about`}
- <TextArea
- bind:value={val_input}
- basis={{
- id: fmt_id(load_data.field_key),
- classes: `min-h-[12rem] pl-4`,
- sync: true,
- layer: 1,
- placeholder: `${$lls(`icu.enter_*`, { value: `${translated_field_key}`.toLowerCase() })}`,
- /*field: {
- charset:
- nostr_profile_form_fields[load_data.field_key].charset,
- validate:
- nostr_profile_form_fields[load_data.field_key]
- .validation,
- validate_keypress: true,
- },*/
- callback_keydown: async ({ key_s }) => {
- if (key_s && val_input_delta)
- await basis.lc_submit();
- },
- }}
- ></TextArea>
- {:else}
- <Input
- bind:value={val_input}
- basis={{
- id: fmt_id(load_data.field_key),
- classes: `rounded-touch pl-4`,
- sync: true,
- layer: 1,
- placeholder: `${$lls(`icu.enter_*`, { value: `${translated_field_key}`.toLowerCase() })}`,
- /*field: {
- charset:
- nostr_profile_form_fields[load_data.field_key].charset,
- validate:
- nostr_profile_form_fields[load_data.field_key]
- .validation,
- validate_keypress: true,
- },*/
- callback_keydown: async ({ key_s }) => {
- if (key_s && val_input_delta)
- await basis.lc_submit();
- },
- }}
- ></Input>
- {/if}
- </div>
- {/if}
-</LayoutView>
-<Nav
- basis={{
- prev: {
- label: `${$lls(`common.profile`)}`,
- route: `/settings/profile`,
- prevent_route: val_input_delta
- ? {
- callback: async () => {
- if (val_input_delta) await basis.lc_submit();
- },
- }
- : undefined,
- },
- title: {
- label: {
- classes: `capitalize`,
- value: `${$lls(`icu.edit_*`, { value: `${$lls(`common.profile`)}` })}`,
- },
- },
- }}
-/>
diff --git a/apps-lib/src/lib/view/settings-profile.svelte b/apps-lib/src/lib/view/settings-profile.svelte
@@ -1,243 +0,0 @@
-<script lang="ts">
- import {
- ascii,
- FloatPageButton,
- FloatTabs,
- Glyph,
- ImageBlob,
- ImagePath,
- ImageUploadAddPhoto,
- kv_init_page,
- lls,
- type CallbackPromise,
- type CallbackPromiseFull,
- type CallbackPromiseReturn,
- type ISettingsNostrProfileLoadData,
- type ViewBasisLoadData,
- } from "$lib";
- import { onDestroy, onMount } from "svelte";
-
- export let bv_photo_path_opt = ``;
-
- type LoadData = ISettingsNostrProfileLoadData;
-
- export let basis: ViewBasisLoadData<
- {
- lc_handle_photo_add: CallbackPromiseReturn<string | undefined>;
- lc_handle_photo_options: CallbackPromise;
- lc_fs_read_bin: CallbackPromiseFull<string, Uint8Array | undefined>;
- lc_handle_edit_profile_name: CallbackPromise;
- lc_handle_edit_profile_name_confirm: CallbackPromiseReturn<boolean>;
- lc_handle_edit_profile_display_name: CallbackPromise;
- lc_handle_edit_profile_about: CallbackPromise;
- },
- LoadData
- >;
- let load_data: LoadData = undefined;
-
- let loading_photo_upload = false;
- type ViewDisplay = `photos` | `following` | `followers`;
- let view_display: ViewDisplay = `photos`;
-
- onMount(async () => {
- if (!basis.kv_init_prevent) await kv_init_page();
- if (basis.lc_on_mount) await basis.lc_on_mount();
- load_data = await basis.lc_load_data();
- });
-
- onDestroy(async () => {
- if (basis.lc_on_destroy) await basis.lc_on_destroy();
- });
-
- $: photo_overlay_visible =
- load_data?.nostr_profile?.picture || bv_photo_path_opt;
- $: classes_photo_overlay_glyph = photo_overlay_visible
- ? `text-white`
- : `text-layer-0-glyph`;
- $: classes_photo_overlay_glyph_opt = photo_overlay_visible
- ? `text-gray-300`
- : `text-layer-0-glyph`;
- $: classes_photo_overlay_glyph_opt_selected = photo_overlay_visible
- ? `text-white`
- : `text-layer-1-glyph`;
-</script>
-
-<div
- class={`relative flex flex-col min-h-[525px] h-[525px] w-full justify-center items-center bg-layer-2-surface fade-in`}
->
- <FloatPageButton
- basis={{
- posx: `left`,
- glyph: `arrow-left`,
- loading: loading_photo_upload,
- callback: basis.lc_handle_back,
- }}
- />
- <FloatPageButton
- basis={{
- posx: `right`,
- glyph: `images-square`,
- loading: loading_photo_upload,
- callback: basis.lc_handle_photo_options,
- }}
- />
- {#if load_data?.nostr_profile?.picture}
- <ImagePath
- basis={{
- path: load_data.nostr_profile.picture,
- }}
- />
- {:else if bv_photo_path_opt}
- {#await basis.lc_fs_read_bin(bv_photo_path_opt) then file_data}
- <ImageBlob
- basis={{
- data: file_data,
- }}
- />
- {/await}
- {:else}
- <div class={`flex flex-row justify-start items-center -translate-y-8`}>
- <ImageUploadAddPhoto
- bind:bv_photo_path={bv_photo_path_opt}
- basis={{
- lc_handle_photo_add: basis.lc_handle_photo_add,
- }}
- />
- </div>
- {/if}
- <div
- class={`absolute bottom-0 left-0 flex flex-col h-[calc(100%-100%/1.618)] w-full px-6 gap-2 justify-end items-center`}
- >
- <div
- class={`flex flex-col w-full gap-[2px] justify-center items-center`}
- >
- <div class={`flex flex-row h-10 w-full justify-start items-center`}>
- <button
- class={`group flex flex-row justify-center items-center`}
- on:click={basis.lc_handle_edit_profile_display_name}
- >
- <p
- class={`font-sansd font-[600] text-[2rem] ${classes_photo_overlay_glyph} ${load_data?.nostr_profile.display_name ? `` : `capitalize opacity-active`} el-re`}
- >
- {load_data?.nostr_profile.display_name
- ? load_data.nostr_profile.display_name
- : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.profile_name`)}` })}`}`}
- </p>
- </button>
- </div>
- <div
- class={`flex flex-row w-full gap-[6px] justify-start items-center`}
- >
- <button
- class={`group flex flex-row justify-center items-center`}
- on:click={async () => {
- if (load_data?.nostr_profile.name) {
- const confirm =
- basis.lc_handle_edit_profile_name_confirm();
- if (!confirm) return;
- }
- await basis.lc_handle_edit_profile_name();
- }}
- >
- <p
- class={`font-sansd font-[600] text-[1.1rem] ${classes_photo_overlay_glyph} ${load_data?.nostr_profile.name ? `` : `capitalize opacity-active`} el-re`}
- >
- {load_data?.nostr_profile.name
- ? `@${load_data.nostr_profile.name}`
- : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.username`)}` })}`}`}
- </p>
- </button>
- <p
- class={`font-sans font-[400] ${classes_photo_overlay_glyph}`}
- >
- {ascii.bullet}
- </p>
- <button
- class={`flex flex-row justify-center items-center`}
- on:click={async () => {
- alert(`@todo!`);
- }}
- >
- <Glyph
- basis={{
- classes: `${classes_photo_overlay_glyph}`,
- dim: `xs`,
- weight: `bold`,
- key: `link-simple`,
- }}
- />
- </button>
- </div>
- <div class={`flex flex-row w-full justify-start items-center`}>
- <button
- class={`group flex flex-row justify-center items-center`}
- on:click={basis.lc_handle_edit_profile_about}
- >
- <p
- class={`font-sansd font-[400] text-[1.1rem] ${classes_photo_overlay_glyph} ${load_data?.nostr_profile.about ? `` : `capitalize opacity-active`}`}
- >
- {load_data?.nostr_profile.about
- ? `@${load_data.nostr_profile.about}`
- : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.bio`)}` })}`}`}
- </p>
- </button>
- </div>
- </div>
- <div
- class={`flex flex-row w-full pt-2 pb-6 gap-2 justify-start items-center`}
- >
- <button
- class={`flex flex-row justify-center items-center`}
- on:click={async () => {
- view_display = `photos`;
- }}
- >
- <p
- class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `photos` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`}
- >
- {`photos`}
- </p>
- </button>
- <button
- class={`flex flex-row justify-center items-center`}
- on:click={async () => {
- view_display = `following`;
- }}
- >
- <p
- class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `following` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`}
- >
- {`following`}
- </p>
- </button>
- <button
- class={`flex flex-row justify-center items-center`}
- on:click={async () => {
- view_display = `followers`;
- }}
- >
- <p
- class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `followers` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`}
- >
- {`followers`}
- </p>
- </button>
- </div>
- </div>
-</div>
-<div class={`flex flex-col w-full justify-start items-center`}>
- {#if view_display === `photos`}
- <p class={`font-sans font-[400] text-layer-0-glyph`}>
- {view_display}
- </p>
- {:else if view_display === `following`}
- <p class={`font-sans font-[400] text-layer-0-glyph`}>
- {view_display}
- </p>
- {:else if view_display === `followers`}
- <p class={`font-sans font-[400] text-layer-0-glyph`}>
- {view_display}
- </p>
- {/if}
-</div>
-<FloatTabs />
diff --git a/apps-lib/src/lib/view/settings.svelte b/apps-lib/src/lib/view/settings.svelte
@@ -1,32 +1,39 @@
<script lang="ts">
import {
app_thc,
- ascii,
+ handle_err,
kv_init_page,
LayoutTrellis,
LayoutView,
lls,
PageToolbar,
Trellis,
+ } from "$root";
+ import {
+ ascii,
type CallbackPromise,
type CallbackPromiseGeneric,
type ISelectOption,
- type ViewBasis,
- } from "$lib";
- import { onDestroy, onMount } from "svelte";
+ type IViewBasis,
+ } from "@radroots/util";
+ import { onMount } from "svelte";
- export let basis: ViewBasis<{
- lc_color_mode: CallbackPromiseGeneric<ISelectOption<string>>;
- lc_settings_nostr: CallbackPromise;
- }>;
+ let {
+ basis,
+ }: {
+ basis: IViewBasis<{
+ lc_color_mode: CallbackPromiseGeneric<ISelectOption<string>>;
+ lc_settings_nostr: CallbackPromise;
+ lc_logout: CallbackPromise;
+ }>;
+ } = $props();
onMount(async () => {
- if (!basis.kv_init_prevent) await kv_init_page();
- if (basis.lc_on_mount) await basis.lc_on_mount();
- });
-
- onDestroy(async () => {
- if (basis.lc_on_destroy) await basis.lc_on_destroy();
+ try {
+ if (!basis.kv_init_prevent) await kv_init_page();
+ } catch (e) {
+ handle_err(e, `on_mount`);
+ }
});
</script>
@@ -41,323 +48,87 @@
<LayoutTrellis>
<Trellis
basis={{
- args: {
- layer: 1,
- title: {
- value: `Appearance`,
- },
- list: [
- {
- hide_active: true,
- select: {
- label: {
- left: [
- {
- value: `${$lls(`common.color_mode`)}`,
- classes: `capitalize`,
- },
- ],
- },
- display: {
- label: {
- value: `${$app_thc}`,
+ layer: 1,
+ title: {
+ value: `Appearance`,
+ },
+ list: [
+ {
+ hide_active: true,
+ select: {
+ label: {
+ left: [
+ {
+ value: `${$lls(`common.color_mode`)}`,
classes: `capitalize`,
},
- },
- el: {
- value: $app_thc,
- options: [
- {
- entries: [
- {
- value: ascii.bullet,
- label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.color_mode`)}`.toLowerCase() })}`,
- disabled: true,
- },
- {
- value: `light`,
- label: `${$lls(`common.light`)}`,
- },
- {
- value: `dark`,
- label: `${$lls(`common.dark`)}`,
- },
- ],
- },
- ],
- callback: basis.lc_color_mode,
- },
- end: {
- glyph: {
- key: `caret-right`,
- },
- },
+ ],
},
- },
- ],
- },
- }}
- />
- <Trellis
- basis={{
- args: {
- layer: 1,
- title: {
- value: `Nostr Keys`,
- },
- list: [
- {
- touch: {
+ display: {
label: {
- left: [
- {
- value: `Nostr Key (public)`,
- classes: `capitalize`,
- },
- ],
- },
- end: {
- glyph: {
- key: `caret-right`,
- },
- },
- callback: async () => {
- alert(`@todo!`);
- /*const public_key = await keystore.get(
- ks.keys.nostr_publickey,
- );
- if (`err` in public_key) return;
- await dialog.alert(
- `Hi! This is your nostr public key ${public_key.result}`,
- );*/
+ value: `${$app_thc}`,
+ classes: `capitalize`,
},
},
- },
- {
- touch: {
- label: {
- left: [
- {
- value: `Nostr Key (secret)`,
- classes: `capitalize`,
- },
- ],
- },
- end: {
- glyph: {
- key: `caret-right`,
+ el: {
+ value: $app_thc,
+ options: [
+ {
+ entries: [
+ {
+ value: ascii.bullet,
+ label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.color_mode`)}`.toLowerCase() })}`,
+ disabled: true,
+ },
+ {
+ value: `light`,
+ label: `${$lls(`common.light`)}`,
+ },
+ {
+ value: `dark`,
+ label: `${$lls(`common.dark`)}`,
+ },
+ ],
},
- },
- callback: async () => {
- alert(`@todo!`);
- /*const public_key = await keystore.get(
- ks.keys.nostr_publickey,
- );
- if (`err` in public_key) return;
- const secret_key = await keystore.get(
- ks.keys.nostr_secretkey(
- public_key.result,
- ),
- );
- if (`err` in secret_key) return;
- await dialog.alert(
- `Hi! This is your nostr secret key ${secret_key.result}`,
- );*/
- },
+ ],
+ callback: basis.lc_color_mode,
},
- },
- ],
- },
- }}
- />
- <Trellis
- basis={{
- args: {
- layer: 1,
- title: {
- value: `Configuration`,
- },
- list: [
- {
- touch: {
- label: {
- left: [
- {
- value: `Nostr Settings`,
- classes: `capitalize`,
- },
- ],
- },
- end: {
- glyph: {
- key: `caret-right`,
- },
+ end: {
+ glyph: {
+ key: `caret-right`,
},
- callback: basis.lc_settings_nostr,
},
},
- {
- touch: {
- label: {
- left: [
- {
- value: `Reset Device`,
- classes: `capitalize`,
- },
- ],
- },
- end: {
- glyph: {
- key: `caret-right`,
- },
- },
- callback: async () => {
- alert(`@todo!`);
- /*const confirm = await dialog.confirm(
- `Hi! This will delete your saved keys.`,
- );
- if (confirm === true) await reset_device();*/
- },
- },
- },
- ],
- },
- }}
- />
- <Trellis
- basis={{
- args: {
- layer: 1,
- title: {
- value: `Location`,
},
- list: [
- {
- touch: {
- label: {
- left: [
- {
- value: `Geolocation Current`,
- classes: `capitalize`,
- },
- ],
- },
- callback: async () => {
- alert(`@todo!`);
- /*const pos = await geol.current();
- await dialog.alert(JSON.stringify(pos));*/
- },
- },
- },
- ],
- },
+ ],
}}
/>
<Trellis
basis={{
- args: {
- layer: 1,
- title: {
- value: `Web`,
- },
- list: [
- {
- touch: {
- label: {
- left: [
- {
- value: `Radroots.Org (Open Homepage)`,
- },
- ],
- },
- callback: async () => {
- //const url = `https://radroots.org`;
- //await browser.open(url);
- },
+ layer: 1,
+ list: [
+ {
+ hide_active: true,
+ touch: {
+ label: {
+ left: [
+ {
+ value: `${$lls(`common.logout`)}`,
+ classes: `capitalize`,
+ },
+ ],
},
- },
- {
- touch: {
- label: {
- left: [
- {
- value: `Radroots.Org (Share Homepage)`,
- },
- ],
- },
- callback: async () => {
- //await share.open({
- // title: `Radroots Home Page`,
- // text: `Find farmers around the world.`,
- // url: `https://radroots.org`,
- // dialog_title: `This is the dialog title`,
- //});
+ end: {
+ glyph: {
+ key: `caret-right`,
},
},
- },
- {
- touch: {
- label: {
- left: [
- {
- value: `Primal.Net (Open Profile)`,
- },
- ],
- },
- callback: async () => {
- //const public_key = await keystore.get(
- // ks.keys.nostr_publickey,
- //);
- //const npub = nostr.lib.npub(public_key);
- //const url = `https://primal.net/p/${npub}`;
- //await browser.open(url);
- },
+ callback: async () => {
+ await basis.lc_logout();
},
},
- ],
- },
- }}
- />
- <Trellis
- basis={{
- args: {
- layer: 1,
- title: {
- value: `Device`,
},
- list: [
- {
- touch: {
- label: {
- left: [
- {
- value: `Device (Info)`,
- },
- ],
- },
- callback: async () => {
- //const data = await device.info();
- //await dialog.alert(JSON.stringify(data));
- },
- },
- },
- {
- touch: {
- label: {
- left: [
- {
- value: `Device (Battery)`,
- },
- ],
- },
- callback: async () => {
- //const data = await device.battery();
- //await dialog.alert(JSON.stringify(data));
- },
- },
- },
- ],
- },
+ ],
}}
/>
</LayoutTrellis>
diff --git a/apps-lib/svelte.config.js b/apps-lib/svelte.config.js
@@ -1,8 +1,15 @@
-import preprocess from 'svelte-preprocess';
+import adapter from '@sveltejs/adapter-auto';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
- preprocess: preprocess(),
+ preprocess: vitePreprocess(),
+ kit: {
+ adapter: adapter(),
+ alias: {
+ $root: './src/lib/index.js',
+ }
+ },
};
export default config;
diff --git a/apps-lib/tsconfig.json b/apps-lib/tsconfig.json
@@ -1,17 +1,13 @@
{
- "$schema": "https://json.schemastore.org/tsconfig",
- "display": "svelte-lib",
"extends": "./.svelte-kit/tsconfig.json",
- "include": [
- "src/**/*.d.ts",
- "src/**/*.js",
- "src/**/*.ts",
- "src/**/*.svelte",
- "src/**/*.css"
- ],
- "exclude": [
- "dist",
- "build",
- "node_modules"
- ]
-}
-\ No newline at end of file
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ }
+}
diff --git a/apps-lib/vite.config.ts b/apps-lib/vite.config.ts
@@ -0,0 +1,11 @@
+import { sveltekit } from '@sveltejs/kit/vite';
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ resolve: {
+ alias: {
+ '$root': '/src/lib/index.js',
+ }
+ },
+ plugins: [sveltekit()]
+});