commit 47503e906f84237d6efcddd07abbe445e3eab75b parent c149b1e559e1e939438d4e68a3d57c4c852d5898 Author: triesap <137732411+triesap@users.noreply.github.com> Date: Tue, 29 Apr 2025 04:28:33 +0000 apps-lib: add lib context. add farms products add view. refactor library Diffstat:
45 files changed, 1061 insertions(+), 366 deletions(-)
diff --git a/apps-lib/src/lib/components/button/button-layout-pair.svelte b/apps-lib/src/lib/components/button/button-layout-pair.svelte @@ -5,6 +5,7 @@ Empty, type IClOpt, type IDisabledOpt, + type ILoadingOpt, } from "$root"; import { fmt_cl, type CallbackPromise } from "@radroots/util"; @@ -12,10 +13,11 @@ basis, }: { basis: IClOpt & { - continue: IDisabledOpt & { - label: string; - callback: CallbackPromise; - }; + continue: IDisabledOpt & + ILoadingOpt & { + label: string; + callback: CallbackPromise; + }; back?: IDisabledOpt & { visible: boolean; label?: string; diff --git a/apps-lib/src/lib/components/button/button-layout.svelte b/apps-lib/src/lib/components/button/button-layout.svelte @@ -1,5 +1,12 @@ <script lang="ts"> - import { app_lo, type IClOpt, type IDisabledOpt, type ILyOpt } from "$root"; + import { + app_lo, + LoadSymbol, + type IClOpt, + type IDisabledOpt, + type ILoadingOpt, + type ILyOpt, + } from "$root"; import { fmt_cl, parse_layer, type CallbackPromise } from "@radroots/util"; let { @@ -7,7 +14,8 @@ }: { basis: ILyOpt & IClOpt & - IDisabledOpt & { + IDisabledOpt & + ILoadingOpt & { classes_inner?: string; hide_active?: boolean; label: string; @@ -31,9 +39,13 @@ 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> + {#if basis.loading} + <LoadSymbol basis={{ dim: `md` }} /> + {:else} + <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> + {/if} </button> diff --git a/apps-lib/src/lib/components/button/button-simple.svelte b/apps-lib/src/lib/components/button/button-simple.svelte @@ -15,7 +15,7 @@ }} > <p - class={`font-sans font-[700] text-xl text-layer-0-glyph capitalize tracking-wider opacity-active`} + class={`font-sans font-[600] text-xl text-layer-0-glyph capitalize tracking-wider opacity-active`} > {basis.label} </p> 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 @@ -1,10 +1,9 @@ <script lang="ts"> import { - ButtonSimple, + ButtonLayoutPair, EnvelopeLowerFull, Map, MapMarkerArea, - type LcGeocodeCallback, } from "$root"; import { cfg_map, @@ -13,6 +12,7 @@ type GeocoderReverseResult, type GeolocationPoint, } from "@radroots/util"; + import LayoutBottomButton from "../layout/layout-bottom-button.svelte"; let { basis, @@ -21,11 +21,13 @@ }: { basis: { visible: boolean; - lc_geocode: LcGeocodeCallback; - lc_submit: CallbackPromiseGeneric<{ - map_geop: GeolocationPoint | undefined; - map_geoc: GeocoderReverseResult | undefined; - }>; + on_submit: CallbackPromiseGeneric< + | { + map_geop: GeolocationPoint | undefined; + map_geoc: GeocoderReverseResult | undefined; + } + | undefined + >; }; map_geop?: GeolocationPoint; map_geoc?: GeocoderReverseResult; @@ -66,23 +68,30 @@ 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 + <LayoutBottomButton> + <ButtonLayoutPair basis={{ - label: `Choose location`, - callback: async () => { - await basis.lc_submit({ map_geoc, map_geop }); - if (map) map.setCenter(cfg_map.coords.default); + continue: { + label: `Choose location`, + disabled: false, + callback: async () => { + await basis.on_submit({ map_geoc, map_geop }); + if (map) map.setCenter(cfg_map.coords.default); + }, + }, + back: { + label: `Back`, + visible: true, + callback: async () => { + await basis.on_submit(undefined); + }, }, }} /> - </div> + </LayoutBottomButton> </div> </EnvelopeLowerFull> diff --git a/apps-lib/src/lib/components/form/form-entry-input.svelte b/apps-lib/src/lib/components/form/form-entry-input.svelte @@ -32,7 +32,7 @@ layer: 1, }, el: { - // id: id ? fmt_id(id) : undefined, + id: id ? fmt_id(id) : undefined, layer: 1, classes: `fade-in`, placeholder: basis.input_placeholder || ``, 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 @@ -2,6 +2,7 @@ import { EntryWrap, fmt_id, + get_context, Input, LayoutTrellisLine, lib_fmt_price, @@ -14,14 +15,12 @@ num_str, type ElementCallbackValue, type FormField, - type I18nTranslateFunction, - type I18nTranslateLocale, } from "@radroots/util"; + const { ls, locale } = get_context(`lib`); + let { basis, - ls, - locale, val_input_price = $bindable(``), val_sel_currency = $bindable(``), val_sel_qty_unit = $bindable(``), @@ -31,8 +30,6 @@ input_field?: FormField; callback_input: ElementCallbackValue; }; - ls: I18nTranslateFunction; - locale: I18nTranslateLocale; val_input_price: string; val_sel_currency: string; val_sel_qty_unit: string; diff --git a/apps-lib/src/lib/components/form/form-entry-price.svelte b/apps-lib/src/lib/components/form/form-entry-price.svelte @@ -2,6 +2,7 @@ import { EntryWrap, fmt_id, + get_context, Input, LayoutTrellisLine, lib_fmt_price, @@ -14,14 +15,12 @@ form_fields, mass_units, type ElementCallbackValue, - type I18nTranslateFunction, - type I18nTranslateLocale, } from "@radroots/util"; + const { ls, locale } = get_context(`lib`); + let { basis, - ls, - locale, val_input_price = $bindable(``), val_sel_currency = $bindable(``), val_sel_quantity_unit = $bindable(``), @@ -32,8 +31,6 @@ input_placeholder?: string; callback_input?: ElementCallbackValue; }; - ls: I18nTranslateFunction; - locale: I18nTranslateLocale; val_input_price: string; val_sel_currency: string; val_sel_quantity_unit: string; diff --git a/apps-lib/src/lib/components/form/form-entry-quantity.svelte b/apps-lib/src/lib/components/form/form-entry-quantity.svelte @@ -2,6 +2,7 @@ import { EntryWrap, fmt_id, + get_context, Input, LayoutTrellisLine, Select, @@ -11,12 +12,12 @@ form_fields, mass_units, type ElementCallbackValue, - type I18nTranslateFunction, } from "@radroots/util"; + const { ls } = get_context(`lib`); + let { basis, - ls, val_input_quantity = $bindable(``), val_sel_quantity_unit = $bindable(``), val_sel_quantity_label = $bindable(``), @@ -27,7 +28,6 @@ input_placeholder?: string; callback_input?: ElementCallbackValue; }; - ls: I18nTranslateFunction; val_input_quantity: string; val_sel_quantity_unit: string; val_sel_quantity_label: string; 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 @@ -3,6 +3,7 @@ EntryLine, EntrySelect, fmt_id, + get_context, LayoutTrellisLine, type ISelectCallback, type ISelectOption, @@ -10,12 +11,12 @@ import { type CallbackPromiseGeneric, type FormField, - type I18nTranslateFunction, } from "@radroots/util"; + const { ls } = get_context(`lib`); + let { basis, - ls, val_sel = $bindable(``), val_sel_input = $bindable(``), }: { @@ -29,7 +30,6 @@ input_field?: FormField; entry_label?: string; }; - ls: I18nTranslateFunction; val_sel: string; val_sel_input: string; } = $props(); diff --git a/apps-lib/src/lib/components/form/form-entry-textarea.svelte b/apps-lib/src/lib/components/form/form-entry-textarea.svelte @@ -42,7 +42,7 @@ classes: `h-full`, }, el: { - classes: `h-full px-2 overflow-hidden`, + classes: `h-full overflow-hidden`, id: id ? fmt_id(id) : undefined, sync: true, placeholder: basis.placeholder || ``, diff --git a/apps-lib/src/lib/components/layout/layout-trellis-line.svelte b/apps-lib/src/lib/components/layout/layout-trellis-line.svelte @@ -1,5 +1,10 @@ <script lang="ts"> - import { Glyph, type IBasisOpt, type ILayoutTrellisLine } from "$root"; + import { + app_lo, + Glyph, + type IBasisOpt, + type ILayoutTrellisLine, + } from "$root"; import { fmt_cl } from "@radroots/util"; import type { Snippet } from "svelte"; import { fade } from "svelte/transition"; @@ -14,7 +19,7 @@ </script> <div - class={`${fmt_cl(basis?.classes)} flex flex-col w-trellis_line gap-[6px] justify-start items-center`} + class={`${fmt_cl(basis?.classes)} flex flex-col w-lo_line_entry_${$app_lo} gap-[6px] justify-start items-center`} > {#if (basis?.label && `value` in basis?.label) || basis?.notify} <div diff --git a/apps-lib/src/lib/components/layout/layout-view.svelte b/apps-lib/src/lib/components/layout/layout-view.svelte @@ -43,8 +43,6 @@ 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 diff --git a/apps-lib/src/lib/components/lib/css.svelte b/apps-lib/src/lib/components/lib/css.svelte @@ -1 +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-lo_view_main_ios0 -bottom-lo_view_main_ios1 -bottom-lo_view_main_webm0 -bottom-lo_view_main_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-lo_view_main_ios0 -top-lo_view_main_ios1 -top-lo_view_main_webm0 -top-lo_view_main_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_lo_view_main_ios0 -translate-y-h_lo_view_main_ios1 -translate-y-h_lo_view_main_webm0 -translate-y-h_lo_view_main_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-lo_view_main_ios0 bottom-lo_view_main_ios1 bottom-lo_view_main_webm0 bottom-lo_view_main_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-lo_view_main_ios0 h-lo_view_main_ios1 h-lo_view_main_webm0 h-lo_view_main_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-lo_view_main_ios0 max-h-lo_view_main_ios1 max-h-lo_view_main_webm0 max-h-lo_view_main_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-lo_view_main_ios0 min-h-lo_view_main_ios1 min-h-lo_view_main_webm0 min-h-lo_view_main_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_lo_view_main_ios0 pb-h_lo_view_main_ios1 pb-h_lo_view_main_webm0 pb-h_lo_view_main_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_lo_view_main_ios0 pt-h_lo_view_main_ios1 pt-h_lo_view_main_webm0 pt-h_lo_view_main_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-lo_view_main_ios0 top-lo_view_main_ios1 top-lo_view_main_webm0 top-lo_view_main_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_lo_view_main_ios0 translate-y-h_lo_view_main_ios1 translate-y-h_lo_view_main_webm0 translate-y-h_lo_view_main_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 +<div class="hidden -bottom-lo_bottom_button_ios0 -bottom-lo_bottom_button_ios1 -bottom-lo_bottom_button_webm0 -bottom-lo_bottom_button_webm1 -bottom-lo_view_main_ios0 -bottom-lo_view_main_ios1 -bottom-lo_view_main_webm0 -bottom-lo_view_main_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_line_entry_ios0 -left-lo_line_entry_ios1 -left-lo_line_entry_webm0 -left-lo_line_entry_webm1 -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_line_entry_ios0 -right-lo_line_entry_ios1 -right-lo_line_entry_webm0 -right-lo_line_entry_webm1 -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-lo_view_main_ios0 -top-lo_view_main_ios1 -top-lo_view_main_webm0 -top-lo_view_main_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_line_entry_ios0 -translate-x-w_lo_line_entry_ios1 -translate-x-w_lo_line_entry_webm0 -translate-x-w_lo_line_entry_webm1 -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_lo_view_main_ios0 -translate-y-h_lo_view_main_ios1 -translate-y-h_lo_view_main_webm0 -translate-y-h_lo_view_main_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-lo_view_main_ios0 bottom-lo_view_main_ios1 bottom-lo_view_main_webm0 bottom-lo_view_main_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-lo_view_main_ios0 h-lo_view_main_ios1 h-lo_view_main_webm0 h-lo_view_main_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_line_entry_ios0 left-lo_line_entry_ios1 left-lo_line_entry_webm0 left-lo_line_entry_webm1 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-lo_view_main_ios0 max-h-lo_view_main_ios1 max-h-lo_view_main_webm0 max-h-lo_view_main_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_line_entry_ios0 max-w-lo_line_entry_ios1 max-w-lo_line_entry_webm0 max-w-lo_line_entry_webm1 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-lo_view_main_ios0 min-h-lo_view_main_ios1 min-h-lo_view_main_webm0 min-h-lo_view_main_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_line_entry_ios0 min-w-lo_line_entry_ios1 min-w-lo_line_entry_webm0 min-w-lo_line_entry_webm1 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_lo_view_main_ios0 pb-h_lo_view_main_ios1 pb-h_lo_view_main_webm0 pb-h_lo_view_main_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_line_entry_ios0 pl-w_lo_line_entry_ios1 pl-w_lo_line_entry_webm0 pl-w_lo_line_entry_webm1 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_line_entry_ios0 pr-w_lo_line_entry_ios1 pr-w_lo_line_entry_webm0 pr-w_lo_line_entry_webm1 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_lo_view_main_ios0 pt-h_lo_view_main_ios1 pt-h_lo_view_main_webm0 pt-h_lo_view_main_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_line_entry_ios0 right-lo_line_entry_ios1 right-lo_line_entry_webm0 right-lo_line_entry_webm1 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-lo_view_main_ios0 top-lo_view_main_ios1 top-lo_view_main_webm0 top-lo_view_main_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_line_entry_ios0 translate-x-w_lo_line_entry_ios1 translate-x-w_lo_line_entry_webm0 translate-x-w_lo_line_entry_webm1 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_lo_view_main_ios0 translate-y-h_lo_view_main_ios1 translate-y-h_lo_view_main_webm0 translate-y-h_lo_view_main_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_line_entry_ios0 w-lo_line_entry_ios1 w-lo_line_entry_webm0 w-lo_line_entry_webm1 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/components/lib/image-blob.svelte b/apps-lib/src/lib/components/lib/image-blob.svelte @@ -1,36 +1,26 @@ <script lang="ts"> import { type IImageBlob } from "$root"; - import { onDestroy, onMount } from "svelte"; + import { fmt_cl } from "@radroots/util"; 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) {} - }); + const img_src = $derived( + basis.data + ? URL.createObjectURL( + new Blob([basis.data], { + type: "image/jpeg", + }), + ) + : undefined, + ); </script> -{#if img_url} +{#if img_src} <img - id={basis.id || null} - src={img_url} - alt={basis.alt || null} + id={basis?.id || null} + class={`${fmt_cl(basis?.classes)}`} + src={img_src || null} + 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 @@ -1,33 +1,21 @@ <script lang="ts"> - import { type IImagePath } from "$root"; - import { fmt_cl } from "@radroots/util"; - import { onMount } from "svelte"; + import { get_context, ImageBlob, type IImagePath } from "$root"; + import ImageSrc from "./image-src.svelte"; + const { lc_img_bin } = get_context(`lib`); let { basis }: { basis: IImagePath } = $props(); - let img_src = $state(``); - - onMount(async () => { - try { - if (basis.path) img_src = basis.path; - } catch (e) { - } finally { - } - }); + const img_path = $derived(basis.path); </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 img_path} + {@const is_bin = img_path.startsWith(`file:`)} + + {#if is_bin} + {#await lc_img_bin(img_path) then data} + <ImageBlob basis={{ data, ...basis }} /> + {/await} + {:else} + <ImageSrc basis={{ src: img_path, ...basis }} /> + {/if} {/if} diff --git a/apps-lib/src/lib/components/lib/image-src.svelte b/apps-lib/src/lib/components/lib/image-src.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { type IImageSource } from "$root"; + import { fmt_cl } from "@radroots/util"; + + let { basis }: { basis: IImageSource } = $props(); + + const img_src = $derived(basis.src); +</script> + +{#if img_src} + <img + id={basis?.id || null} + class={`${fmt_cl(basis?.classes)}`} + src={img_src || null} + 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.svelte b/apps-lib/src/lib/components/lib/image.svelte @@ -0,0 +1,16 @@ +<script lang="ts"> + import { type IImage } from "$root"; + import { fmt_cl } from "@radroots/util"; + + let { basis }: { basis: IImage } = $props(); +</script> + +{#if basis.src} + <img + id={basis?.id || null} + class={`${fmt_cl(basis?.classes)}`} + src={basis.src} + alt={basis?.alt || null} + style={`height: 100%; width: 100%; object-fit: cover; display: block;`} + /> +{/if} diff --git a/apps-lib/src/lib/components/lib/text-area.svelte b/apps-lib/src/lib/components/lib/text-area.svelte @@ -1,14 +1,12 @@ <script lang="ts"> import { browser } from "$app/environment"; - import { handle_err, idb, type ITextArea } from "$root"; + import { idb, type ITextArea } from "$root"; import { fmt_cl, fmt_textarea_value, parse_layer, - sleep, value_constrain_textarea, } from "@radroots/util"; - import { onMount } from "svelte"; let { basis, @@ -25,24 +23,8 @@ 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); - })(); - } + if (browser && id && basis?.sync && value) idb.set(id, value); }); const handle_on_input = async (el: HTMLTextAreaElement): Promise<void> => { @@ -60,7 +42,6 @@ } 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); @@ -90,6 +71,6 @@ }} {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`} + class={`${fmt_cl(basis.classes)} el-textarea py-4 px-[18px] 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/map/map-marker-area.svelte b/apps-lib/src/lib/components/map/map-marker-area.svelte @@ -1,11 +1,17 @@ <script lang="ts"> - import { MapMarkerAreaDisplay, type IMapMarkerArea } from "$root"; + import { + get_context, + MapMarkerAreaDisplay, + type IMapMarkerArea, + } from "$root"; import { type GeocoderReverseResult, type GeolocationPoint, } from "@radroots/util"; import { Marker, Popup } from "svelte-maplibre"; + const { lc_geocode } = get_context(`lib`); + let { basis, map_geop = $bindable(), @@ -23,7 +29,7 @@ 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); + const geoc = await lc_geocode(map_geop); if (geoc) map_geoc = geoc; }} > diff --git a/apps-lib/src/lib/components/marker/marker-indexed-view.svelte b/apps-lib/src/lib/components/marker/marker-indexed-view.svelte @@ -19,7 +19,7 @@ > {#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`} + class={`flex flex-row flex-grow h-full justify-start items-center ${basis.index_curr >= index ? `bg-layer-0-glyph` : `bg-layer-2-surface`} rounded-full el-re`} onclick={async () => { await basis.callback_index(index); }} diff --git a/apps-lib/src/lib/components/trellis/trellis.svelte b/apps-lib/src/lib/components/trellis/trellis.svelte @@ -1,6 +1,7 @@ <script lang="ts"> import { app_lo, + get_context, TrellisDefaultLabel, TrellisInput, TrellisOffset, @@ -9,25 +10,21 @@ TrellisTouch, type ITrellis, } from "$root"; - import { - fmt_cl, - parse_layer, - type I18nTranslateFunction, - } from "@radroots/util"; + import { fmt_cl, parse_layer } from "@radroots/util"; import type { Snippet } from "svelte"; + const { ls } = get_context(`lib`); + let { basis, el_default, el_offset, el_append, - ls, }: { basis: ITrellis; el_default?: Snippet; el_offset?: Snippet; el_append?: Snippet; - ls: I18nTranslateFunction; } = $props(); const hide_border_t = $derived( 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 @@ -1,17 +1,15 @@ <script lang="ts"> - import { Glyph, LoadSymbol, type LcPhotoAddCallback } from "$root"; - import { type I18nTranslateFunction } from "@radroots/util"; + import { get_context, Glyph, LoadSymbol } from "$root"; + + const { ls, lc_photos_add } = get_context(`lib`); let { basis, - ls, photo_path = $bindable(``), }: { basis: { loading?: boolean; - lc_handle_photo_add: LcPhotoAddCallback; }; - ls: I18nTranslateFunction; photo_path: string; } = $props(); </script> @@ -20,8 +18,8 @@ <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; + const photo_paths_add = await lc_photos_add(); + if (photo_paths_add) photo_path = photo_paths_add[0]; }} > {#if basis.loading} diff --git a/apps-lib/src/lib/components/upload/image-upload-simple.svelte b/apps-lib/src/lib/components/upload/image-upload-simple.svelte @@ -1,40 +1,42 @@ <script lang="ts"> import { + get_context, Glyph, ImagePath, ImageUploadButtonsAspect, - type LcPhotoAddMultipleCallback, } from "$root"; import { list_assign } from "@radroots/util"; + const { ls, lc_photos_add } = get_context(`lib`); + let { - basis, photo_paths = $bindable([]), }: { - basis: { - lc_handle_photo_add: LcPhotoAddMultipleCallback; - }; photo_paths: string[]; } = $props(); - let img_data = $state(``); + let img_path = $state(``); + + $effect(() => { + console.log(`img_path `, img_path); + }); </script> <div class={`flex flex-col w-full gap-4 justify-center items-center`}> - {#if img_data} + {#if img_path} <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 }} /> + <ImagePath basis={{ path: img_path }} /> </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(); + const photo_paths_add = await lc_photos_add(); if (photo_paths_add) { photo_paths = list_assign(photo_paths, photo_paths_add); - img_data = photo_paths[0]; + img_path = photo_paths[0]; } }} > @@ -52,7 +54,7 @@ <p class={`font-sans font-[400] text-layer-0-glyph/80 capitalize`} > - {`Upload image`} + {`${$ls(`icu.upload_*`, { value: `${$ls(`common.image`)}` })}`} </p> </div> </button> @@ -60,7 +62,7 @@ <ImageUploadButtonsAspect basis={{ callback: async (ratio) => { - console.log(`ratio `, ratio); + // @todo }, }} /> 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 @@ -1,10 +1,8 @@ <script lang="ts"> - import { CarouselItem, FormLineLedger } from "$root"; - import { - area_units, - form_fields, - type I18nTranslateFunction, - } from "@radroots/util"; + import { CarouselItem, FormLineLedger, get_context } from "$root"; + import { area_units, form_fields } from "@radroots/util"; + + const { ls } = get_context(`lib`); let { val_farmname = $bindable(``), @@ -14,7 +12,6 @@ val_farmcontact = $bindable(``), farm_geop_lat, farm_geop_lng, - ls, }: { val_farmname: string; val_farmaddress: string; @@ -23,7 +20,6 @@ val_farmcontact: string; farm_geop_lat: string; farm_geop_lng: string; - ls: I18nTranslateFunction; } = $props(); </script> 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 @@ -8,9 +8,8 @@ app_lo, focus_map_marker, geop_is_valid, + get_context, handle_err, - type LcGeocodeCallback, - type LcGeocodeCurrentCallback, } from "$root"; import { type GeocoderReverseResult, @@ -18,17 +17,14 @@ } from "@radroots/util"; import { onMount } from "svelte"; + const { lc_geop_current, lc_geocode } = get_context(`lib`); + 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; @@ -41,10 +37,10 @@ onMount(async () => { try { - const geop = await basis.lc_geop_current(); + const geop = await lc_geop_current(); if (!geop) return; map_geop = { ...geop }; - const geoc = await basis.lc_geocode(geop); + const geoc = await lc_geocode(geop); if (!geoc) return; map_geoc = geoc; if (map) map.setCenter([map_geop.lng, map_geop.lat]); @@ -66,7 +62,6 @@ bind:map_geop bind:map_geoc basis={{ - lc_geocode: basis.lc_geocode, show_display: true, }} /> 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 @@ -1,5 +1,5 @@ <script lang="ts"> - import { Map, MapMarkerArea, type LcGeocodeCallback } from "$root"; + import { get_context, Map, MapMarkerArea } from "$root"; import { fmt_geolocation_address, geol_lat_fmt, @@ -7,25 +7,19 @@ parse_geol_point_tup, parse_tup_geop_point, type CallbackPromiseGeneric, - type FarmBasis, + type FarmExtended, type GeolocationPointTuple, - type I18nTranslateFunction, - type I18nTranslateLocale, } from "@radroots/util"; import { onMount } from "svelte"; + const { ls, locale } = get_context(`lib`); + let { basis, - lc_geocode, - lc_handle_farm_view, - ls, - locale, + on_handle_farm_view, }: { - basis: FarmBasis; - lc_geocode: LcGeocodeCallback; - lc_handle_farm_view: CallbackPromiseGeneric<string>; - ls: I18nTranslateFunction; - locale: I18nTranslateLocale; + basis: FarmExtended; + on_handle_farm_view: CallbackPromiseGeneric<string>; } = $props(); let map: maplibregl.Map | undefined = $state(undefined); @@ -61,7 +55,7 @@ <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.farm.id) await lc_handle_farm_view(basis.farm.id); + if (basis.farm.id) await on_handle_farm_view(basis.farm.id); }} > <div class={`flex flex-col w-full gap-2 justify-center items-center`}> @@ -106,7 +100,6 @@ {map_geop} basis={{ no_drag: true, - lc_geocode, }} /> </Map> 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 @@ -1,38 +1,37 @@ <script lang="ts"> import { app_lo, + get_context, Glyph, ImagePath, lib_parse_currency_marker, - type IViewFarmsProductsAddSubmission, + type IViewFarmsProductsAddSubmitPayload, } from "$root"; - import { - symbols, - type I18nTranslateFunction, - type I18nTranslateLocale, - } from "@radroots/util"; + import { parse_geocode_address, symbols } from "@radroots/util"; + + const { ls, locale } = get_context(`lib`); let { basis, - ls, - locale, }: { basis: { - data: IViewFarmsProductsAddSubmission | undefined; + data: IViewFarmsProductsAddSubmitPayload | undefined; }; - ls: I18nTranslateFunction; - locale: I18nTranslateLocale; } = $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`} + class={`flex flex-col h-[20rem] w-lo_line_entry_${$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] }} /> + <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`} @@ -58,6 +57,9 @@ class={`flex flex-col h-[10rem] w-full px-3 py-2 justify-start items-center`} > {#if basis.data} + {@const data_geoc_address = parse_geocode_address( + basis.data.geocode_result, + )} <div class={`flex flex-row w-full justify-between items-center`}> <div class={`flex flex-row gap-1 justify-start items-center`}> <p @@ -106,21 +108,25 @@ {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`}> - {symbols.bullet} - </p> - <p class={`font-sans font-[600] text-th-black`}> - {`${basis.data.geolocation_address.country}`} - </p> + {#if data_geoc_address} + <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`}> + {`${data_geoc_address.primary}, ${data_geoc_address.admin}`} + </p> + <p class={`font-sans font-[600] text-th-black`}> + {symbols.bullet} + </p> + <p class={`font-sans font-[600] text-th-black`}> + {`${data_geoc_address.country}`} + </p> + </div> </div> - </div> + {/if} {/if} </div> </div> diff --git a/apps-lib/src/lib/index.ts b/apps-lib/src/lib/index.ts @@ -47,6 +47,8 @@ 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 ImageSrc } from "./components/lib/image-src.svelte" +export { default as Image } from "./components/lib/image.svelte" export { default as InputIdb } from "./components/lib/input-idb.svelte" export { default as InputValue } from "./components/lib/input-value.svelte" export { default as Input } from "./components/lib/input.svelte" @@ -98,6 +100,7 @@ export * from "./store/layout.js" export * from "./store/ndk.js" export * from "./store/nostr.js" export * from "./types/component.js" +export * from "./types/context.js" export * from "./types/interface.js" export * from "./types/lib.js" export * from "./types/view/farm.js" @@ -105,6 +108,7 @@ export * from "./types/view/lib.js" export * from "./types/view/profile.js" export * from "./util/carousel.js" export * from "./util/casl.js" +export * from "./util/context/lib.js" export * from "./util/idb.js" export * from "./util/lib.js" export * from "./util/nostr/nostr-poll-relays.js" @@ -116,6 +120,7 @@ export * from "./util/validation/farm.js" export * from "./util/view.js" export { default as FarmsAdd } from "./view/farms-add.svelte" export { default as FarmsDetails } from "./view/farms-details.svelte" +export { default as FarmsProductsAdd } from "./view/farms-products-add.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" diff --git a/apps-lib/src/lib/types/component.ts b/apps-lib/src/lib/types/component.ts @@ -1,4 +1,4 @@ -import type { CallbackRoute, ICb, ICbG, ICbGOpt, ICbOpt, IClOpt, IDisabledOpt, IEntryWrap, IGl, IGlOpt, IGlyphKey, IIdGOpt, IIdOpt, ILabel, ILabelOpt, ILabelTup, ILoadingOpt, ILy, ILyOpt, INavigationRoutePreventRouteNav, LcGeocodeCallback, NavigationRouteParamKey } from "$root"; +import type { CallbackRoute, ICb, ICbG, ICbGOpt, ICbOpt, IClOpt, IDisabledOpt, IEntryWrap, IGl, IGlOpt, IGlyphKey, IIdGOpt, IIdOpt, ILabel, ILabelOpt, ILabelTup, ILoadingOpt, ILy, ILyOpt, INavigationRoutePreventRouteNav, NavigationRouteParamKey } from "$root"; import type { CallbackPromise, CallbackPromiseGeneric, ElementCallbackMount, ElementCallbackValue, ElementCallbackValueBlur, ElementCallbackValueKeydown, FormField, GeometryGlyphDimension, GeometryScreenPosition, GeometryScreenPositionHorizontal, GlyphKey, GlyphWeight, NavigationParamTuple } from "@radroots/util"; export type IButtonNavRound = ICb & IDisabledOpt & ILoadingOpt & IGlyphKey; @@ -239,7 +239,6 @@ export type IButtonSimple = ILyOpt & { export type IMapMarkerArea = { show_display?: boolean; no_drag?: boolean; - lc_geocode: LcGeocodeCallback; } export type ILayoutTrellisLine = ILabelOpt & diff --git a/apps-lib/src/lib/types/context.ts b/apps-lib/src/lib/types/context.ts @@ -0,0 +1,22 @@ +import type { LocalCallbackColorMode, LocalCallbackGeocode, LocalCallbackGeocodeCurrent, LocalCallbackGuiAlert, LocalCallbackGuiConfirm, LocalCallbackImgBin, LocalCallbackPhotosAddMultiple, LocalCallbackPhotosUpload } from "$root"; +import type { I18nTranslateFunction, I18nTranslateLocale } from "@radroots/util"; + +export type ContextKeys = + | `lib`; + +export type ContextMap = { + lib: LibContext; +}; + +export type LibContext = { + ls: I18nTranslateFunction; + locale: I18nTranslateLocale; + lc_color_mode: LocalCallbackColorMode; + lc_gui_alert: LocalCallbackGuiAlert; + lc_gui_confirm: LocalCallbackGuiConfirm; + lc_geocode: LocalCallbackGeocode; + lc_photos_add: LocalCallbackPhotosAddMultiple; + lc_img_bin: LocalCallbackImgBin; + lc_geop_current: LocalCallbackGeocodeCurrent; + lc_photos_upload: LocalCallbackPhotosUpload; +}; +\ No newline at end of file diff --git a/apps-lib/src/lib/types/interface.ts b/apps-lib/src/lib/types/interface.ts @@ -23,6 +23,12 @@ export type ICbG<T> = { callback: CallbackPromiseGeneric<T> | never; }; +export type ICbMouseEventOpt = ICbGOpt< + MouseEvent & { + currentTarget: EventTarget & HTMLImageElement; + } +>; + export type ICbGOpt<T> = Partial<ICbG<T>>; export type ICl = { @@ -201,19 +207,22 @@ export type INavigationRoutePreventRoute = { prevent_route: CallbackPromise; }; -export type IImageBlob = IIdOpt & { +export type IImage = IIdOpt & IClOpt & { + src?: string; + alt?: string; +}; + +export type IImageBlob = IIdOpt & IClOpt & { data: Uint8Array | undefined; alt?: string; }; -export type IImagePath = IClOpt & - ICbGOpt< - MouseEvent & { - currentTarget: EventTarget & HTMLImageElement; - } - > & - IIdOpt & { - path?: string; - alt?: string; - }; +export type IImageSource = IIdOpt & IClOpt & { + src?: string; + alt?: string; +}; + +export type IImagePath = Omit<IImage, 'src'> & { + path?: string; +}; diff --git a/apps-lib/src/lib/types/lib.ts b/apps-lib/src/lib/types/lib.ts @@ -1,5 +1,5 @@ -import type { INavigationRoute } from "$root"; -import type { CallbackPromise, CallbackPromiseFull, CallbackPromiseResult, GeocoderReverseResult, GeolocationPoint, IClientGeolocationPosition, NavigationParamTuple } from "@radroots/util"; +import type { INavigationRoute, ISelectOption } from "$root"; +import type { CallbackPromise, CallbackPromiseFull, CallbackPromiseGeneric, CallbackPromiseResult, GeocoderReverseResult, GeolocationPoint, IClientGeolocationPosition, MediaImageUploadResult, NavigationParamTuple } from "@radroots/util"; export type CallbackRoute<T extends string> = CallbackPromise | INavigationRoute<T>; @@ -13,9 +13,12 @@ export type NavigationRouteParamKey = NavigationRouteParamId | NavigationRoutePa export type NavigationRouteParamTuple = NavigationParamTuple<NavigationRouteParamKey>; export type NavigationPreviousParam<T extends string> = { route: T, label?: string; params?: NavigationRouteParamTuple[] } -export type LcGuiAlertCallback = CallbackPromiseFull<string, boolean>; -export type LcGuiConfirmCallback = CallbackPromiseFull<string | { message: string; ok?: string; cancel?: string }, boolean>; -export type LcGeocodeCurrentCallback = CallbackPromiseResult<IClientGeolocationPosition>; -export type LcGeocodeCallback = CallbackPromiseFull<GeolocationPoint, GeocoderReverseResult | undefined>; -export type LcPhotoAddCallback = CallbackPromiseResult<string>; -export type LcPhotoAddMultipleCallback = CallbackPromiseResult<string[]>; +export type LocalCallbackColorMode = CallbackPromiseGeneric<ISelectOption<string>> +export type LocalCallbackGuiAlert = CallbackPromiseFull<string, boolean>; +export type LocalCallbackGuiConfirm = CallbackPromiseFull<string | { message: string; ok?: string; cancel?: string }, boolean>; +export type LocalCallbackGeocode = CallbackPromiseFull<GeolocationPoint, GeocoderReverseResult | undefined>; +export type LocalCallbackGeocodeCurrent = CallbackPromiseResult<IClientGeolocationPosition>; +export type LocalCallbackImgBin = CallbackPromiseFull<string, Uint8Array | undefined>; +export type LocalCallbackPhotosAdd = CallbackPromiseResult<string>; +export type LocalCallbackPhotosAddMultiple = CallbackPromiseResult<string[]>; +export type LocalCallbackPhotosUpload = CallbackPromiseFull<{ url: string, path: string }, MediaImageUploadResult | undefined>; diff --git a/apps-lib/src/lib/types/view/farm.ts b/apps-lib/src/lib/types/view/farm.ts @@ -1,12 +1,14 @@ -import type { FarmBasis, GeocoderReverseResult, GeolocationAddress, GeolocationPoint } from "@radroots/util"; +import type { FarmExtended, GeocoderReverseResult, GeolocationPoint } from "@radroots/util"; export type IViewFarmsData = { - list: FarmBasis[]; + list: FarmExtended[]; }; -export type IViewFarmsDetailsData = FarmBasis; +export type IViewFarmsDetailsData = FarmExtended; -export type IViewFarmsProductsAddSubmission = { +export type IViewFarmsProductsAddData = FarmExtended; + +export type IViewFarmsProductsAddSubmitPayload = { product: string; process: string; description: string; @@ -18,7 +20,7 @@ export type IViewFarmsProductsAddSubmission = { quantity_unit: string; quantity_label: string; geolocation_point: GeolocationPoint; - geolocation_address: GeolocationAddress; + geocode_result: GeocoderReverseResult; }; export type IViewFarmsAddSubmission = { diff --git a/apps-lib/src/lib/types/view/lib.ts b/apps-lib/src/lib/types/view/lib.ts @@ -3,9 +3,9 @@ import type { CallbackPromiseGeneric } from "@radroots/util"; export type IViewHomeData = {}; export type IViewOnMount<TypeCallbackParam> = { - lc_on_mount: CallbackPromiseGeneric<TypeCallbackParam>; + on_mount: CallbackPromiseGeneric<TypeCallbackParam>; }; export type IViewOnDestroy<TypeCallbackParam> = { - lc_on_destroy: CallbackPromiseGeneric<TypeCallbackParam>; + on_destroy: CallbackPromiseGeneric<TypeCallbackParam>; }; diff --git a/apps-lib/src/lib/util/context/lib.ts b/apps-lib/src/lib/util/context/lib.ts @@ -0,0 +1,10 @@ +import type { ContextMap } from "$lib/types/context"; +import { getContext, setContext } from "svelte"; + +export const get_context = <K extends keyof ContextMap>(key: K): ContextMap[K] => { + return getContext(key) as ContextMap[K]; +}; + +export const set_context = <K extends keyof ContextMap>(key: K, value: ContextMap[K]): void => { + setContext(key, value); +}; +\ No newline at end of file diff --git a/apps-lib/src/lib/util/lib.ts b/apps-lib/src/lib/util/lib.ts @@ -3,7 +3,7 @@ import { goto } from "$app/navigation"; import { page } from "$app/state"; import { win_h, win_w, type CallbackRoute, type NavigationRouteParamKey } from "$root"; import type { ColorMode, ThemeKey } from "@radroots/theme"; -import { encode_route, fmt_geometry_point_coords, fmt_price, parse_currency_marker, type GeolocationPoint, type GeometryPoint, type IErrorCatchCallback } from "@radroots/util"; +import { encode_route, fmt_geometry_point_coords, fmt_price, geo_point_to_geometry, parse_currency_marker, type GeolocationPoint, type GeometryPoint, type IErrorCatchCallback } from "@radroots/util"; import { get } from "svelte/store"; export const get_store = get; @@ -73,6 +73,13 @@ export const lib_fmt_geometry_point_coords = (locale: string, point: GeometryPoi return fmt_geometry_point_coords(point, locale); }; +export const loc_fmt_geo_point_coords = (locale: string, geo_point: GeolocationPoint): string => { + const point = geo_point_to_geometry(geo_point); + if (!point) return ``; //@todo + 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]`)) { diff --git a/apps-lib/src/lib/util/validation/farm.ts b/apps-lib/src/lib/util/validation/farm.ts @@ -1,5 +1,6 @@ -import type { IViewFarmsAddSubmission } from "$lib/types/view/farm"; -import { form_fields, schema_geocode_result, schema_geolocation_point, zf_numf_pos } from "@radroots/util"; +import { dev } from "$app/environment"; +import type { IViewFarmsAddSubmission, IViewFarmsProductsAddSubmitPayload } from "$lib/types/view/farm"; +import { form_fields, schema_geocode_result, schema_geolocation_point, util_rxp, zf_numf_pos, zf_numi_pos, zf_price } from "@radroots/util"; import { z } from "zod"; export const schema_view_farms_add_submission: z.ZodSchema<IViewFarmsAddSubmission> = z.object({ @@ -9,4 +10,19 @@ export const schema_view_farms_add_submission: z.ZodSchema<IViewFarmsAddSubmissi farm_contact_name: z.string().regex(form_fields.contact_name.validate).optional(), geolocation_point: schema_geolocation_point, geocode_result: schema_geocode_result, +}); + +export const schema_view_farms_products_add_submission: z.ZodSchema<IViewFarmsProductsAddSubmitPayload> = z.object({ + product: z.string().regex(form_fields.product_key.validate), + process: z.string().regex(form_fields.product_process.validate), + description: z.string().regex(form_fields.product_description.validate), + price_amount: zf_price, + price_currency: z.string().regex(form_fields.price_currency.validate), + price_quantity_unit: z.string().regex(form_fields.quantity_unit.validate), + photos: z.array(z.string().regex(dev ? util_rxp.url_image_upload_dev : util_rxp.url_image_upload)), + quantity_amount: zf_numi_pos, + quantity_unit: z.string().regex(form_fields.quantity_unit.validate), + quantity_label: z.string().regex(form_fields.quantity_label.validate), + geolocation_point: schema_geolocation_point, + geocode_result: schema_geocode_result, }); \ No newline at end of file diff --git a/apps-lib/src/lib/view/farms-add.svelte b/apps-lib/src/lib/view/farms-add.svelte @@ -15,14 +15,12 @@ focus_map_marker, geop_init, geop_is_valid, + get_context, handle_err, LayoutView, PageToolbar, type CallbackRoute, type IViewFarmsAddSubmission, - type LcGeocodeCallback, - type LcGeocodeCurrentCallback, - type LcGuiAlertCallback, } from "$root"; import { el_id, @@ -34,31 +32,23 @@ type GeocoderReverseResult, type GeolocationAddress, type GeolocationPoint, - type I18nTranslateFunction, - type I18nTranslateLocale, } from "@radroots/util"; import { onMount } from "svelte"; + const { ls, locale, lc_gui_alert, lc_geop_current, lc_geocode } = + get_context(`lib`); + let { basis, - ls, - locale, }: { basis: { callback_route?: CallbackRoute<string>; - lc_gui_alert: LcGuiAlertCallback; - lc_geop_current: LcGeocodeCurrentCallback; - lc_geocode: LcGeocodeCallback; - lc_submit: CallbackPromiseGeneric<{ - data_s: IViewFarmsAddSubmission; + on_submit: CallbackPromiseGeneric<{ + payload: IViewFarmsAddSubmission; }>; }; - ls: I18nTranslateFunction; - locale: I18nTranslateLocale; } = $props(); - let loading = $state(false); - let map_geop: GeolocationPoint = $state(geop_init()); let map_geoc: GeocoderReverseResult | undefined = $state(undefined); @@ -109,7 +99,7 @@ const handle_continue_1 = async (): Promise<void> => { if (!map_geop || !map_geoc) - return void basis.lc_gui_alert(`No farm location provided.`); //@todo + return void lc_gui_alert(`No farm location provided.`); //@todo const farms_add_submission = schema_view_farms_add_submission.safeParse( { farm_name: val_farmname, @@ -127,13 +117,11 @@ ); if (!farms_add_submission.success) { - return void basis.lc_gui_alert( + return void lc_gui_alert( `Request invalid: ${farms_add_submission.error}`, ); //@todo } - loading = true; - await basis.lc_submit({ data_s: farms_add_submission.data }); - loading = false; + await basis.on_submit({ payload: farms_add_submission.data }); }; const handle_continue = async (): Promise<void> => { @@ -149,10 +137,10 @@ switch ($casl_i) { case 1: { if (!geop_is_valid(map_geop)) { - const geop_cur = await basis.lc_geop_current(); + const geop_cur = await lc_geop_current(); if (geop_cur) { map_geop = geop_cur; - const geoc_cur = await basis.lc_geocode(geop_cur); + const geoc_cur = await lc_geocode(geop_cur); if (geoc_cur) map_geoc = geoc_cur; focus_map_marker(); } @@ -174,7 +162,7 @@ }} > {#snippet header_option()} - <!--{#if $casl_i === 0} + <!-- @todo {#if $casl_i === 0} <button class={`flex flex-row justify-center items-center`} onclick={async () => { @@ -203,10 +191,6 @@ 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 @@ -216,7 +200,6 @@ bind:val_farmarea_unit {farm_geop_lat} {farm_geop_lng} - {ls} /> </Carousel> </LayoutView> diff --git a/apps-lib/src/lib/view/farms-details.svelte b/apps-lib/src/lib/view/farms-details.svelte @@ -2,6 +2,7 @@ import { ButtonSimple, Empty, + get_context, Glyph, handle_err, LayoutPage, @@ -11,7 +12,6 @@ PageToolbar, type CallbackRoute, type IViewFarmsDetailsData, - type LcGeocodeCallback, } from "$root"; import { fmt_geolocation_address, @@ -21,27 +21,22 @@ parse_tup_geop_point, type CallbackPromiseGeneric, type GeolocationPointTuple, - type I18nTranslateFunction, - type I18nTranslateLocale, type IViewBasis, } from "@radroots/util"; import { onDestroy, onMount } from "svelte"; + const { ls, locale } = get_context(`lib`); + let { basis, - ls, - locale, }: { basis: IViewBasis<{ data: IViewFarmsDetailsData; 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>; + on_handle_farm_lot_add: CallbackPromiseGeneric<string>; + on_handle_farm_products_view: CallbackPromiseGeneric<string>; + on_handle_farm_orders_view: CallbackPromiseGeneric<string>; }>; - ls: I18nTranslateFunction; - locale: I18nTranslateLocale; } = $props(); let map: maplibregl.Map | undefined = $state(undefined); @@ -49,7 +44,7 @@ onMount(async () => { try { - if (basis.lc_on_mount) await basis.lc_on_mount(); + if (basis.on_mount) await basis.on_mount(); if (basis.data?.location) map_center = parse_geol_point_tup(basis.data?.location.point); if (map) { @@ -63,7 +58,7 @@ onDestroy(async () => { try { - if (basis.lc_on_destroy) await basis.lc_on_destroy(); + if (basis.on_destroy) await basis.on_destroy(); } catch (e) { handle_err(e, `on_destroy`); } @@ -229,7 +224,7 @@ label: `View Products`, callback: async () => { if (basis.data?.farm.id) - await basis.lc_handle_farm_products_view( + await basis.on_handle_farm_products_view( basis.data?.farm.id, ); }, @@ -240,7 +235,7 @@ label: `View Orders`, callback: async () => { if (basis.data?.farm.id) - await basis.lc_handle_farm_orders_view( + await basis.on_handle_farm_orders_view( basis.data?.farm.id, ); }, @@ -260,7 +255,6 @@ {map_geop} basis={{ no_drag: true, - lc_geocode: basis.lc_geocode, }} /> </Map> 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,669 @@ +<script lang="ts"> + import { + app_lo, + ButtonLayoutPair, + Carousel, + CarouselItem, + casl_dec, + casl_i, + casl_imax, + casl_inc, + casl_init, + FarmsProductsReviewCard, + fmt_id, + FormEntryPrice, + FormEntryQuantity, + FormEntrySelect, + FormEntrySelectInput, + FormEntryTextarea, + get_context, + handle_err, + idb, + ImageUploadSimple, + LayoutBottomButton, + LayoutView, + loc_fmt_geo_point_coords, + MapLocationSelectEnvelope, + MarkerIndexedView, + PageToolbar, + schema_view_farms_products_add_submission, + type CallbackRoute, + type ISelectOption, + type IViewFarmsProductsAddData, + type IViewFarmsProductsAddSubmitPayload, + } from "$root"; + import { + el_id, + fmt_geocode_address, + fmt_geolocation_address, + form_fields, + location_basis_to_geo_point, + obj_keys_maxnum, + parse_float, + parse_geop_point, + parse_int, + parse_trade_key, + sleep, + str_trunc, + trade, + trade_keys, + type CallbackPromiseFull, + type CallbackPromiseGeneric, + type GeocoderReverseResult, + type GeolocationPoint, + type IViewBasis, + type LocationBasis, + } from "@radroots/util"; + import { onMount } from "svelte"; + + const { ls, locale, lc_geocode, lc_gui_alert, lc_geop_current } = + get_context(`lib`); + + let { + basis, + }: { + basis: IViewBasis<{ + data: IViewFarmsProductsAddData; + callback_route?: CallbackRoute<string>; + on_handle_farm_lot_add: CallbackPromiseGeneric<string>; + on_handle_photo_envelope_edit: CallbackPromiseGeneric<number>; + on_handle_tradepr_key_toggle: CallbackPromiseFull<boolean, string>; + on_submit: CallbackPromiseGeneric<{ + payload: IViewFarmsProductsAddSubmitPayload; + farm_id: string; + geolocation_id?: string; + }>; + }>; + } = $props(); + + const casl_param: Record< + "default", + { + index_map: Record<number, { label_desc: string }>; + } + > = { + default: { + index_map: { + 0: { + label_desc: `${$ls(`icu.add_*`, { value: `${$ls(`icu.*_information`, { value: `${$ls(`common.product`)}` })}` })}`, + }, + 1: { + label_desc: `${$ls(`icu.upload_*`, { value: `${$ls(`common.images`)}` })}`, + }, + 2: { + label_desc: `${$ls(`icu.*_price`, { value: `${$ls(`common.product`)}` })}`, + }, + 3: { + label_desc: `${$ls(`icu.add_*`, { value: `${$ls(`common.location`)}` })}`, + }, + 4: { + label_desc: `${$ls(`common.review`)}`, + }, + }, + }, + }; + + let choose_location_map_geop: GeolocationPoint | undefined = + $state(undefined); + let choose_location_map_geoc: GeocoderReverseResult | undefined = + $state(undefined); + + let loading_submit = $state(false); + let payload_submit: IViewFarmsProductsAddSubmitPayload | undefined = + $state(undefined); + + let photo_paths = $state([]); + + 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_location_sel_geoc: GeocoderReverseResult | undefined = + $state(undefined); + + 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`); + + 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: LocationBasis[] = $derived( + [ + basis.data?.location ? basis.data?.location : undefined, + ...(basis.data?.lots?.length + ? [...basis.data?.lots.map((i) => i.location)] + : []), + ].filter((i) => typeof i !== `undefined`), + ); + + const find_location_by_id = (id: string): LocationBasis | undefined => { + return list_farm_geolocations.find((i) => i.id === id) || undefined; + }; + + const product_geolocation_id: string | undefined = $derived( + product_location_sel === `*map-current` + ? undefined + : find_location_by_id(product_location_sel)?.id, + ); + + const product_location: LocationBasis | undefined = $derived( + product_location_sel + ? find_location_by_id(product_location_sel) + : undefined, + ); + + $effect(() => { + console.log(`choose_location_map_geoc `, choose_location_map_geoc); + }); + + $effect(() => { + console.log(`product_location_sel_geoc `, product_location_sel_geoc); + }); + + const product_geolocation_point: GeolocationPoint | undefined = $derived( + product_location_sel === `*map-current` + ? choose_location_map_geop + : location_basis_to_geo_point(product_location) || undefined, + ); + + const product_geocode_reverse: GeocoderReverseResult | undefined = $derived( + product_location_sel === `*map-current` + ? choose_location_map_geoc + : product_location_sel_geoc, + ); + + const entries_farm_location: ISelectOption<string>[] = $derived( + basis.data?.location + ? [ + { + value: basis.data.location.id, + label: basis.data.location.address + ? fmt_geolocation_address(basis.data.location.address) + : basis.data.location.point + ? loc_fmt_geo_point_coords( + $locale, + basis.data.location.point, + ) + : ``, + }, + ] + : [], + ); + const entries_farm_lots_locations: ISelectOption<string>[] = $derived( + basis.data?.lots?.length + ? basis.data.lots + .filter( + (i) => + i.location && + i.location?.address && + !entries_farm_location + .map((i) => i.value) + .includes(i.location.id), + ) + .map(({ location }) => + location && location.address + ? { + value: location.id, + label: `${location.address.primary}, ${location.address.admin}, ${location.address.country}`, + } + : undefined, + ) + .filter((i) => typeof i !== `undefined`) + : [], + ); + + $effect(() => { + if (product_key_sel === ``) product_process_sel = ``; + }); + + 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> => { + if (value in trade.key) product_process_sel = ``; + el_id(fmt_id(`key_wrap`))?.classList.remove(`layer-1-ring-apply`); + if (value === `*other`) { + await handle_tradepr_key_toggle(true); + product_process_sel = ``; + } + }; + + 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 === `*other`) handle_product_process_toggle(true); + else 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 = async ( + value: string, + ): Promise<void> => { + console.log(`value `, value); + el_id(fmt_id(`location_wrap`))?.classList.remove(`layer-1-ring-apply`); + if (value === `*map`) { + product_location_sel_geoc = undefined; + if (choose_location_map_geop) choose_location_map_geop = undefined; + if (choose_location_map_geoc) choose_location_map_geoc = undefined; + const geop = await lc_geop_current(); + if (!geop) { + return void (await lc_gui_alert( + `${$ls(`icu.failure_*`, { value: `${$ls(`icu.reading_*`, { value: `${$ls(`common.location`)}`.toLowerCase() })}` })}`, + )); + } + const geoc = await lc_geocode(geop); + if (!geoc) return; //@todo + choose_location_map_geoc = geoc; + choose_location_map_geop = parse_geop_point(geop); + product_location_map_toggle = true; + await sleep(1000); + product_location_sel = ``; + } else if (value) { + const value_product_location = find_location_by_id(value); + if (!value_product_location) return; //@todo + const geoc = await lc_geocode(value_product_location.point); + if (geoc) product_location_sel_geoc = geoc; + } + }; + + 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 (await lc_gui_alert( + `${$ls(`farm.product.validation.${kv_id}.required`)}`, + )); + } + await casl_inc(); + }; + + const continue_1 = async (): Promise<void> => { + if (!photo_paths.length) + return void (await 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_geocode_reverse) + return void (await lc_gui_alert(`Product location not provided`)); + const vp_payload_submit = + schema_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, + geocode_result: product_geocode_reverse, + } satisfies IViewFarmsProductsAddSubmitPayload); + if (!vp_payload_submit.success) + return void (await lc_gui_alert( + `Errors: ${vp_payload_submit.error}`, + )); + payload_submit = vp_payload_submit.data; + await casl_inc(); + }; + + const continue_4 = async (): Promise<void> => { + if (!payload_submit) + return void (await lc_gui_alert(`${$ls(`error.default.failure`)}`)); + loading_submit = true; + try { + await basis.on_submit({ + payload: payload_submit, + farm_id: basis.data.farm.id, + geolocation_id: product_geolocation_id, + }); + } catch { + } finally { + loading_submit = false; + } + }; + + const handle_continue = async (): Promise<void> => { + try { + switch ($casl_i) { + 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`); + } + }; + + const handle_back = async (): Promise<void> => { + try { + switch ($casl_i) { + default: + return await casl_dec(); + } + } catch (e) { + await handle_err(e, `handle_back`); + } + }; +</script> + +<LayoutView basis={{ classes: `` }}> + <PageToolbar + basis={{ + header: { + label: `${`${$ls(`common.farm`)}`} / ${`${$ls(`common.product`)}`}`, + callback_route: basis.callback_route || { route: `/farms` }, + }, + }} + /> + <LayoutBottomButton> + <ButtonLayoutPair + basis={{ + continue: { + label: + $casl_i === $casl_imax + ? `${$ls(`common.post`)}` + : `${$ls(`common.continue`)}`, + disabled: false, + loading: loading_submit, + callback: handle_continue, + }, + back: { + label: `${$ls(`common.back`)}`, + visible: $casl_i > 0, + callback: handle_back, + }, + }} + /> + </LayoutBottomButton> + <div + class={`flex flex-col w-lo_line_entry_${$app_lo} pt-4 pb-6 px-1 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: `${$ls(`common.product`)}`, + visible_input: product_key_sel_toggle, + input_placeholder: `${$ls(`icu.enter_the_*`, { value: `${$ls(`icu.*_name`, { value: `${$ls(`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: `${$ls(`icu.choose_*`, { value: `${$ls(`common.product`)}`.toLowerCase() })}`, + disabled: true, + }, + ...trade_keys.map((i) => ({ + value: i, + label: `${$ls(`products.product.key.${i}.name`)}`, + })), + { + value: `*other`, + label: + product_key_sel === `*other` + ? `` + : `${$ls(`common.other`)}`, + }, + ], + }} + /> + <FormEntrySelectInput + bind:val_sel={product_process_sel} + bind:val_sel_input={product_process_sel_input} + basis={{ + id: `process`, + entry_label: `${$ls(`common.process`)}`, + visible_input: product_process_sel_toggle, + input_placeholder: `${$ls(`icu.enter_the_*`, { value: `${$ls(`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: `${$ls(`icu.choose_*`, { value: `${$ls(`common.process`)}`.toLowerCase() })}`, + disabled: true, + }, + ...product_process_list.map((i) => ({ + value: i, + label: `${$ls(`products.product.key.${tradepr_key_parsed}.process.${i}`)}`, + })), + { + value: `*other`, + label: + product_key_sel === `*other` + ? `` + : `${$ls(`common.other`)}`, + }, + ] + : [ + { + value: ``, + label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.process`)}`.toLowerCase() })}`, + disabled: true, + }, + { + value: `*choose-product`, + label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.product`)}`.toLowerCase() })}`, + }, + ] + .concat( + product_key_sel === `*other` + ? [ + ...trade.default.process.map( + (i) => ({ + value: i, + label: `${$ls(`products.product.default.process.${i}`)}`, + }), + ), + { + value: `*other`, + label: `${$ls(`common.other`)}`, + }, + ] + : [], + ) + .filter((i) => + product_key_sel + ? i.value !== `*choose-product` + : true, + ), + }} + /> + <FormEntryTextarea + bind:val={product_description_input} + basis={{ + id: `description`, + entry_label: `${$ls(`common.description`)}`, + field: form_fields.product_description, + placeholder: `${$ls(`common.describe_your_product`)}`, + }} + /> + </CarouselItem> + <CarouselItem basis={{ classes: `px-4` }}> + <ImageUploadSimple bind:photo_paths /> + </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: `${$ls(`icu.enter_*`, { value: `${$ls(`common.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: `${$ls(`icu.enter_*`, { value: `${$ls(`common.quantity`)}` })}`, + }} + /> + </CarouselItem> + <CarouselItem> + <FormEntrySelect + bind:val={product_location_sel} + basis={{ + id: `location`, + entry_label: `${$ls(`common.location`)}`, + callback: async ({ value }) => + await handle_product_location_sel(value), + entries: choose_location_map_geoc + ? [ + { + value: ``, + label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.location`)}`.toLowerCase() })}`, + disabled: true, + }, + { + value: `*map`, + label: `${$ls(`common.choose_on_map`)}`, + }, + { + value: `*map-current`, + label: `${str_trunc(fmt_geocode_address(choose_location_map_geoc))} (map)`, + }, + ...entries_farm_location, + ...entries_farm_lots_locations, + ] + : [ + { + value: ``, + label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.location`)}`.toLowerCase() })}`, + disabled: true, + }, + ...entries_farm_location, + ...entries_farm_lots_locations, + { + value: `*map`, + label: `${$ls(`common.choose_on_map`)}`, + }, + ], + }} + /> + </CarouselItem> + <CarouselItem> + <div class={`flex flex-col w-full justify-center items-center`}> + <FarmsProductsReviewCard + basis={{ + data: payload_submit, + }} + /> + </div> + </CarouselItem> + </Carousel> +</LayoutView> +<MapLocationSelectEnvelope + bind:map_geop={choose_location_map_geop} + bind:map_geoc={choose_location_map_geoc} + basis={{ + visible: product_location_map_toggle, + on_submit: async (params) => { + try { + if (!params) { + choose_location_map_geoc = undefined; + return; + } + const { map_geoc, map_geop } = params; + if (map_geoc && map_geop) product_location_sel = `*map-current`; + else product_location_sel = ``; + } catch (e) { + await handle_err(e, `on_submit`); + } finally { + product_location_map_toggle = false; + } + }, + }} +/> diff --git a/apps-lib/src/lib/view/farms.svelte b/apps-lib/src/lib/view/farms.svelte @@ -3,6 +3,7 @@ ButtonLabelDashed, Fade, FarmsDisplayLiEl, + get_context, GlyphButtonSimple, handle_err, idb_init_page, @@ -11,31 +12,25 @@ PageToolbar, type CallbackRoute, type IViewFarmsData, - type LcGeocodeCallback, } from "$root"; import { type CallbackPromise, type CallbackPromiseGeneric, - type I18nTranslateFunction, - type I18nTranslateLocale, type IViewBasis, } from "@radroots/util"; import { onMount } from "svelte"; + const { ls } = get_context(`lib`); + let { basis, - ls, - locale, }: { basis: IViewBasis<{ data?: IViewFarmsData; callback_route?: CallbackRoute<string>; - lc_geocode: LcGeocodeCallback; - lc_handle_farm_add: CallbackPromise; - lc_handle_farm_view: CallbackPromiseGeneric<string>; + on_handle_farm_add: CallbackPromise; + on_handle_farm_view: CallbackPromiseGeneric<string>; }>; - ls: I18nTranslateFunction; - locale: I18nTranslateLocale; } = $props(); onMount(async () => { @@ -63,7 +58,7 @@ basis={{ label: `${$ls(`icu.add_*`, { value: `${$ls(`common.farm`)}` })}`, callback: async () => { - await basis.lc_handle_farm_add(); + await basis.on_handle_farm_add(); }, }} /> @@ -77,10 +72,7 @@ {#each basis.data?.list || [] as li} <FarmsDisplayLiEl basis={li} - lc_geocode={basis.lc_geocode} - lc_handle_farm_view={basis.lc_handle_farm_view} - {ls} - {locale} + on_handle_farm_view={basis.on_handle_farm_view} /> {/each} {:else} @@ -88,7 +80,7 @@ basis={{ label: `Add farm`, callback: async () => { - await basis.lc_handle_farm_add(); + await basis.on_handle_farm_add(); }, }} /> diff --git a/apps-lib/src/lib/view/home.svelte b/apps-lib/src/lib/view/home.svelte @@ -1,6 +1,7 @@ <script lang="ts"> import { ButtonSimple, + get_context, handle_err, idb_init_page, LayoutPage, @@ -9,23 +10,19 @@ PageToolbar, type IViewHomeData, } from "$root"; - import { - type CallbackPromise, - type I18nTranslateFunction, - type IViewBasis, - } from "@radroots/util"; + import { type CallbackPromise, type IViewBasis } from "@radroots/util"; import { onMount } from "svelte"; + const { ls } = get_context(`lib`); + let { - ls, basis, }: { basis: IViewBasis<{ data?: IViewHomeData; - lc_handle_farms: CallbackPromise; - lc_handle_products: CallbackPromise; + on_handle_farms: CallbackPromise; + on_handle_products: CallbackPromise; }>; - ls: I18nTranslateFunction; } = $props(); onMount(async () => { @@ -52,7 +49,7 @@ basis={{ label: `${$ls(`common.farms`)}`, callback: async () => { - await basis.lc_handle_farms(); + await basis.on_handle_farms(); }, }} /> diff --git a/apps-lib/src/lib/view/profile-edit.svelte b/apps-lib/src/lib/view/profile-edit.svelte @@ -4,6 +4,7 @@ import Empty from "$lib/components/lib/empty.svelte"; import { fmt_id, + get_context, handle_err, idb_init_page, Input, @@ -16,25 +17,24 @@ import { type CallbackPromiseGeneric, type ElementCallbackValue, - type I18nTranslateFunction, type IViewBasis, } from "@radroots/util"; import { onMount } from "svelte"; + const { ls } = get_context(`lib`); + let { basis, - ls, val_field = $bindable(``), }: { basis: IViewBasis<{ data?: IViewProfileEditData; - lc_handle_back: CallbackPromiseGeneric<{ + on_handle_back: CallbackPromiseGeneric<{ field: ViewProfileEditFieldKey; public_key: string; }>; - lc_handle_input: ElementCallbackValue; + on_handle_input: ElementCallbackValue; }>; - ls: I18nTranslateFunction; val_field: string; } = $props(); @@ -78,7 +78,7 @@ sync: true, classes: `pl-6 h-entry_line text-layer-1-glyph bg-layer-1-surface rounded-2xl`, placeholder: input_placeholder, - callback: basis.lc_handle_input, + callback: basis.on_handle_input, }} /> {/if} @@ -93,7 +93,7 @@ basis={{ glyph: `arrow-left`, callback: async () => { - await basis.lc_handle_back({ + await basis.on_handle_back({ field: basis_data.field, public_key: basis_data.public_key, }); diff --git a/apps-lib/src/lib/view/profile.svelte b/apps-lib/src/lib/view/profile.svelte @@ -2,50 +2,45 @@ import { ButtonRoundNav, FloatPage, + get_context, Glyph, handle_err, idb_init_page, - ImageBlob, ImagePath, ImageUploadAddPhoto, NavigationTabs, SelectMenu, type IViewOnDestroy, type IViewProfileData, - type LcPhotoAddCallback, type ViewProfileEditFieldKey, } from "$root"; import { symbols, type CallbackPromise, - type CallbackPromiseFull, type CallbackPromiseGeneric, - type I18nTranslateFunction, type IViewBasis, } from "@radroots/util"; import { onDestroy, onMount } from "svelte"; + const { ls } = get_context(`lib`); + let { basis, - ls, photo_path = $bindable(``), }: { basis: IViewBasis<{ data?: IViewProfileData; loading_photo_upload: boolean; loading_photo_upload_open: boolean; - lc_handle_back: CallbackPromiseGeneric<{ + on_handle_back: CallbackPromiseGeneric<{ is_photo_existing: boolean; }>; - lc_handle_photo_add: LcPhotoAddCallback; - lc_handle_photo_options: CallbackPromise; - lc_fs_read_bin: CallbackPromiseFull<string, Uint8Array | undefined>; - lc_handle_edit_profile_field: CallbackPromiseGeneric<{ + on_handle_photo_options: CallbackPromise; + on_handle_edit_profile_field: CallbackPromiseGeneric<{ field: ViewProfileEditFieldKey; }>; }> & IViewOnDestroy<{ public_key: string }>; - ls: I18nTranslateFunction; photo_path: string; } = $props(); @@ -64,8 +59,8 @@ onDestroy(async () => { try { - if (basis.data) - await basis.lc_on_destroy({ + if (basis.data?.public_key) + await basis.on_destroy({ public_key: basis.data.public_key, }); } catch (e) { @@ -104,7 +99,7 @@ glyph: `arrow-left`, loading: basis.loading_photo_upload, callback: async () => { - await basis.lc_handle_back({ + await basis.on_handle_back({ is_photo_existing: photo_overlay_visible, }); }, @@ -135,30 +130,22 @@ <ButtonRoundNav basis={{ glyph: `images-square`, - callback: basis.lc_handle_photo_options, + callback: basis.on_handle_photo_options, }} /> </SelectMenu> </FloatPage> {#if basis.data.picture || photo_path} {@const img_path = photo_path || basis.data.picture || ``} - {#if img_path.startsWith(`file://`)} - {#await basis.lc_fs_read_bin(img_path) then data} - <ImageBlob basis={{ data }} /> - {/await} - {:else if img_path.startsWith(`http://`) || img_path.startsWith(`https://`)} - <ImagePath basis={{ path: img_path }} /> - {/if} + <ImagePath basis={{ path: img_path }} /> {:else} <div class={`flex flex-row justify-start items-center -translate-y-8`} > <ImageUploadAddPhoto bind:photo_path - {ls} basis={{ loading: basis.loading_photo_upload_open, - lc_handle_photo_add: basis.lc_handle_photo_add, }} /> </div> @@ -175,7 +162,7 @@ <button class={`group flex flex-row justify-center items-center`} onclick={async () => { - await basis.lc_handle_edit_profile_field({ + await basis.on_handle_edit_profile_field({ field: `display_name`, }); }} @@ -199,7 +186,7 @@ <button class={`group flex flex-row justify-center items-center`} onclick={async () => { - await basis.lc_handle_edit_profile_field({ + await basis.on_handle_edit_profile_field({ field: `name`, }); }} @@ -239,7 +226,7 @@ <button class={`group flex flex-row justify-center items-center`} onclick={async () => { - await basis.lc_handle_edit_profile_field({ + await basis.on_handle_edit_profile_field({ field: `about`, }); }} diff --git a/apps-lib/src/lib/view/settings.svelte b/apps-lib/src/lib/view/settings.svelte @@ -1,32 +1,26 @@ <script lang="ts"> import { app_thc, + get_context, handle_err, idb_init_page, LayoutTrellis, LayoutView, PageToolbar, Trellis, - type ISelectOption, type ITrellisKind, } from "$root"; - import { - symbols, - type CallbackPromiseGeneric, - type I18nTranslateFunction, - type IViewBasis, - } from "@radroots/util"; + import { symbols, type IViewBasis } from "@radroots/util"; import { onMount } from "svelte"; + const { ls, lc_color_mode } = get_context(`lib`); + let { basis, - ls, }: { basis: IViewBasis<{ trellis_2?: (ITrellisKind | undefined)[]; - lc_color_mode: CallbackPromiseGeneric<ISelectOption<string>>; }>; - ls: I18nTranslateFunction; } = $props(); onMount(async () => { @@ -48,7 +42,6 @@ /> <LayoutTrellis> <Trellis - {ls} basis={{ layer: 1, title: { @@ -93,7 +86,7 @@ ], }, ], - callback: basis.lc_color_mode, + callback: lc_color_mode, }, end: { glyph: { @@ -106,7 +99,6 @@ }} /> <Trellis - {ls} basis={{ layer: 1, list: basis.trellis_2,