web_lib

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

commit 39bbd067ee7b042efed196359fee679593a54b7c
parent 09be0dd9513e61e8ed3966426b510b4242c31e2f
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Mon, 10 Feb 2025 10:20:52 +0000

apps-lib: refactor library for app/0.0.1

Diffstat:
Mapps-lib/.gitignore | 28+++++++++++++++++++---------
Mapps-lib/README.md | 2+-
Mapps-lib/package.json | 117++++++++++++++++++++++++++++++++++++++++---------------------------------------
Rapps-lib/src/lib/global.d.ts -> apps-lib/src/global.d.ts | 0
Dapps-lib/src/lib/component/button/button-arrow.svelte | 58----------------------------------------------------------
Dapps-lib/src/lib/component/button/button-layout-pair.svelte | 55-------------------------------------------------------
Dapps-lib/src/lib/component/button/button-layout.svelte | 39---------------------------------------
Dapps-lib/src/lib/component/carousel/carousel-item.svelte | 6------
Dapps-lib/src/lib/component/carousel/carousel.svelte | 12------------
Dapps-lib/src/lib/component/entry/entry-line.svelte | 56--------------------------------------------------------
Dapps-lib/src/lib/component/entry/entry-wrap.svelte | 21---------------------
Dapps-lib/src/lib/component/float/float-page-button.svelte | 37-------------------------------------
Dapps-lib/src/lib/component/float/float-tabs.svelte | 101-------------------------------------------------------------------------------
Dapps-lib/src/lib/component/glyph/glyph-button-simple.svelte | 40----------------------------------------
Dapps-lib/src/lib/component/glyph/glyph-button.svelte | 21---------------------
Dapps-lib/src/lib/component/glyph/glyph-circle.svelte | 22----------------------
Dapps-lib/src/lib/component/glyph/glyph-title-select-label.svelte | 32--------------------------------
Dapps-lib/src/lib/component/label/label-display.svelte | 28----------------------------
Dapps-lib/src/lib/component/lib/fade.svelte | 11-----------
Dapps-lib/src/lib/component/lib/load-screen.svelte | 12------------
Dapps-lib/src/lib/component/lib/logo-circle.svelte | 41-----------------------------------------
Dapps-lib/src/lib/component/lib/splash-screen.svelte | 12------------
Dapps-lib/src/lib/component/lib/view.svelte | 14--------------
Dapps-lib/src/lib/component/map/map-marker-dot.svelte | 15---------------
Dapps-lib/src/lib/component/map/map-point-display.svelte | 28----------------------------
Dapps-lib/src/lib/component/map/map-point-select.svelte | 70----------------------------------------------------------------------
Dapps-lib/src/lib/component/map/map-popup-point-geolocation.svelte | 74--------------------------------------------------------------------------
Dapps-lib/src/lib/component/nav/nav-option.svelte | 82-------------------------------------------------------------------------------
Dapps-lib/src/lib/component/nav/nav.svelte | 167-------------------------------------------------------------------------------
Dapps-lib/src/lib/component/page/page-header.svelte | 41-----------------------------------------
Dapps-lib/src/lib/component/page/page-toolbar.svelte | 55-------------------------------------------------------
Dapps-lib/src/lib/component/trellis/trellis-default-label.svelte | 30------------------------------
Dapps-lib/src/lib/component/trellis/trellis-end.svelte | 30------------------------------
Dapps-lib/src/lib/component/trellis/trellis-input.svelte | 82-------------------------------------------------------------------------------
Dapps-lib/src/lib/component/trellis/trellis-line.svelte | 40----------------------------------------
Dapps-lib/src/lib/component/trellis/trellis-offset.svelte | 70----------------------------------------------------------------------
Dapps-lib/src/lib/component/trellis/trellis-row-display-value.svelte | 41-----------------------------------------
Dapps-lib/src/lib/component/trellis/trellis-row-label.svelte | 61-------------------------------------------------------------
Dapps-lib/src/lib/component/trellis/trellis-select.svelte | 57---------------------------------------------------------
Dapps-lib/src/lib/component/trellis/trellis-title.svelte | 62--------------------------------------------------------------
Dapps-lib/src/lib/component/trellis/trellis-touch.svelte | 34----------------------------------
Dapps-lib/src/lib/component/trellis/trellis.svelte | 124-------------------------------------------------------------------------------
Aapps-lib/src/lib/components/button/button-horizontal-pair-slide.svelte | 45+++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/button/button-label-dashed.svelte | 24++++++++++++++++++++++++
Aapps-lib/src/lib/components/button/button-label-glyph.svelte | 42++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/button/button-layout-pair.svelte | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/button/button-layout.svelte | 46++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/button/button-round.svelte | 25+++++++++++++++++++++++++
Aapps-lib/src/lib/components/button/button-simple-glyph.svelte | 33+++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/button/button-simple.svelte | 21+++++++++++++++++++++
Aapps-lib/src/lib/components/button/float-page-button.svelte | 40++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/button/glyph-button-simple.svelte | 47+++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/button/glyph-button.svelte | 18++++++++++++++++++
Aapps-lib/src/lib/components/carousel/carousel-flow-item.svelte | 18++++++++++++++++++
Aapps-lib/src/lib/components/carousel/carousel-flow.svelte | 37+++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/carousel/carousel-item.svelte | 18++++++++++++++++++
Aapps-lib/src/lib/components/carousel/carousel.svelte | 24++++++++++++++++++++++++
Aapps-lib/src/lib/components/envelope/envelope-lower-full.svelte | 29+++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/envelope/map-location-select-envelope.svelte | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/entry-line-idb.svelte | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/entry-line.svelte | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/entry-multiline.svelte | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/entry-select.svelte | 48++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/entry-wrap.svelte | 36++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-entry-input.svelte | 44++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-entry-price-quantity.svelte | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-entry-price.svelte | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-entry-quantity.svelte | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-entry-select-input.svelte | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-entry-select.svelte | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-entry-textarea.svelte | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-line-ledger-label-select-label.svelte | 36++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-line-ledger-select.svelte | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-line-ledger.svelte | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/form-review-display.svelte | 48++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/form/trade_field_display_kv.svelte | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/glyph/glyph-circle.svelte | 20++++++++++++++++++++
Aapps-lib/src/lib/components/glyph/glyph.svelte | 18++++++++++++++++++
Aapps-lib/src/lib/components/label/label-display.svelte | 30++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/layout/layout-bottom-button.svelte | 12++++++++++++
Aapps-lib/src/lib/components/layout/layout-page.svelte | 18++++++++++++++++++
Aapps-lib/src/lib/components/layout/layout-trellis-line.svelte | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/layout/layout-trellis.svelte | 18++++++++++++++++++
Aapps-lib/src/lib/components/layout/layout-view.svelte | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/layout/layout-window.svelte | 38++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/css.svelte | 2++
Rapps-lib/src/lib/component/lib/empty.svelte -> apps-lib/src/lib/components/lib/empty.svelte | 0
Aapps-lib/src/lib/components/lib/fade.svelte | 21+++++++++++++++++++++
Aapps-lib/src/lib/components/lib/image-blob.svelte | 36++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/image-path.svelte | 32++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/input-value.svelte | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/input.svelte | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/label-swap.svelte | 41+++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/load-symbol-white.svelte | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/load-symbol.svelte | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rapps-lib/src/lib/component/lib/logo-circle-sm.svelte -> apps-lib/src/lib/components/lib/logo-circle-sm.svelte | 0
Aapps-lib/src/lib/components/lib/logo-letters.svelte | 5+++++
Aapps-lib/src/lib/components/lib/logo_circle.svelte | 18++++++++++++++++++
Aapps-lib/src/lib/components/lib/select-menu.svelte | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/select.svelte | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/text-area.svelte | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/theme.svelte | 17+++++++++++++++++
Aapps-lib/src/lib/components/lib/toast.svelte | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/lib/wrap-border.svelte | 22++++++++++++++++++++++
Aapps-lib/src/lib/components/map/map-marker-area-display.svelte | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/map/map-marker-area.svelte | 44++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/map/map.svelte | 42++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/marker/marker-indexed-view.svelte | 31+++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/navigation/navigation-tabs.svelte | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/navigation/page-header.svelte | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/navigation/page-toolbar.svelte | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis-default-label.svelte | 36++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis-end.svelte | 36++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis-input.svelte | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis-line.svelte | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis-offset.svelte | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis-row-display-value.svelte | 47+++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis-row-label.svelte | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis-select.svelte | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis-title.svelte | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis-touch.svelte | 41+++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/trellis/trellis.svelte | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/upload/image-upload-add-photo.svelte | 41+++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/upload/image-upload-buttons-aspect.svelte | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/components/upload/image-upload-simple.svelte | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dapps-lib/src/lib/el/glyph.svelte | 19-------------------
Dapps-lib/src/lib/el/image-blob.svelte | 43-------------------------------------------
Dapps-lib/src/lib/el/image-path.svelte | 48------------------------------------------------
Dapps-lib/src/lib/el/input.svelte | 104-------------------------------------------------------------------------------
Dapps-lib/src/lib/el/label-swap.svelte | 32--------------------------------
Dapps-lib/src/lib/el/load-symbol.svelte | 64----------------------------------------------------------------
Dapps-lib/src/lib/el/select-menu.svelte | 64----------------------------------------------------------------
Dapps-lib/src/lib/el/select.svelte | 111-------------------------------------------------------------------------------
Dapps-lib/src/lib/el/styles.svelte | 2--
Dapps-lib/src/lib/el/text-area.svelte | 98-------------------------------------------------------------------------------
Dapps-lib/src/lib/el/toast.svelte | 55-------------------------------------------------------
Dapps-lib/src/lib/feature/image-upload-add-photo.svelte | 36------------------------------------
Dapps-lib/src/lib/feature/search-result-container.svelte | 16----------------
Dapps-lib/src/lib/feature/search-result-display.svelte | 216-------------------------------------------------------------------------------
Aapps-lib/src/lib/features/farm/farms-add-casli-detail.svelte | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/features/farm/farms-add-casli-map.svelte | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/features/farm/farms-display-li-el.svelte | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/features/farm/farms-products-review-card.svelte | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/features/search/search-result-container.svelte | 21+++++++++++++++++++++
Aapps-lib/src/lib/features/search/search-result-display.svelte | 225+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapps-lib/src/lib/index.ts | 198+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Dapps-lib/src/lib/layout/layout-overlay-blur.svelte | 13-------------
Dapps-lib/src/lib/layout/layout-overlay-loading.svelte | 7-------
Dapps-lib/src/lib/layout/layout-overlay-splash.svelte | 7-------
Dapps-lib/src/lib/layout/layout-styles.svelte | 2--
Dapps-lib/src/lib/layout/layout-trellis.svelte | 11-----------
Dapps-lib/src/lib/layout/layout-view.svelte | 55-------------------------------------------------------
Dapps-lib/src/lib/layout/layout-window.svelte | 52----------------------------------------------------
Mapps-lib/src/lib/locale/en/common.json | 10++++++++++
Aapps-lib/src/lib/locale/en/farm.json | 16++++++++++++++++
Mapps-lib/src/lib/locale/en/measurement.json | 8+++++++-
Mapps-lib/src/lib/locale/en/trade.json | 7+++++++
Mapps-lib/src/lib/locale/i18n.ts | 2+-
Dapps-lib/src/lib/service/search/lib.ts | 67-------------------------------------------------------------------
Dapps-lib/src/lib/service/search/types.ts | 7-------
Mapps-lib/src/lib/store/app.ts | 8+++++---
Mapps-lib/src/lib/store/client.ts | 3++-
Mapps-lib/src/lib/store/component.ts | 6+++---
Mapps-lib/src/lib/store/ndk.ts | 4++--
Dapps-lib/src/lib/types/app.ts | 184-------------------------------------------------------------------------------
Dapps-lib/src/lib/types/component.ts | 156-------------------------------------------------------------------------------
Dapps-lib/src/lib/types/el.ts | 57---------------------------------------------------------
Dapps-lib/src/lib/types/feature.ts | 9---------
Dapps-lib/src/lib/types/interface.ts | 156-------------------------------------------------------------------------------
Dapps-lib/src/lib/types/model.ts | 35-----------------------------------
Dapps-lib/src/lib/types/util.ts | 25-------------------------
Dapps-lib/src/lib/types/view.ts | 41-----------------------------------------
Dapps-lib/src/lib/util/app.ts | 143-------------------------------------------------------------------------------
Mapps-lib/src/lib/util/carousel.ts | 5+++--
Mapps-lib/src/lib/util/casl.ts | 28++++++++++++++--------------
Dapps-lib/src/lib/util/component.ts | 11-----------
Dapps-lib/src/lib/util/conf.ts | 52----------------------------------------------------
Dapps-lib/src/lib/util/document.ts | 52----------------------------------------------------
Dapps-lib/src/lib/util/error.ts | 37-------------------------------------
Dapps-lib/src/lib/util/geolocation.ts | 48------------------------------------------------
Dapps-lib/src/lib/util/i18n.ts | 23-----------------------
Aapps-lib/src/lib/util/idb.ts | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dapps-lib/src/lib/util/kv.ts | 55-------------------------------------------------------
Aapps-lib/src/lib/util/lib.ts | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dapps-lib/src/lib/util/styles.ts | 43-------------------------------------------
Dapps-lib/src/lib/util/view.ts | 8--------
Dapps-lib/src/lib/view/farm-land-add.svelte | 422-------------------------------------------------------------------------------
Dapps-lib/src/lib/view/farm-land-view.svelte | 82-------------------------------------------------------------------------------
Dapps-lib/src/lib/view/farm-land.svelte | 141-------------------------------------------------------------------------------
Aapps-lib/src/lib/view/farms-add.svelte | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/view/farms-products-add.svelte | 648+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/view/farms-products-view.svelte | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/view/farms-view.svelte | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps-lib/src/lib/view/farms.svelte | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapps-lib/src/lib/view/home.svelte | 73+++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mapps-lib/src/lib/view/notifications.svelte | 4++--
Aapps-lib/src/lib/view/profile.svelte | 249+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapps-lib/src/lib/view/search.svelte | 101+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Dapps-lib/src/lib/view/settings-nostr.svelte | 134-------------------------------------------------------------------------------
Dapps-lib/src/lib/view/settings-profile-edit.svelte | 138-------------------------------------------------------------------------------
Dapps-lib/src/lib/view/settings-profile.svelte | 243-------------------------------------------------------------------------------
Mapps-lib/src/lib/view/settings.svelte | 389+++++++++++++++++--------------------------------------------------------------
Mapps-lib/svelte.config.js | 11+++++++++--
Mapps-lib/tsconfig.json | 27+++++++++++----------------
Aapps-lib/vite.config.ts | 11+++++++++++
205 files changed, 6981 insertions(+), 5789 deletions(-)

diff --git a/apps-lib/.gitignore b/apps-lib/.gitignore @@ -18,20 +18,30 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -# local env files -.env.local -.env.development.local -.env.test.local -.env.production.local - # turbo .turbo -.dev* + +# Output +.output +/build +dist + +# local env files +.env* +!.env.example + + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +# local .tmp* +.backup* +.dev* .vscode notes*.txt notes*.md -justfile -dist git-diff.txt +justfile diff --git a/apps-lib/README.md b/apps-lib/README.md @@ -1 +1 @@ -# svelte-lib +# lib-app diff --git a/apps-lib/package.json b/apps-lib/package.json @@ -1,60 +1,61 @@ { - "name": "@radroots/svelte-lib", - "version": "0.0.0", - "private": true, - "license": "GPLv3", - "type": "module", - "files": [ - "dist", - "!dist/**/*.test.*", - "!dist/**/*.spec.*" - ], - "exports": { - ".": { - "types": "./dist/index.d.ts", - "svelte": "./dist/index.js" - } - }, - "svelte": "./dist/index.js", - "types": "./dist/index.d.ts", - "scripts": { - "dev": "svelte-package -w", - "prebuild": "just build", - "build": "npm run check && svelte-package", - "preview": "vite preview", - "package": "svelte-kit sync && svelte-package && publint", - "prepublishOnly": "npm run package", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" - }, - "peerDependencies": { - "@sveltejs/kit": "^2.0.0", - "svelte": "^4.2.19" - }, - "devDependencies": { - "@sveltejs/kit": "^2.0.0", - "@sveltejs/package": "^2.0.0", - "@types/luxon": "^3.4.2", - "publint": "^0.1.9", - "svelte": "^4.2.19", - "svelte-check": "^3.8.6", - "svelte-preprocess": "^5.1.0", - "tslib": "^2.4.1", - "typescript": "^5.3.3", - "vite": "^5.0.11" - }, - "dependencies": { - "@nostr-dev-kit/ndk": "^2.10.7", - "@nostr-dev-kit/ndk-cache-dexie": "^2.5.8", - "@nostr-dev-kit/ndk-svelte": "^2.3.2", - "@radroots/theme": "workspace:*", - "@radroots/svelte-maplibre": "workspace:*", - "@sveltekit-i18n/base": "^1.3.7", - "@sveltekit-i18n/parser-icu": "^1.0.8", - "luxon": "^3.5.0", - "sveltekit-search-params": "^3.0.0" - }, - "publishConfig": { - "access": "public" - } + "name": "@radroots/lib-app", + "version": "0.0.0", + "private": true, + "license": "GPLv3", + "scripts": { + "dev": "svelte-package -w", + "prebuild": "npm run check", + "build": "svelte-package", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "prepack": "svelte-kit sync && svelte-package && publint", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "files": [ + "dist", + "!dist/**/*.test.*", + "!dist/**/*.spec.*" + ], + "sideEffects": [ + "**/*.css" + ], + "svelte": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "svelte": "./dist/index.js" + } + }, + "peerDependencies": { + "@sveltejs/kit": "^2.16.0", + "svelte": "^5.0.0" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^4.0.0", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/package": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@types/node": "^22.5.0", + "publint": "^0.3.2", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "typescript": "^5.0.0", + "vite": "^6.0.0" + }, + "dependencies": { + "@nostr-dev-kit/ndk": "^2.11.0", + "@nostr-dev-kit/ndk-cache-dexie": "^2.5.9", + "@nostr-dev-kit/ndk-svelte": "^2.4.0", + "@radroots/theme": "workspace:*", + "@radroots/util": "workspace:*", + "@radroots/svelte-maplibre": "workspace:*", + "@sveltekit-i18n/base": "^1.3.7", + "@sveltekit-i18n/parser-icu": "^1.0.8", + "luxon": "^3.5.0", + "sveltekit-search-params": "^3.0.0" + } } \ No newline at end of file diff --git a/apps-lib/src/lib/global.d.ts b/apps-lib/src/global.d.ts diff --git a/apps-lib/src/lib/component/button/button-arrow.svelte b/apps-lib/src/lib/component/button/button-arrow.svelte @@ -1,58 +0,0 @@ -<script lang="ts"> - import { Glyph, type CallbackPromise } from "$lib"; - import { onDestroy, onMount } from "svelte"; - - export let basis: { - label: string; - callback: CallbackPromise; - }; - - let visible_action = false; - - onMount(async () => { - try { - document.addEventListener("click", () => toggle(false)); - } catch (e) { - } finally { - } - }); - - onDestroy(() => { - document.removeEventListener("click", () => toggle(false)); - }); - - const toggle = (toggle_force?: boolean): void => { - visible_action = - typeof toggle_force === `boolean` ? toggle_force : !visible_action; - }; -</script> - -<div class={`relative inline-block`}> - <button - class={`flex flex-row w-auto h-10 gap-1 justify-center items-center el-re`} - on:click|stopPropagation={async () => { - toggle(); - }} - > - <button - class={`${visible_action ? `fade-in` : `hidden`} absolute top-0 left-0 flex flex-row h-full justify-start items-center el-re`} - on:click|stopPropagation={async () => { - await basis.callback(); - }} - > - <Glyph - basis={{ - classes: `text-layer-0-glyph`, - dim: `sm`, - weight: `bold`, - key: `arrow-left`, - }} - /> - </button> - <p - class={`font-circ font-[700] text-layer-0-glyph text-[1.6rem] tracking-tight ${visible_action ? `translate-x-8` : ``} duration-[350ms] el-re`} - > - {basis.label || ``} - </p> - </button> -</div> diff --git a/apps-lib/src/lib/component/button/button-layout-pair.svelte b/apps-lib/src/lib/component/button/button-layout-pair.svelte @@ -1,55 +0,0 @@ -<script lang="ts"> - import { - app_layout, - ButtonLayout, - Empty, - type CallbackPromise, - type IDisabledOpt, - } from "$lib"; - - export let basis: { - continue: IDisabledOpt & { - label: string; - callback: CallbackPromise; - }; - back?: IDisabledOpt & { - visible: boolean; - label?: string; - callback: CallbackPromise; - }; - }; -</script> - -<div class={`flex flex-col justify-center items-center`}> - <ButtonLayout - basis={{ - disabled: basis.continue.disabled, - label: basis.continue.label, - callback: basis.continue.callback, - }} - ></ButtonLayout> - {#if basis.back} - <div class={`flex flex-col justify-center items-center el-re`}> - {#if basis.back.visible} - <button - class={`group flex flex-row h-12 w-${$app_layout} justify-center items-center fade-in`} - on:click|stopPropagation={async () => { - if (!basis.back?.disabled) await basis.back?.callback(); - }} - > - <p - class={`font-sans font-[600] tracking-wide text-layer-1-glyph-shade ${basis.back?.disabled ? `` : `group-active:text-layer-1-glyph/40`} el-re`} - > - {basis.back.label || ``} - </p> - </button> - {:else} - <div - class={`flex flex-row h-4 w-full justify-start items-center`} - > - <Empty></Empty> - </div> - {/if} - </div> - {/if} -</div> diff --git a/apps-lib/src/lib/component/button/button-layout.svelte b/apps-lib/src/lib/component/button/button-layout.svelte @@ -1,39 +0,0 @@ -<script lang="ts"> - import { - app_layout, - fmt_cl, - parse_layer, - type CallbackPromise, - type IClOpt, - type IDisabledOpt, - type ILyOpt, - } from "$lib"; - - export let basis: ILyOpt & - IClOpt & - IDisabledOpt & { - classes_inner?: string; - hide_active?: boolean; - label: string; - callback: CallbackPromise; - }; - - $: layer = parse_layer(basis.layer, 1); - - $: classes_active = !basis.hide_active - ? `layer-1-active-surface layer-1-active-raise-less layer-1-active-ring-less` - : ``; -</script> - -<button - class={`${fmt_cl(basis.classes)} group flex flex-row h-touch_guide w-${$app_layout} justify-center items-center bg-layer-${layer}-surface rounded-touch ${basis.disabled ? `opacity-60` : classes_active} el-re`} - on:click|stopPropagation={async () => { - if (!basis.disabled) await basis.callback(); - }} -> - <p - class={`${fmt_cl(basis.classes_inner)} font-sans font-[600] tracking-wide text-layer-${layer}-glyph-shade ${basis.disabled ? `` : `group-active:text-layer-${layer}-glyph/40 `} el-re`} - > - {basis.label || ``} - </p> -</button> diff --git a/apps-lib/src/lib/component/carousel/carousel-item.svelte b/apps-lib/src/lib/component/carousel/carousel-item.svelte @@ -1,6 +0,0 @@ -<script lang="ts"> -</script> - -<div class={`flex flex-col flex-shrink-0 w-[100vw] justify-start items-center`}> - <slot /> -</div> diff --git a/apps-lib/src/lib/component/carousel/carousel.svelte b/apps-lib/src/lib/component/carousel/carousel.svelte @@ -1,12 +0,0 @@ -<script lang="ts"> - import { casl_index } from "$lib"; -</script> - -<div class={`relative flex flex-col w-full`}> - <div - class={`flex transition-transform duration-500`} - style={`transform: translateX(-${Math.max($casl_index, 0) * 100}vw)`} - > - <slot /> - </div> -</div> diff --git a/apps-lib/src/lib/component/entry/entry-line.svelte b/apps-lib/src/lib/component/entry/entry-line.svelte @@ -1,56 +0,0 @@ -<script lang="ts"> - import { - EntryWrap, - Glyph, - Input, - LoadSymbol, - parse_layer, - type IEntryLine, - type LoadingDimension, - } from "$lib"; - - export let basis: IEntryLine; - - $: layer = - typeof basis.wrap?.layer === `boolean` - ? parse_layer(0) - : parse_layer(basis.wrap?.layer); - $: classes_layer = - typeof basis.wrap?.layer === `boolean` - ? `` - : `text-layer-${layer}-glyph`; - let loading_dim: LoadingDimension = `sm`; - $: loading_dim = basis.wrap?.style === `guide` ? `md` : `sm`; -</script> - -<EntryWrap basis={basis?.wrap}> - <Input basis={basis.el}></Input> - {#if basis.loading} - <div - class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-4 justify-end items-center fade-in el-re`} - > - <LoadSymbol - basis={{ - dim: loading_dim, - }} - /> - </div> - {:else if basis.notify_inline} - {#if `glyph` in basis.notify_inline} - <div - class={`z-5 absolute el-re right-0 top-0 flex flex-row h-full pr-3 justify-end items-center translate-x-[34px] fade-in`} - > - <Glyph - basis={typeof basis.notify_inline.glyph === `string` - ? { - key: basis.notify_inline.glyph, - dim: `xs+`, - weight: `bold`, - classes: `${classes_layer}`, - } - : basis.notify_inline.glyph} - ></Glyph> - </div> - {/if} - {/if} -</EntryWrap> diff --git a/apps-lib/src/lib/component/entry/entry-wrap.svelte b/apps-lib/src/lib/component/entry/entry-wrap.svelte @@ -1,21 +0,0 @@ -<script lang="ts"> - import { fmt_cl, parse_layer, type IEntryWrap } from "$lib"; - - export let basis: IEntryWrap | undefined = undefined; - - $: layer = - typeof basis?.layer === `boolean` - ? parse_layer(0) - : parse_layer(basis?.layer); - $: classes_layer = - typeof basis?.layer === `boolean` - ? `bg-transparent` - : `bg-layer-${layer}-surface ${basis?.style_a ? `active:bg-layer-${layer}-surface_a` : ``}`; //text-layer-${layer}-glyph_d -</script> - -<button - id={basis?.id || null} - class={`${fmt_cl(basis?.classes)} relative entry-line-wrap ${!basis.no_pad ? ` pl-6 pr-4` : ``} h-entry_${basis?.style ? basis.style : `line`} rounded-touch ${classes_layer} el-re`} -> - <slot /> -</button> diff --git a/apps-lib/src/lib/component/float/float-page-button.svelte b/apps-lib/src/lib/component/float/float-page-button.svelte @@ -1,37 +0,0 @@ -<script lang="ts"> - import { - Glyph, - LoadSymbol, - type GeometryScreenPositionHorizontal, - type ICb, - type IGlyphKey, - type ILoadingOpt, - } from "$lib"; - - export let basis: ICb & - ILoadingOpt & - IGlyphKey & { - posx: Omit<GeometryScreenPositionHorizontal, "center">; - }; -</script> - -<div class={`absolute top-16 ${basis.posx}-6 flex flex-row`}> - <button - class={`flex flex-row h-12 w-12 justify-center items-center bg-layer-1-surface rounded-full el-re`} - on:click={basis.callback} - > - {#if basis.loading} - <LoadSymbol /> - {:else} - <Glyph - basis={{ - classes: `text-layer-0-glyph`, - dim: `sm+`, - weight: `bold`, - key: basis.glyph, - }} - ></Glyph> - {/if} - </button> -</div> -<div class="hidden left-6 right-6"></div> diff --git a/apps-lib/src/lib/component/float/float-tabs.svelte b/apps-lib/src/lib/component/float/float-tabs.svelte @@ -1,101 +0,0 @@ -<script lang="ts"> - import { goto } from "$app/navigation"; - import { page } from "$app/stores"; - import { Empty, Glyph } from "$lib"; -</script> - -<div - class={`absolute bottom-0 left-0 h-24 flex flex-row w-full pt-2 justify-center items-start`} -> - <div class={`flex flex-row justify-between gap-10 items-center`}> - <div - class={`grid grid-cols-4 flex flex-row h-[3.1rem] px-6 gap-6 justify-start items-center bg-layer-1-surface rounded-full backdrop-blur-lg`} - > - <button - class={`col-span-1 flex flex-row justify-center items-center`} - on:click={async () => { - await goto(`/`); - }} - > - <Glyph - basis={{ - classes: `text-[26px] text-layer-0-glyph/80 rotate-90`, - weight: $page.url.pathname !== `/` ? `bold` : `fill`, - key: `columns`, - }} - ></Glyph> - </button> - <button - class={`relative col-span-1 flex flex-row justify-center items-center`} - on:click={async () => { - await goto(`/search`); - }} - > - <Glyph - basis={{ - classes: `text-[24px] text-layer-0-glyph/80`, - weight: $page.url.pathname.startsWith(`/search`) - ? `fill` - : `bold`, - key: `magnifying-glass`, - }} - ></Glyph> - </button> - <button - class={`relative col-span-1 flex flex-row justify-center items-center`} - on:click={async () => { - goto(`/settings/profile`); - }} - > - <Glyph - basis={{ - classes: `text-[24px] text-layer-0-glyph/80`, - weight: $page.url.pathname.startsWith( - `/settings/profile`, - ) - ? `fill` - : `bold`, - key: `user`, - }} - ></Glyph> - </button> - <button - class={`relative col-span-1 flex flex-row h-full justify-center items-center`} - on:click={async () => { - await goto(`/notifications`); - }} - > - <Glyph - basis={{ - classes: `text-[24px] text-layer-0-glyph/80`, - weight: $page.url.pathname.startsWith(`/notifications`) - ? `fill` - : `bold`, - key: `bell`, - }} - ></Glyph> - <div - class={`absolute top-2 -right-1 flex flex-row justify-start items-center`} - > - <div - class={`flex flex-row h-2 w-2 justify-start items-center bg-yellow-400 rounded-full`} - > - <Empty></Empty> - </div> - </div> - </button> - </div> - <button - class={`flex flex-row h-[3.1rem] w-[3.1rem] justify-center items-center bg-layer-1-surface rounded-full backdrop-blur-lg`} - on:click={async () => {}} - > - <Glyph - basis={{ - classes: `text-[22px] text-layer-0-glyph/80`, - weight: `bold`, - key: `plus`, - }} - ></Glyph> - </button> - </div> -</div> diff --git a/apps-lib/src/lib/component/glyph/glyph-button-simple.svelte b/apps-lib/src/lib/component/glyph/glyph-button-simple.svelte @@ -1,40 +0,0 @@ -<script lang="ts"> - import { - fmt_cl, - Glyph, - type CallbackPromise, - type GlyphKey, - type IClOpt, - } from "$lib"; - - export let basis: IClOpt & { - kind?: `primary` | `neutral`; - label: string; - callback: CallbackPromise; - glyph?: GlyphKey; - }; - - $: classes_kind = - basis.kind === `neutral` - ? `text-layer-0-glyph-shade` - : `text-layer-0-glyph-hl`; -</script> - -<button - class={`${fmt_cl(basis.classes)} group flex flex-row justify-center items-center`} - on:click={basis.callback} -> - {#if basis.glyph} - <Glyph - basis={{ - classes: `${classes_kind}`, - dim: `sm+`, - weight: `bold`, - key: basis.glyph, - }} - /> - {/if} - <p class={`font-sans font-[600] text-guide ${classes_kind} opacity-active`}> - {basis.label} - </p> -</button> diff --git a/apps-lib/src/lib/component/glyph/glyph-button.svelte b/apps-lib/src/lib/component/glyph/glyph-button.svelte @@ -1,21 +0,0 @@ -<script lang="ts"> - import { fmt_cl, glyph_style_map, type IGlyph } from "$lib"; - - export let basis: IGlyph; - $: basis = basis; - - $: weight = - !basis?.weight || basis?.weight === `regular` ? `` : `-${basis.weight}`; - $: styles = basis?.dim - ? glyph_style_map.get(basis.dim) - : glyph_style_map.get(`sm`); -</script> - -<button - class={`${fmt_cl(basis.classes)} flex flex-col justify-center items-center text-[${styles?.gl_1}px] el-re`} - on:click={async () => { - if (basis.callback) await basis.callback(); - }} -> - <i class={`ph${weight} ph-${basis.key}`}></i> -</button> diff --git a/apps-lib/src/lib/component/glyph/glyph-circle.svelte b/apps-lib/src/lib/component/glyph/glyph-circle.svelte @@ -1,22 +0,0 @@ -<script lang="ts"> - import { - type IGlyphCircle, - GlyphButton, - fmt_cl, - glyph_style_map, - } from "$lib"; - - export let basis: IGlyphCircle; - - $: styles = basis?.glyph?.dim - ? glyph_style_map.get(basis?.glyph?.dim) - : glyph_style_map.get(`sm`); -</script> - -{#if styles?.dim_1} - <div - class={`${fmt_cl(basis?.classes_wrap)} flex flex-col h-[${styles?.dim_1}px] w-[${styles?.dim_1}px] justify-center items-center rounded-full el-re`} - > - <GlyphButton basis={basis?.glyph}></GlyphButton> - </div> -{/if} diff --git a/apps-lib/src/lib/component/glyph/glyph-title-select-label.svelte b/apps-lib/src/lib/component/glyph/glyph-title-select-label.svelte @@ -1,32 +0,0 @@ -<script lang="ts"> - import { ascii } from "$lib"; - - export let basis: { - label: string; - }; -</script> - -<div class={`flex flex-row justify-start items-center`}> - <p - class={`pr-[13px] font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`(${basis.label}`} - </p> - <div - class={`relative flex flex-row justify-start items-center -translate-x-[10px] -translate-y-[1px]`} - > - <p - class={`absolute font-sansd text-trellis_ti text-layer-0-glyph-label uppercase scale-y-[70%] scale-x-[80%] -translate-y-[1px]`} - > - {`${ascii.up}`} - </p> - <p - class={`absolute font-sansd text-trellis_ti text-layer-0-glyph-label uppercase scale-y-[70%] scale-x-[80%] translate-y-[2px]`} - > - {`${ascii.down}`} - </p> - </div> - <p class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}> - {`)`} - </p> -</div> diff --git a/apps-lib/src/lib/component/label/label-display.svelte b/apps-lib/src/lib/component/label/label-display.svelte @@ -1,28 +0,0 @@ -<script lang="ts"> - import { fmt_cl, parse_layer, type ILabelDisplay } from "$lib"; - - export let basis: ILabelDisplay; - $: basis = basis; - - $: layer = - typeof basis?.layer === `boolean` - ? false - : parse_layer(basis?.layer, 1); - $: classes_layer = - typeof layer === `boolean` - ? `bg-transparent` - : `bg-layer-${layer}-surface`; - $: clases_style = - basis.style === `guide` ? `h-entry_guide rounded-touch` : ``; -</script> - -<div - id={basis.id_wrap || null} - class={`${fmt_cl(basis.classes)} relative el-re entry-line-wrap px-2 ${classes_layer} ${clases_style}`} -> - <p - class={`${fmt_cl(basis.label.classes)} font-sans font-[400] text-layer-0-glyph`} - > - {basis.label.value} - </p> -</div> diff --git a/apps-lib/src/lib/component/lib/fade.svelte b/apps-lib/src/lib/component/lib/fade.svelte @@ -1,11 +0,0 @@ -<script lang="ts"> - import type { IBasisOpt } from "$lib/types/component"; - import type { IClOpt } from "$lib/types/interface"; - import { fmt_cl } from "$lib/util/app"; - - export let basis: IBasisOpt<IClOpt> = undefined; -</script> - -<div class={`${fmt_cl(basis?.classes)} flex`}> - <slot /> -</div> diff --git a/apps-lib/src/lib/component/lib/load-screen.svelte b/apps-lib/src/lib/component/lib/load-screen.svelte @@ -1,12 +0,0 @@ -<script lang="ts"> - import { LoadSymbol } from "$lib"; - import { fade } from "svelte/transition"; -</script> - -<div - in:fade={{ duration: 200 }} - out:fade={{ delay: 50, duration: 200 }} - class={`z-50 absolute top-0 left-0 flex flex-row h-[100vh] w-full justify-center items-center bg-layer-0-surface`} -> - <LoadSymbol basis={{ dim: `lg` }} /> -</div> diff --git a/apps-lib/src/lib/component/lib/logo-circle.svelte b/apps-lib/src/lib/component/lib/logo-circle.svelte @@ -1,41 +0,0 @@ -<script lang="ts"> - let array = [` • `, `radroots`, ` • `, `radroots`]; -</script> - -<div - class={`relative flex flex-col h-[196px] w-full justify-center items-center`} -> - <div - class={`relative flex flex-row h-36 w-36 justify-center items-center bg-layer-2-surface rounded-full`} - > - <p - class={`font-sans font-[900] text-6xl text-layer-0-glyph -tracking-[0.4rem] -translate-x-[6px]`} - > - {"»`,"} - </p> - <p - class={`font-sans font-[900] text-6xl text-layer-0-glyph translate-x-[8px]`} - > - {"-"} - </p> - </div> - {#each array as char, index} - <div - class={`char font-sans text-layer-0-glyph/60 text-[0.8rem] text-center uppercase`} - style="--angle: {`${(1 / array.length) * index + 0.18}turn`}" - > - {char} - </div> - {/each} -</div> - -<style> - .char { - width: 1em; - height: 100%; - position: absolute; - top: 0; - left: 50%; - transform: translateX(-50%) rotate(var(--angle, 0deg)); - } -</style> diff --git a/apps-lib/src/lib/component/lib/splash-screen.svelte b/apps-lib/src/lib/component/lib/splash-screen.svelte @@ -1,12 +0,0 @@ -<script lang="ts"> - import { LogoCircle } from "$lib"; - import { fade } from "svelte/transition"; -</script> - -<div - in:fade={{ duration: 200 }} - out:fade={{ delay: 50, duration: 200 }} - class={`z-50 absolute top-0 left-0 flex flex-row h-[100vh] w-full justify-center items-center bg-layer-0-surface`} -> - <LogoCircle /> -</div> diff --git a/apps-lib/src/lib/component/lib/view.svelte b/apps-lib/src/lib/component/lib/view.svelte @@ -1,14 +0,0 @@ -<script lang="ts"> - import { fade } from "svelte/transition"; - - export let key: string; -</script> - -<div - in:fade={{ duration: 200 }} - out:fade={{ delay: 50, duration: 200 }} - data-view={key} - class={`hidden flex flex-col h-full w-full justify-start items-center`} -> - <slot /> -</div> diff --git a/apps-lib/src/lib/component/map/map-marker-dot.svelte b/apps-lib/src/lib/component/map/map-marker-dot.svelte @@ -1,15 +0,0 @@ -<script lang="ts"> - import { Empty } from "$lib"; -</script> - -<div class="flex flex-row p-1"> - <div - class={`z-20 flex flex-row h-map_circle w-map_circle justify-center items-center bg-white rounded-full shadow-lg`} - > - <div - class={`z-10 flex flex-row h-map_circle_inner w-map_circle_inner justify-center items-center bg-blue-400 rounded-full`} - > - <Empty></Empty> - </div> - </div> -</div> diff --git a/apps-lib/src/lib/component/map/map-point-display.svelte b/apps-lib/src/lib/component/map/map-point-display.svelte @@ -1,28 +0,0 @@ -<script lang="ts"> - import { - app_thc, - cfg_map, - fmt_cl, - type GeolocationPoint, - type IClOpt, - MapMarkerDot, - } from "$lib"; - import { MapLibre, Marker } from "@radroots/svelte-maplibre"; - - export let basis: IClOpt & { - point: GeolocationPoint; - zoom?: number; - }; -</script> - -<MapLibre - center={basis.point} - zoom={basis.zoom || 4} - class={`${fmt_cl(basis.classes)} relative aspect-1 w-full`} - style={cfg_map.styles.base[$app_thc]} - attributionControl={false} -> - <Marker bind:lngLat={basis.point} draggable={false}> - <MapMarkerDot /> - </Marker> -</MapLibre> diff --git a/apps-lib/src/lib/component/map/map-point-select.svelte b/apps-lib/src/lib/component/map/map-point-select.svelte @@ -1,70 +0,0 @@ -<script lang="ts"> - import { - app_thc, - cfg_map, - Empty, - handle_err, - MapPopupPointGeolocation, - type GeocoderReverseResult, - type GeolocationPoint, - type IClOpt, - type ILcGeocodeCallback, - } from "$lib"; - import { MapLibre, Marker, Popup } from "@radroots/svelte-maplibre"; - - let map_center: GeolocationPoint = { - ...cfg_map.coords.default, - }; - export let map_point: GeolocationPoint = { - ...cfg_map.coords.default, - }; - export let map_point_geoc_r: GeocoderReverseResult | undefined = undefined; - - export let basis: IClOpt & { - lc_geocode: ILcGeocodeCallback; - }; - - $: if (map_point && map_center.lat === 0 && map_center.lng === 0) { - map_center = map_point; - (async () => { - try { - map_point_geoc_r = await basis.lc_geocode(map_center); - } catch (e) { - await handle_err(e, `map-point-select-lc-geocode-init`); - } - })(); - } -</script> - -<MapLibre - center={map_center} - zoom={10} - class={`relative h-full w-full`} - style={cfg_map.styles.base[$app_thc]} - attributionControl={false} -> - <Marker - bind:lngLat={map_point} - draggable - class={`flex flex-row h-map_circle w-map_circle justify-center items-center bg-white rounded-full shadow-lg`} - on:dragend={async () => { - if (!map_point) return; - const geoc_r = await basis.lc_geocode(map_point); - if (geoc_r) map_point_geoc_r = geoc_r; - }} - > - <div - class={`flex flex-row h-map_circle_inner w-map_circle_inner justify-center items-center bg-blue-400 rounded-full`} - > - <Empty /> - </div> - <Popup open={true} offset={[0, -10]}> - <MapPopupPointGeolocation - basis={{ - point: map_point, - geoc_r: map_point_geoc_r, - }} - /> - </Popup> - </Marker> -</MapLibre> diff --git a/apps-lib/src/lib/component/map/map-popup-point-geolocation.svelte b/apps-lib/src/lib/component/map/map-popup-point-geolocation.svelte @@ -1,74 +0,0 @@ -<script lang="ts"> - import { - geol_lat_fmt, - geol_lng_fmt, - Glyph, - type GeocoderReverseResult, - type GeolocationPoint, - type IClOpt, - } from "$lib"; - import Fade from "../lib/fade.svelte"; - - export let basis: IClOpt & { - point?: GeolocationPoint; - geoc_r?: GeocoderReverseResult; - }; -</script> - -<div - class={`flex flex-col w-fit px-5 py-[0.7rem] justify-start items-start bg-layer-1-surface rounded-2xl shadow-lg`} -> - <div class={`flex flex-col w-full gap-1 justify-start items-start`}> - {#if basis.geoc_r} - <Fade - basis={{ - classes: `flex-col w-full gap-1 justify-start items-start`, - }} - > - <div class={`flex flex-row gap-1 justify-start items-center`}> - <p - class={`font-sans font-[500] text-[0.9rem] text-layer-2-glyph`} - > - {basis.geoc_r.name} - </p> - <Glyph - basis={{ - classes: `text-layer-2-glyph -translate-y-[2px]`, - dim: `xs`, - weight: `bold`, - key: `map-pin-simple`, - }} - ></Glyph> - </div> - <div - class={`flex flex-row w-full gap-1 justify-start items-center`} - > - <p - class={`font-sans font-[500] text-[0.9rem] tracking-tight text-layer-2-glyph`} - > - {`${basis.geoc_r.admin1_name},`} - </p> - <p - class={`font-sans font-[500] text-[0.9rem] tracking-tight text-layer-2-glyph`} - > - {`${basis.geoc_r.country_name}`} - </p> - </div> - </Fade> - {/if} - {#if basis.point} - <div class={`flex flex-col w-full justify-start items-start`}> - <p - class={`font-sans font-[400] text-[0.9rem] text-layer-0-glyph`} - > - {`${geol_lat_fmt(basis.point.lat, `dms`)}`} - </p> - <p - class={`font-sans font-[400] text-[0.9rem] text-layer-0-glyph`} - > - {`${geol_lng_fmt(basis.point.lng, `dms`)}`} - </p> - </div> - {/if} - </div> -</div> diff --git a/apps-lib/src/lib/component/nav/nav-option.svelte b/apps-lib/src/lib/component/nav/nav-option.svelte @@ -1,82 +0,0 @@ -<!-- svelte-ignore a11y-label-has-associated-control --> -<script lang="ts"> - import { - fmt_cl, - Glyph, - LoadSymbol, - parse_layer, - type INavBasisOption, - } from "$lib"; - - let el_swap: HTMLLabelElement | null = null; - - export let basis: INavBasisOption; - $: basis = basis; - $: layer = parse_layer(1); - $: classes_disabled = basis.disabled ? `opacity-40` : ``; -</script> - -{#if basis?.loading} - <div class={`flex flex-row pr-4 justify-center items-center`}> - <LoadSymbol /> - </div> -{:else} - <button - class={`${fmt_cl(basis?.classes)} group col-span-4 flex flex-row h-full justify-end items-center ${classes_disabled}`} - on:click={async () => { - if (!basis.disabled) await basis?.callback(el_swap); - }} - > - {#if `glyph` in basis && basis?.glyph} - <Glyph - basis={{ - classes: `group-active:opacity-70 ${basis?.glyph?.classes}`, - ...basis?.glyph, - }} - ></Glyph> - {/if} - {#if `label` in basis && basis?.label} - <div - class={`flex flex-row justify-start items-center ${basis?.label && `glyph` in basis?.label && basis.label?.glyph ? `pr-2` : `pr-4`}`} - > - {#if `swap` in basis?.label} - <label - bind:this={el_swap} - class={`swap${basis?.label?.swap?.toggle ? ` swap-active` : ``}`} - > - <div class="swap-on"> - <p - class={`${fmt_cl(basis?.label?.swap?.on.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-70`)} font-sans -translate-y-[1px] el-re`} - > - {basis?.label?.swap?.on.value} - </p> - </div> - <div class="swap-off"> - <p - class={`${fmt_cl(basis?.label?.swap?.off.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-70`)} font-sans -translate-y-[1px] el-re`} - > - {basis?.label?.swap?.off.value} - </p> - </div> - </label> - {:else if `value` in basis?.label} - <p - class={`${fmt_cl(basis?.label.classes)} font-sans text-nav_prev text-layer-1-glyph-hl group-active:opacity-70 el-re`} - > - {basis?.label.value} - </p> - {/if} - {#if `glyph` in basis?.label} - <Glyph - basis={{ - key: basis?.label?.glyph?.key, - classes: `text-layer-1-glyph-hl group-active:opacity-70 ${basis?.label?.glyph?.classes}`, - weight: `bold`, - dim: `md`, - }} - ></Glyph> - {/if} - </div> - {/if} - </button> -{/if} diff --git a/apps-lib/src/lib/component/nav/nav.svelte b/apps-lib/src/lib/component/nav/nav.svelte @@ -1,167 +0,0 @@ -<script lang="ts"> - import { goto } from "$app/navigation"; - import { - app_layout, - ButtonArrow, - Empty, - encode_qp_route, - fmt_cl, - Glyph, - LoadSymbol, - nav_blur, - nav_prev, - nav_visible, - NavOption, - type INavBasis, - } from "$lib"; - import { onDestroy, onMount } from "svelte"; - - export let basis: INavBasis; - $: basis = basis; - - let el: HTMLElement | null; - let el_inner: HTMLElement | null; - - let nav_prev_label = ``; - - $: classes_nav_blur = $nav_blur ? `bg-white/40 backdrop-blur-sm` : ``; - - onMount(async () => { - try { - nav_visible.set(true); - if ($nav_prev.length) - nav_prev_label = $nav_prev[$nav_prev.length - 1].label || ``; - } catch (e) { - } finally { - } - }); - - onDestroy(async () => { - try { - nav_visible.set(false); - } catch (e) { - } finally { - } - }); - - const callback_prev = async (): Promise<void> => { - try { - if (basis.prev.prevent_route) { - await basis.prev.prevent_route.callback(); - return; - } else if (basis.prev.callback) await basis.prev.callback(); - let route_to = - typeof basis.prev.route === `string` - ? basis.prev.route - : encode_qp_route(basis.prev.route[0], basis.prev.route[1]); - if ($nav_prev.length) { - const nav_prev_li = $nav_prev[$nav_prev.length - 1]; - $nav_prev = [...$nav_prev.slice(0, -1)]; - if (nav_prev_li) - route_to = encode_qp_route( - nav_prev_li.route, - nav_prev_li.params, - ); - } - await goto(route_to); - } catch (e) { - console.log(`(error) callback_prev `, e); - } - }; -</script> - -<div - bind:this={el} - class={`z-10 absolute top-0 left-0 flex flex-col w-full justify-start items-start h-nav_${$app_layout} ${classes_nav_blur} duration-[250ms] el-re`} -> - <div - bind:this={el_inner} - class={`relative flex flex-col h-full w-full justify-end items-center`} - > - <div - class={`absolute bottom-[6px] left-0 grid grid-cols-12 flex flex-row h-8 w-full justify-start items-center`} - > - <div - class={`col-span-4 flex flex-row w-full justify-start items-center`} - > - {#if basis.prev.loading} - <div - class={`flex flex-row pl-4 justify-center items-center`} - > - <LoadSymbol /> - </div> - {:else if basis.prev.kind === `arrow`} - <div - class={`flex flex-row w-full pl-8 justify-start items-center`} - > - <ButtonArrow - basis={{ - label: nav_prev_label || basis.prev.label, - callback: async () => { - await callback_prev(); - }, - }} - /> - </div> - {:else} - <button - class={`group flex flex-row h-full pl-2 justify-start items-center`} - on:click={async () => { - await callback_prev(); - }} - > - <Glyph - basis={{ - key: `caret-left`, - weight: `bold`, - dim: `md+`, - classes: `text-layer-1-glyph-hl group-active:opacity-70 transition-opacity`, - }} - ></Glyph> - {#if nav_prev_label || basis.prev.label} - <p - class={`font-sans text-nav_prev text-layer-1-glyph-hl group-active:opacity-60 transition-opacity`} - > - {nav_prev_label || basis.prev.label} - </p> - {:else} - <Empty></Empty> - {/if} - </button> - {/if} - </div> - <div - class={`col-span-4 flex flex-row h-full justify-center items-center`} - > - {#if basis.title} - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - if (basis.title.callback) - await basis.title.callback(); - }} - > - {#if `value` in basis.title.label} - <p - class={`${fmt_cl(basis.title.label.classes)} font-sans text-nav_curr text-layer-1-glyph`} - > - {basis.title.label.value} - </p> - {/if} - </button> - {:else} - <Empty></Empty> - {/if} - </div> - <div - class={`col-span-4 flex flex-row h-full justify-end items-center`} - > - {#if basis.option} - <NavOption basis={basis.option} /> - {:else} - <Empty></Empty> - {/if} - </div> - </div> - </div> -</div> diff --git a/apps-lib/src/lib/component/page/page-header.svelte b/apps-lib/src/lib/component/page/page-header.svelte @@ -1,41 +0,0 @@ -<script lang="ts"> - import { - app_layout, - callback_route, - Empty, - ph_blur, - type IPageHeader, - } from "$lib"; - import { fade } from "svelte/transition"; - - export let basis: IPageHeader; -</script> - -{#if $ph_blur} - <div - in:fade={{ duration: 50 }} - out:fade={{ delay: 50, duration: 200 }} - class={`z-20 fixed top-0 left-0 flex flex-row h-nav_${$app_layout} w-full justify-center items-center bg-layer-0-surface-blur/30 backdrop-blur-lg`} - > - <Empty></Empty> - </div> -{/if} -<div - class={`z-20 sticky top-0 flex flex-row w-full pb-4 px-6 justify-between items-center`} -> - <div class={`flex flex-row justify-start items-center`}> - <button - class={`flex flex-row gap-1 justify-center items-center`} - on:click={async () => { - await callback_route(basis.callback_route); - }} - > - <p - class={`font-sansd font-[700] text-2xl text-layer-0-glyph capitalize`} - > - {basis.label || ``} - </p> - </button> - </div> - <slot name="header-option" /> -</div> diff --git a/apps-lib/src/lib/component/page/page-toolbar.svelte b/apps-lib/src/lib/component/page/page-toolbar.svelte @@ -1,55 +0,0 @@ -<script lang="ts"> - import { goto } from "$app/navigation"; - import { - Empty, - Glyph, - LogoCircleSm, - PageHeader, - type IBasisOpt, - type IPageToolbar, - } from "$lib"; - - export let basis: IBasisOpt<IPageToolbar> = undefined; -</script> - -<div class={`flex flex-row h-12 w-full px-6 justify-between items-center`}> - <button - class={`flex flex-row gap-2 justify-start items-center`} - on:click={async () => { - if (basis?.callback) await basis.callback(); - else await goto(`/`); - }} - > - <LogoCircleSm /> - <p - class={`font-sansd italic font-[700] text-[1.7rem] text-layer-0-glyph lowercase`} - > - {`radroots`} - </p> - </button> - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - await goto(`/settings`); - }} - > - <Glyph - basis={{ - classes: `text-layer-0-glyph`, - dim: `lg`, - weight: `bold`, - key: `gear`, - }} - ></Glyph> - </button> -</div> -{#if basis?.header} - <div class={`flex flex-row h-5 w-full justify-center items-center`}> - <Empty></Empty> - </div> - <PageHeader basis={basis.header}> - <div slot="header-option"> - <slot name="header-option" /> - </div> - </PageHeader> -{/if} diff --git a/apps-lib/src/lib/component/trellis/trellis-default-label.svelte b/apps-lib/src/lib/component/trellis/trellis-default-label.svelte @@ -1,30 +0,0 @@ -<script lang="ts"> - import { fmt_cl, type ITrellisDefaultLabel } from "$lib"; - import type { ThemeLayer } from "@radroots/theme"; - - export let classes = ``; - export let layer: ThemeLayer; - export let labels: ITrellisDefaultLabel[]; -</script> - -<div class={`${fmt_cl(classes)} flex flex-row`}> - <p class={`font-sans text-trellis_ti text-layer-${layer}-glyph-shade`}> - {#each labels as label} - <span class={`${fmt_cl(label.classes)} font-sans text-trellis_ti`}> - {#if `callback` in label} - <button - class={``} - on:click|preventDefault={async () => { - if (`callback` in label && label.callback) - await label.callback(); - }} - > - {label.label} - </button> - {:else} - {label.label} - {/if} - </span> - {/each} - </p> -</div> diff --git a/apps-lib/src/lib/component/trellis/trellis-end.svelte b/apps-lib/src/lib/component/trellis/trellis-end.svelte @@ -1,30 +0,0 @@ -<script lang="ts"> - import { Glyph, type ITrellisBasisTouchEnd } from "$lib"; - import type { ThemeLayer } from "@radroots/theme"; - - export let basis: ITrellisBasisTouchEnd; - export let layer: ThemeLayer; - export let hide_active: boolean; -</script> - -<div - class={`absolute top-0 right-0 h-full w-max flex flex-row justify-center items-center`} -> - <button - class={`flex pr-3`} - on:click|preventDefault={async (ev) => { - if (basis.callback) await basis.callback(ev); - }} - > - {#if basis.glyph} - <Glyph - basis={{ - classes: `text-layer-${layer}-glyph-shade ${hide_active ? `` : `group-active:text-layer-${layer}-glyph_a`} translate-y-[1px] opacity-70`, - dim: `xs+`, - weight: `bold`, - ...basis.glyph, - }} - ></Glyph> - {/if} - </button> -</div> diff --git a/apps-lib/src/lib/component/trellis/trellis-input.svelte b/apps-lib/src/lib/component/trellis/trellis-input.svelte @@ -1,82 +0,0 @@ -<script lang="ts"> - import { - fmt_cl, - fmt_trellis, - Glyph, - Input, - LoadSymbol, - type ITrellisBasisInput, - } from "$lib"; - import type { ThemeLayer } from "@radroots/theme"; - - export let basis: ITrellisBasisInput; - export let layer: ThemeLayer; - export let hide_border_t: boolean; - export let hide_border_b: boolean; -</script> - -<div class={`flex flex-row flex-grow h-full w-full`}> - <div - class={`${fmt_trellis(hide_border_b, hide_border_t)} flex flex-row h-line w-full justify-start items-center border-t-line border-layer-${layer}-surface-edge overflow-hidden`} - > - {#if basis.line_label && basis.line_label.value} - <div - class={`${fmt_cl(basis.line_label.classes)} flex flex-row h-full justify-start items-center overflow-x-hidden`} - > - <p class={`font-sans text-layer-${layer}-glyph_b`}> - {basis.line_label.value} - </p> - </div> - {/if} - <div - class={`relative flex flex-row flex-grow h-full pr-12 justify-start items-center`} - > - <Input - basis={{ - ...basis.basis, - layer: layer, - }} - ></Input> - {#if basis.action} - {#if basis.action.visible} - <div - class={`absolute top-0 right-0 flex flex-row h-full w-12 pr-4 justify-end items-center fade-in`} - > - {#if basis.action.loading} - <div class={`flex flex-row fade-in`}> - <LoadSymbol - basis={{ - dim: `glyph-send-button`, - blades: 8, - classes: `text-layer-${layer}-glyph el-re`, - }} - /> - </div> - {:else} - <button - class={`group fade-in-long`} - on:click|preventDefault={async () => { - if (basis.action.callback) - await basis.action.callback(); - }} - > - <Glyph - basis={basis.action.glyph - ? { - dim: `md-`, - ...basis.action.glyph, - } - : { - key: `plus`, - classes: `text-layer-${layer}-glyph`, - dim: `md-`, - }} - ></Glyph> - </button> - {/if} - </div> - {/if} - {/if} - </div> - </div> -</div> diff --git a/apps-lib/src/lib/component/trellis/trellis-line.svelte b/apps-lib/src/lib/component/trellis/trellis-line.svelte @@ -1,40 +0,0 @@ -<script lang="ts"> - import { fmt_trellis, LoadSymbol, type CallbackPromiseGeneric } from "$lib"; - import type { ThemeLayer } from "@radroots/theme"; - - export let loading: boolean = false; - export let layer: ThemeLayer; - export let callback: CallbackPromiseGeneric<MouseEvent>; - export let hide_border_t: boolean; - export let hide_border_b: boolean; -</script> - -<button - class={`flex flex-row flex-grow overflow-x-hidden`} - on:click={async (ev) => { - if (callback) await callback(ev); - }} -> - <div - class={`${fmt_trellis(hide_border_b, hide_border_t)} flex flex-row h-full w-full justify-center items-center border-t-line border-layer-${layer}-surface-edge el-re`} - > - {#if loading} - <div - class={`flex flex-row h-full w-full justify-center items-center`} - > - <LoadSymbol basis={{ dim: `sm` }} /> - </div> - {:else} - <div - class={`relative group flex flex-row h-line w-full pr-[2px] justify-between items-center el-re`} - > - <div - class={`flex flex-row h-full w-trellis_display justify-between items-center`} - > - <slot /> - </div> - <slot name="end" /> - </div> - {/if} - </div> -</button> diff --git a/apps-lib/src/lib/component/trellis/trellis-offset.svelte b/apps-lib/src/lib/component/trellis/trellis-offset.svelte @@ -1,70 +0,0 @@ -<script lang="ts"> - import { - Empty, - fmt_cl, - Glyph, - GlyphCircle, - LoadSymbol, - type ITrellisBasisOffset, - type ITrellisBasisOffsetMod, - } from "$lib"; - import type { ThemeLayer } from "@radroots/theme"; - - export let basis: ITrellisBasisOffset | undefined = undefined; - export let layer: ThemeLayer; - - let mod: ITrellisBasisOffsetMod = `sm`; - $: mod = basis && basis.mod ? basis.mod : `sm`; -</script> - -<div class={`flex flex-row h-full`}> - {#if mod === `sm`} - <div class={`${fmt_cl(``)} flex flex-row h-full w-[22px]`}> - <Empty></Empty> - </div> - {:else if mod === `glyph`} - <div class={`flex flex-row pr-[2px]`}> - <div class={`${fmt_cl(``)} flex flex-row h-full w-trellisOffset`}> - <Empty></Empty> - </div> - </div> - {:else if typeof mod === `object`} - <div - class={`flex flex-row h-full min-w-[20px] w-trellisOffset justify-center items-center pr-3`} - > - <button - class={`fade-in pl-2 translate-x-[3px] translate-y-[1px]`} - on:click|preventDefault={async (ev) => { - if (mod.loading) return; - else if (typeof basis !== `boolean` && basis?.callback) - await basis.callback(ev); - }} - > - {#if mod.loading} - <LoadSymbol basis={{ blades: 8, dim: `xs` }} /> - {:else if `glyph` in mod} - <Glyph - basis={{ - classes: mod.glyph.classes - ? mod.glyph.classes - : `text-layer-${layer}-glyph`, - ...mod.glyph, - }} - ></Glyph> - {:else if `glyph_circle` in mod} - <GlyphCircle - basis={{ - classes_wrap: mod.glyph_circle?.classes_wrap, - glyph: { - classes: mod.glyph_circle?.glyph?.classes - ? mod.glyph_circle?.glyph?.classes - : `text-layer-${layer}-glyph`, - ...mod.glyph_circle?.glyph, - }, - }} - ></GlyphCircle> - {/if} - </button> - </div> - {/if} -</div> diff --git a/apps-lib/src/lib/component/trellis/trellis-row-display-value.svelte b/apps-lib/src/lib/component/trellis/trellis-row-display-value.svelte @@ -1,41 +0,0 @@ -<script lang="ts"> - import { - fmt_cl, - get_label_classes_kind, - Glyph, - type ITrellisKindDisplayValue, - } from "$lib"; - import type { ThemeLayer } from "@radroots/theme"; - - export let basis: ITrellisKindDisplayValue; - export let layer: ThemeLayer; - export let hide_active: boolean; -</script> - -<button - class={`z-10 flex flex-grow justify-end`} - on:click|stopPropagation={async (ev) => { - if (basis.callback) await basis.callback(ev); - }} -> - {#if `icon` in basis} - <Glyph - basis={{ - classes: - basis.icon.classes || - `${get_label_classes_kind(layer, `shade`, hide_active)}`, - key: basis.icon.key, - weight: `bold`, - dim: `sm`, - }} - ></Glyph> - {:else if basis.label} - {#if `value` in basis.label} - <p - class={`${fmt_cl(basis.label.classes)} font-sans text-line_d_e line-clamp-1 text-layer-0-glyph-label el-re`} - > - {basis.label.value} - </p> - {/if} - {/if} -</button> diff --git a/apps-lib/src/lib/component/trellis/trellis-row-label.svelte b/apps-lib/src/lib/component/trellis/trellis-row-label.svelte @@ -1,61 +0,0 @@ -<script lang="ts"> - import { - GlyphButton, - fmt_cl, - get_label_classes_kind, - type ILabelTupFields, - } from "$lib"; - import type { ThemeLayer } from "@radroots/theme"; - - export let basis: ILabelTupFields; - export let layer: ThemeLayer; - export let hide_active: boolean; -</script> - -<div class={`flex flex-row h-full items-center justify-between`}> - {#if basis.left && basis.left.length} - <div class={`flex flex-row h-full items-center truncate`}> - {#each basis.left as title_l} - <div - class={`${fmt_cl(title_l.classes_wrap)} flex flex-row h-full items-center ${get_label_classes_kind(layer, undefined, hide_active)} ${title_l.hide_truncate ? `` : `truncate`}`} - > - {#if `glyph` in title_l} - <div - class={`flex flex-row justify-start items-center pr-2`} - > - <GlyphButton basis={{ ...title_l.glyph }} - ></GlyphButton> - </div> - {:else if `value` in title_l} - <p - class={`${fmt_cl(title_l.classes)} font-sans text-line_d ${title_l.hide_truncate ? `` : `truncate`} el-re`} - > - {title_l.value || ``} - </p> - {/if} - </div> - {/each} - </div> - {/if} - {#if basis.right && basis.right.length} - <div - class={`flex flex-row h-full w-content items-center justify-end pr-4`} - > - {#each basis.right.reverse() as title_r} - <div - class={`${fmt_cl(title_r.classes_wrap)} flex flex-row h-full gap-1 items-center ${title_r.hide_truncate ? `` : `truncate`}`} - > - {#if `glyph` in title_r} - <GlyphButton basis={{ ...title_r.glyph }}></GlyphButton> - {:else if `value` in title_r} - <p - class={`${fmt_cl(title_r.classes)} font-sans text-line_d text-layer-${layer}-glyph_d ${title_r.hide_truncate ? `` : `truncate`} el-re`} - > - {title_r.value || ``} - </p> - {/if} - </div> - {/each} - </div> - {/if} -</div> diff --git a/apps-lib/src/lib/component/trellis/trellis-select.svelte b/apps-lib/src/lib/component/trellis/trellis-select.svelte @@ -1,57 +0,0 @@ -<script lang="ts"> - import { - LoadSymbol, - SelectMenu, - TrellisEnd, - TrellisLine, - TrellisRowDisplayValue, - TrellisRowLabel, - type ITrellisBasisSelect, - } from "$lib"; - import type { ThemeLayer } from "@radroots/theme"; - - export let basis: ITrellisBasisSelect; - export let layer: ThemeLayer; - export let hide_border_t: boolean; - export let hide_border_b: boolean; - export let hide_active: boolean; - - $: value = basis.el.value; - $: loading = typeof basis?.loading === `boolean` ? basis.loading : false; -</script> - -<TrellisLine - {layer} - {loading} - {hide_border_b} - {hide_border_t} - callback={basis.callback} -> - <TrellisRowLabel basis={basis.label} {layer} {hide_active} /> - {#if basis.display} - <div class={`flex flex-row pr-3 justify-center items-end`}> - <SelectMenu {value} basis={basis.el}> - <svelte:fragment slot="element"> - {#if basis.display.loading} - <div - class={`flex flex-row h-full w-full justify-end items-center`} - > - <LoadSymbol basis={{ dim: `sm` }} /> - </div> - {:else} - <TrellisRowDisplayValue - basis={{ ...basis.display }} - {layer} - {hide_active} - /> - {/if} - </svelte:fragment> - </SelectMenu> - </div> - {/if} - <div slot="end"> - {#if basis.end && !basis.display.loading} - <TrellisEnd basis={basis.end} {layer} {hide_active} /> - {/if} - </div> -</TrellisLine> diff --git a/apps-lib/src/lib/component/trellis/trellis-title.svelte b/apps-lib/src/lib/component/trellis/trellis-title.svelte @@ -1,62 +0,0 @@ -<script lang="ts"> - import { Empty, fmt_cl, Glyph, type ITrellisTitle, LabelSwap } from "$lib"; - import type { ThemeLayer } from "@radroots/theme"; - - export let basis: ITrellisTitle; - export let layer: ThemeLayer = 0; - - $: mod = basis && basis.mod ? basis.mod : `sm`; -</script> - -<div - class={`${fmt_cl(basis.classes)} flex flex-row h-[24px] w-full pl-[2px] gap-1 items-center`} -> - <button - class={`flex flex-row h-full w-max items-center gap-1 ${mod === `glyph` ? `pl-[36px]` : mod === `sm` ? `pl-[16px]` : ``}`} - on:click|preventDefault={async () => { - if (basis && basis.callback) await basis.callback(); - }} - > - {#if basis.value === true} - <Empty></Empty> - {:else} - <p - class={`font-sans text-trellis_ti text-layer-${layer}-glyph-label uppercase`} - > - {basis.value || ``} - </p> - {/if} - </button> - {#if basis.link} - <button - class={`${fmt_cl(basis.link.classes)} group flex flex-row h-full w-max items-center`} - on:click|preventDefault={async () => { - if (basis.link && basis.link.callback) - await basis.link.callback(); - }} - > - {#if basis.link.label} - {#if `swap` in basis.link.label} - <LabelSwap basis={basis.link.label} /> - {:else if `value` in basis.link.label} - <p - class={`${fmt_cl(basis.link.label.classes)} font-sans text-trellis_ti uppercase fade-in`} - > - {basis.link.label.value || ``} - </p> - {/if} - {/if} - {#if basis.link.glyph} - <div class={`flex flex-row w-max`}> - <Glyph - basis={{ - ...basis.link.glyph, - dim: `xs-`, - classes: `${fmt_cl(basis.link.glyph.classes)} fade-in`, - }} - ></Glyph> - </div> - {/if} - </button> - {/if} -</div> diff --git a/apps-lib/src/lib/component/trellis/trellis-touch.svelte b/apps-lib/src/lib/component/trellis/trellis-touch.svelte @@ -1,34 +0,0 @@ -<script lang="ts"> - import { - TrellisEnd, - TrellisLine, - TrellisRowDisplayValue, - TrellisRowLabel, - type ITrellisBasisTouch, - } from "$lib"; - import type { ThemeLayer } from "@radroots/theme"; - - export let basis: ITrellisBasisTouch; - export let layer: ThemeLayer; - export let hide_border_t: boolean; - export let hide_border_b: boolean; - export let hide_active: boolean; -</script> - -<TrellisLine {layer} {hide_border_b} {hide_border_t} callback={basis.callback}> - <TrellisRowLabel basis={basis.label} {layer} {hide_active} /> - {#if basis.display} - <TrellisRowDisplayValue - basis={{ - ...basis.display, - }} - {layer} - {hide_active} - /> - {/if} - <svelte:fragment slot="end"> - {#if basis.end} - <TrellisEnd basis={basis.end} {layer} {hide_active} /> - {/if} - </svelte:fragment> -</TrellisLine> diff --git a/apps-lib/src/lib/component/trellis/trellis.svelte b/apps-lib/src/lib/component/trellis/trellis.svelte @@ -1,124 +0,0 @@ -<script lang="ts"> - import { - app_layout, - fmt_cl, - lls, - parse_layer, - TrellisDefaultLabel, - TrellisInput, - TrellisOffset, - TrellisSelect, - TrellisTitle, - TrellisTouch, - type ITrellis, - } from "$lib"; - - export let basis: { args: ITrellis }; - $: ({ args } = basis); - $: hide_border_t = - typeof args.hide_border_top === `boolean` ? args.hide_border_top : true; - $: hide_border_b = - typeof args.hide_border_bottom === `boolean` - ? args.hide_border_bottom - : true; - $: hide_rounded = - typeof args.hide_rounded === `boolean` ? args.hide_rounded : false; - $: set_title_background = - typeof args.set_title_background === `boolean` - ? args.set_title_background - : false; - $: set_default_background = - typeof args.set_default_background === `boolean` - ? args.set_default_background - : false; -</script> - -<div - id={basis.args.id || ``} - class={`${fmt_cl(args.classes)} flex flex-col`} - data-view={basis.args.view || ``} -> - <div - class={`relative flex flex-col h-auto w-${$app_layout} gap-[3px] ${set_title_background ? `bg-layer-${args.layer}-surface` : ``}`} - > - {#if args.title && (!args.default_el || (args.default_el && args.default_el.show_title))} - <TrellisTitle - basis={args.title} - layer={parse_layer(args.layer - 1)} - /> - {/if} - {#if args.default_el} - <div - class={`flex flex-col h-auto w-full justify-center items-center`} - > - {#if $$slots.default_el} - <slot name="default_el" /> - {:else if args.default_el} - <TrellisDefaultLabel - layer={parse_layer(args.layer - 1)} - labels={args.default_el.labels - ? args.default_el.labels - : [ - { - label: `${$lls(`common.no_items_to_display`)}.`, - }, - ]} - /> - {/if} - </div> - {:else if args.list} - <div class={`flex flex-col w-full justify-center items-center`}> - {#each args.list as basis} - <div - class={`${basis.hide_field ? "hidden" : ``} group flex flex-row h-full w-full justify-end items-center bg-layer-${args.layer}-surface ${basis.full_rounded ? `rounded-touch` : ``} ${hide_rounded ? `` : `first:rounded-t-2xl last:rounded-b-2xl`} ${!basis.hide_active ? `active:bg-layer-${args.layer}-surface_a` : ``} el-re`} - > - <div - class={`flex flex-row h-full w-full gap-1 items-center overflow-y-hidden`} - > - {#if !args.hide_offset} - <TrellisOffset - basis={basis.offset} - layer={args.layer} - /> - {/if} - {#if $$slots.offset} - <slot name="offset" /> - {/if} - {#if `touch` in basis && basis.touch} - <TrellisTouch - basis={basis.touch} - layer={args.layer} - {hide_border_b} - {hide_border_t} - hide_active={!!basis.hide_active} - /> - {:else if `input` in basis && basis.input} - <TrellisInput - basis={basis.input} - layer={args.layer} - {hide_border_b} - {hide_border_t} - /> - {:else if `select` in basis && basis.select} - <TrellisSelect - basis={basis.select} - layer={args.layer} - {hide_border_b} - {hide_border_t} - hide_active={!!basis.hide_active} - /> - {/if} - </div> - </div> - {/each} - </div> - {/if} - </div> - {#if $$slots.append} - <div - class={`flex flex-col w-full ${set_default_background ? `bg-layer-${args.layer}-surface` : ``}`} - > - <slot name="append" /> - </div> - {/if} -</div> diff --git a/apps-lib/src/lib/components/button/button-horizontal-pair-slide.svelte b/apps-lib/src/lib/components/button/button-horizontal-pair-slide.svelte @@ -0,0 +1,45 @@ +<script lang="ts"> + import { Empty } from "$root"; + + let { + basis, + toggle = $bindable(false), + }: { + basis: { + label_l: string; + label_r: string; + }; + toggle: boolean; + } = $props(); +</script> + +<button + class={`group relative flex flex-row h-[38px] w-full justify-between items-center rounded-touch bg-layer-2-surface`} + onclick={async () => { + toggle = !toggle; + }} +> + <div + class={`z-10 flex flex-row h-full basis-1/2 justify-center items-center bg-transparent`} + > + <p + class={`font-sans font-[400] text-[16px] tracking-wide ${!toggle ? `text-white/60` : `text-layer-0-glyph`} el-re`} + > + {basis.label_l} + </p> + </div> + <div + class={`z-10 flex flex-row h-full basis-1/2 justify-center items-center bg-transparent`} + > + <p + class={`font-sans font-[400] text-[16px] tracking-wide ${toggle ? `text-white/60` : `text-layer-0-glyph`} el-re`} + > + {basis.label_r} + </p> + </div> + <div + class={`z-5 absolute top-0 bottom-0 flex flex-row h-full w-[50%] justify-center items-center rounded-touch bg-th-black transition-all duration-[400ms] ${toggle ? `translate-x-full` : `translate-x-0`}`} + > + <Empty /> + </div> +</button> diff --git a/apps-lib/src/lib/components/button/button-label-dashed.svelte b/apps-lib/src/lib/components/button/button-label-dashed.svelte @@ -0,0 +1,24 @@ +<script lang="ts"> + import type { ICb } from "@radroots/util"; + + let { + basis, + }: { + basis: ICb & { + label: string; + }; + } = $props(); +</script> + +<button + class={`flex flex-row h-line_button w-full justify-center items-center`} + onclick={async () => { + await basis.callback(); + }} +> + <p + class={`font-sans font-[500] text-lg text-layer-0-glyph-hl tracking-wide`} + > + {`- ${basis.label} -`} + </p> +</button> diff --git a/apps-lib/src/lib/components/button/button-label-glyph.svelte b/apps-lib/src/lib/components/button/button-label-glyph.svelte @@ -0,0 +1,42 @@ +<script lang="ts"> + import { Glyph } from "$root"; + import { type GlyphKey, type ICb } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis, + children, + }: { + basis: ICb & { + label: string; + glyph?: GlyphKey; + ring?: boolean; + }; + children?: Snippet; + } = $props(); +</script> + +<div class={`flex flex-col w-full gap-1 justify-center items-center`}> + <button + class={`flex flex-row h-12 w-12 p-2 justify-center items-center rounded-2xl bg-layer-1-surface ${basis.ring ? `ring-[4px] ring-layer-2-surface/40` : ``} el-re`} + onclick={async () => { + await basis.callback(); + }} + > + {#if basis.glyph} + <Glyph + basis={{ + classes: `text-layer-0-glyph/80`, + dim: `sm`, + + key: basis.glyph, + }} + /> + {:else if children} + {@render children()} + {/if} + </button> + <p class={`font-sans font-[400] text-sm text-layer-0-glyph el-re`}> + {basis.label} + </p> +</div> diff --git a/apps-lib/src/lib/components/button/button-layout-pair.svelte b/apps-lib/src/lib/components/button/button-layout-pair.svelte @@ -0,0 +1,62 @@ +<script lang="ts"> + import { app_lo, ButtonLayout, Empty } from "$root"; + import { + fmt_cl, + type CallbackPromise, + type IClOpt, + type IDisabledOpt, + } from "@radroots/util"; + + let { + basis, + }: { + basis: IClOpt & { + continue: IDisabledOpt & { + label: string; + callback: CallbackPromise; + }; + back?: IDisabledOpt & { + visible: boolean; + label?: string; + callback: CallbackPromise; + }; + }; + } = $props(); +</script> + +<div + class={`${fmt_cl(basis.classes)} flex flex-col gap-1 justify-center items-center ${basis?.back?.visible ? `-translate-y-1` : ``}`} +> + <ButtonLayout + basis={{ + disabled: basis.continue.disabled, + label: basis.continue.label, + callback: basis.continue.callback, + }} + ></ButtonLayout> + {#if basis.back} + <div class={`flex flex-col justify-center items-center el-re`}> + {#if basis.back.visible} + <button + class={`group flex flex-row h-12 w-lo_${$app_lo} justify-center items-center fade-in el-re`} + onclick={async (ev) => { + ev.stopPropagation(); + if (!basis.back?.disabled) await basis.back?.callback(); + }} + > + <p + class={`font-sans font-[600] tracking-wide text-layer-1-glyph-shade ${basis.back?.disabled ? `` : `group-active:text-layer-1-glyph/40`} el-re`} + > + {basis.back.label || ``} + </p> + </button> + {:else} + <div + class={`flex flex-row h-4 w-full justify-start items-center`} + > + <Empty /> + </div> + {/if} + </div> + {/if} +</div> diff --git a/apps-lib/src/lib/components/button/button-layout.svelte b/apps-lib/src/lib/components/button/button-layout.svelte @@ -0,0 +1,46 @@ +<script lang="ts"> + import { app_lo } from "$lib/store/app"; + import { + fmt_cl, + parse_layer, + type CallbackPromise, + type IClOpt, + type IDisabledOpt, + type ILyOpt, + } from "@radroots/util"; + + let { + basis, + }: { + basis: ILyOpt & + IClOpt & + IDisabledOpt & { + classes_inner?: string; + hide_active?: boolean; + label: string; + callback: CallbackPromise; + }; + } = $props(); + + const layer = $derived(parse_layer(basis.layer, 1)); + + const classes_active = $derived( + !basis.hide_active + ? `layer-1-active-surface layer-1-active-raise-less layer-1-active-ring-less` + : ``, + ); +</script> + +<button + class={`${fmt_cl(basis.classes)} group flex flex-row h-touch_guide w-lo_${$app_lo} justify-center items-center bg-layer-${layer}-surface rounded-touch ${basis.disabled ? `opacity-60` : classes_active} el-re`} + onclick={async (ev) => { + ev.stopPropagation(); + if (!basis.disabled) await basis.callback(); + }} +> + <p + class={`${fmt_cl(basis.classes_inner)} font-sans font-[600] tracking-wide text-layer-${layer}-glyph-shade ${basis.disabled ? `` : `group-active:text-layer-${layer}-glyph/40 `} el-re`} + > + {basis.label || ``} + </p> +</button> diff --git a/apps-lib/src/lib/components/button/button-round.svelte b/apps-lib/src/lib/components/button/button-round.svelte @@ -0,0 +1,25 @@ +<script lang="ts"> + import { LoadSymbol } from "$root"; + import { fmt_cl, type IButtonRound } from "@radroots/util"; + + let { + basis, + }: { + basis: IButtonRound; + } = $props(); +</script> + +<button + class={`${fmt_cl(basis.classes)} group flex flex-row h-touch_guide w-full justify-center items-center rounded-full el-re`} + onclick={async () => { + await basis.callback(); + }} +> + {#if basis.loading} + <LoadSymbol basis={{ dim: `md` }} /> + {:else} + <p class={`font-sans font-[500] text-[16px]`}> + {basis.label} + </p> + {/if} +</button> diff --git a/apps-lib/src/lib/components/button/button-simple-glyph.svelte b/apps-lib/src/lib/components/button/button-simple-glyph.svelte @@ -0,0 +1,33 @@ +<script lang="ts"> + import { + parse_layer, + type CallbackPromise, + type ILyOpt, + } from "@radroots/util"; + + let { + basis, + }: { + basis: ILyOpt & { + label: string; + callback: CallbackPromise; + allow_propogation?: boolean; + }; + } = $props(); + + const layer = $derived(parse_layer(basis?.layer ? basis.layer : 1)); +</script> + +<button + class={`group flex flex-row h-[3.5rem] w-full justify-center items-center rounded-touch`} + onclick={async (ev) => { + if (!basis.allow_propogation) ev.stopPropagation(); + await basis.callback(); + }} +> + <p + class={`font-sans font-[700] text-xl text-layer-${layer}-glyph capitalize tracking-wider opacity-active`} + > + {basis.label} + </p> +</button> diff --git a/apps-lib/src/lib/components/button/button-simple.svelte b/apps-lib/src/lib/components/button/button-simple.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import { parse_layer, type IButtonSimple } from "@radroots/util"; + + let { basis }: { basis: IButtonSimple } = $props(); + + const layer = $derived(parse_layer(basis.layer || 1)); +</script> + +<button + class={`group flex flex-row h-line_button w-full justify-center items-center rounded-touch bg-layer-${layer}-surface layer-${layer}-active-surface layer-${layer}-active-ring`} + onclick={async (ev) => { + if (!basis.allow_propogation) ev.stopPropagation(); + await basis.callback(); + }} +> + <p + class={`font-sans font-[700] text-xl text-layer-0-glyph capitalize tracking-wider opacity-active`} + > + {basis.label} + </p> +</button> diff --git a/apps-lib/src/lib/components/button/float-page-button.svelte b/apps-lib/src/lib/components/button/float-page-button.svelte @@ -0,0 +1,40 @@ +<script lang="ts"> + import { Glyph, LoadSymbol } from "$root"; + import { + type GeometryScreenPositionHorizontal, + type ICb, + type IGlyphKey, + type ILoadingOpt, + } from "@radroots/util"; + + let { + basis, + }: { + basis: ICb & + ILoadingOpt & + IGlyphKey & { + posx: Omit<GeometryScreenPositionHorizontal, "center">; + }; + } = $props(); +</script> + +<div class={`absolute top-10 ${basis.posx}-6 flex flex-row`}> + <button + class={`flex flex-row h-12 w-12 justify-center items-center bg-layer-1-surface rounded-full el-re`} + onclick={basis.callback} + > + {#if basis.loading} + <LoadSymbol /> + {:else} + <Glyph + basis={{ + classes: `text-layer-0-glyph`, + dim: `sm+`, + + key: basis.glyph, + }} + /> + {/if} + </button> +</div> +<div class="hidden left-6 right-6"></div> diff --git a/apps-lib/src/lib/components/button/glyph-button-simple.svelte b/apps-lib/src/lib/components/button/glyph-button-simple.svelte @@ -0,0 +1,47 @@ +<script lang="ts"> + import { Glyph } from "$root"; + import { + fmt_cl, + type CallbackPromise, + type GlyphKey, + type IClOpt, + } from "@radroots/util"; + + let { + basis, + }: { + basis: IClOpt & { + kind?: `primary` | `neutral`; + label: string; + callback: CallbackPromise; + glyph?: GlyphKey; + }; + } = $props(); + + const classes_kind = $derived( + basis.kind === `neutral` + ? `text-layer-0-glyph-shade` + : `text-layer-0-glyph-hl`, + ); +</script> + +<button + class={`${fmt_cl(basis.classes)} group flex flex-row justify-center items-center`} + onclick={basis.callback} +> + {#if basis.glyph} + <Glyph + basis={{ + classes: `${classes_kind}`, + dim: `sm+`, + + key: basis.glyph, + }} + /> + {/if} + <p + class={`font-sans font-[600] text-line_label ${classes_kind} opacity-active`} + > + {basis.label} + </p> +</button> diff --git a/apps-lib/src/lib/components/button/glyph-button.svelte b/apps-lib/src/lib/components/button/glyph-button.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { fmt_cl, glyph_style_map, type IGlyph } from "@radroots/util"; + + let { basis }: { basis: IGlyph } = $props(); + const styles = $derived( + basis?.dim ? glyph_style_map.get(basis.dim) : glyph_style_map.get(`sm`), + ); +</script> + +<!-- svelte-ignore a11y_consider_explicit_label --> +<button + class={`${fmt_cl(basis.classes)} flex flex-col justify-center items-center text-[${styles?.gl_1}px] el-re`} + onclick={async () => { + if (basis.callback) await basis.callback(); + }} +> + <i class={`ph-bold ph-${basis.key}`}></i> +</button> diff --git a/apps-lib/src/lib/components/carousel/carousel-flow-item.svelte b/apps-lib/src/lib/components/carousel/carousel-flow-item.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { fmt_cl, type IClOpt } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis, + children, + }: { + basis?: IClOpt; + children: Snippet; + } = $props(); +</script> + +<div + class={`${fmt_cl(basis?.classes)} flex flex-col flex-shrink-0 h-full w-full justify-start items-start`} +> + {@render children()} +</div> diff --git a/apps-lib/src/lib/components/carousel/carousel-flow.svelte b/apps-lib/src/lib/components/carousel/carousel-flow.svelte @@ -0,0 +1,37 @@ +<script lang="ts"> + import { casl_i } from "@radroots/lib-app"; + import { fmt_cl, type IClOpt } from "@radroots/util"; + import { onMount, type Snippet } from "svelte"; + + let { + basis, + el_parent = $bindable(null), + children, + }: { + basis?: IClOpt; + el_parent?: HTMLDivElement | null; + children: Snippet; + } = $props(); + + let offset_w = $state(0); + + onMount(() => { + if (el_parent && el_parent.children.length > 0) + offset_w = (el_parent.children[0] as HTMLElement).offsetWidth; + }); + + window.addEventListener("resize", () => { + if (el_parent && el_parent.children.length > 0) + offset_w = (el_parent.children[0] as HTMLElement).offsetWidth; + }); +</script> + +<div class={`${fmt_cl(basis?.classes)}relative flex flex-col h-full w-full`}> + <div + bind:this={el_parent} + class="flex flex-grow transition-transform duration-500" + style={`transform: translateX(-${Math.max($casl_i, 0) * offset_w}px)`} + > + {@render children()} + </div> +</div> diff --git a/apps-lib/src/lib/components/carousel/carousel-item.svelte b/apps-lib/src/lib/components/carousel/carousel-item.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis = undefined, + children, + }: { + basis?: IBasisOpt<IClOpt>; + children: Snippet; + } = $props(); +</script> + +<div + class={`${fmt_cl(basis?.classes)} flex flex-col flex-shrink-0 w-[100vw] justify-start items-center`} +> + {@render children()} +</div> diff --git a/apps-lib/src/lib/components/carousel/carousel.svelte b/apps-lib/src/lib/components/carousel/carousel.svelte @@ -0,0 +1,24 @@ +<script lang="ts"> + import { casl_i } from "$root"; + import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis = undefined, + children, + }: { + basis?: IBasisOpt<IClOpt>; + children: Snippet; + } = $props(); +</script> + +<div + class={`${fmt_cl(basis?.classes)} relative flex flex-col w-full overflow-hidden`} +> + <div + class={`flex h-full transition-transform duration-500`} + style={`transform: translateX(-${Math.max($casl_i, 0) * 100}vw)`} + > + {@render children()} + </div> +</div> diff --git a/apps-lib/src/lib/components/envelope/envelope-lower-full.svelte b/apps-lib/src/lib/components/envelope/envelope-lower-full.svelte @@ -0,0 +1,29 @@ +<script lang="ts"> + import type { Snippet } from "svelte"; + import { quintInOut } from "svelte/easing"; + import { fly } from "svelte/transition"; + + let { + basis, + children, + }: { + basis: { + visible: boolean; + }; + children: Snippet; + } = $props(); +</script> + +{#if basis.visible} + <div + in:fly={{ y: `100%`, easing: quintInOut }} + out:fly={{ y: `100%`, easing: quintInOut }} + class={`z-50 absolute bottom-0 left-0 flex flex-col h-[100vh] w-full justify-start items-start bg-transparent el-re`} + > + <div + class={`z-[100] [flex flex-col h-full w-full justify-start items-center overflow-y-scroll overflow-x-hidden scroll-hide el-re`} + > + {@render children()} + </div> + </div> +{/if} diff --git a/apps-lib/src/lib/components/envelope/map-location-select-envelope.svelte b/apps-lib/src/lib/components/envelope/map-location-select-envelope.svelte @@ -0,0 +1,85 @@ +<script lang="ts"> + import { ButtonSimple, EnvelopeLowerFull, Map, MapMarkerArea } from "$root"; + import { + type CallbackPromiseGeneric, + type GeocoderReverseResult, + type GeolocationPoint, + type LcGeocodeCallback, + type ResolveFarmInfo, + cfg_map, + parse_geol_point_tup, + } from "@radroots/util"; + + let { + basis, + map_geop = $bindable(undefined), + map_geoc = $bindable(undefined), + }: { + basis: { + farm_info: ResolveFarmInfo; + visible: boolean; + lc_geocode: LcGeocodeCallback; + lc_submit: CallbackPromiseGeneric<{ + map_geop: GeolocationPoint | undefined; + map_geoc: GeocoderReverseResult | undefined; + }>; + }; + map_geop?: GeolocationPoint; + map_geoc?: GeocoderReverseResult; + } = $props(); + + let map: maplibregl.Map | undefined = $state(undefined); + let map_init = $state(false); + + $effect(() => { + if (basis.visible && map && map_geop && !map_init) { + map.setCenter(parse_geol_point_tup(map_geop)); + map_init = true; + } + }); + + $effect(() => { + if (basis.visible === false) map_init = false; //@todo + }); +</script> + +<EnvelopeLowerFull + basis={{ + visible: basis.visible, + }} +> + <div + class={`relative flex flex-col h-full w-full justify-center items-center bg-layer-1-surface`} + > + <Map + bind:map + basis={{ + zoom_click_off: true, + }} + > + {#if map_geop} + <MapMarkerArea + bind:map_geop + bind:map_geoc + basis={{ + show_display: !!map_geop, + lc_geocode: basis.lc_geocode, + }} + /> + {/if} + </Map> + <div + class={`absolute bottom-0 left-0 flex flex-row h-24 w-full px-4 justify-center items-start`} + > + <ButtonSimple + basis={{ + label: `Choose location`, + callback: async () => { + await basis.lc_submit({ map_geoc, map_geop }); + if (map) map.setCenter(cfg_map.coords.default); + }, + }} + /> + </div> + </div> +</EnvelopeLowerFull> diff --git a/apps-lib/src/lib/components/form/entry-line-idb.svelte b/apps-lib/src/lib/components/form/entry-line-idb.svelte @@ -0,0 +1,66 @@ +<script lang="ts"> + import { EntryWrap, Glyph, Input, LoadSymbol } from "$root"; + import { + parse_layer, + type IEntryLineIdb, + type LoadingDimension, + } from "@radroots/util"; + + let { + basis, + }: { + basis: IEntryLineIdb; + } = $props(); + + const layer = $derived( + parse_layer( + typeof basis.wrap?.layer === `boolean` ? 0 : basis.wrap?.layer, + ), + ); + + const classes_layer = $derived( + typeof basis.wrap?.layer === `boolean` + ? `` + : `text-layer-${layer}-glyph`, + ); + + const loading_dim: LoadingDimension = $derived( + basis.wrap?.style === `guide` ? `md` : `sm`, + ); +</script> + +<EntryWrap basis={basis?.wrap}> + <Input + basis={{ + ...basis.el, + classes: `h-entry_line ${basis.el.classes}`, + }} + /> + {#if basis.loading} + <div + class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-4 justify-end items-center fade-in el-re`} + > + <LoadSymbol + basis={{ + dim: loading_dim, + }} + /> + </div> + {:else if basis.notify_inline} + {#if `glyph` in basis.notify_inline} + <div + class={`z-5 absolute el-re right-0 top-0 flex flex-row h-full pr-3 justify-end items-center translate-x-[34px] fade-in`} + > + <Glyph + basis={typeof basis.notify_inline.glyph === `string` + ? { + key: basis.notify_inline.glyph, + dim: `xs+`, + classes: `${classes_layer}`, + } + : basis.notify_inline.glyph} + /> + </div> + {/if} + {/if} +</EntryWrap> diff --git a/apps-lib/src/lib/components/form/entry-line.svelte b/apps-lib/src/lib/components/form/entry-line.svelte @@ -0,0 +1,69 @@ +<script lang="ts"> + import { EntryWrap, Glyph, InputValue, LoadSymbol } from "$root"; + import { + parse_layer, + type IEntryLine, + type LoadingDimension, + } from "@radroots/util"; + + let { + basis, + value = $bindable(``), + }: { + basis: IEntryLine; + value: string; + } = $props(); + + const layer = $derived( + parse_layer( + typeof basis.wrap?.layer === `boolean` ? 0 : basis.wrap?.layer, + ), + ); + + const classes_layer = $derived( + typeof basis.wrap?.layer === `boolean` + ? `` + : `text-layer-${layer}-glyph`, + ); + + const loading_dim: LoadingDimension = $derived( + basis.wrap?.style === `guide` ? `md` : `sm`, + ); +</script> + +<EntryWrap basis={basis?.wrap}> + <InputValue + bind:value + basis={{ + ...basis.el, + classes: `h-entry_line ${basis.el.classes}`, + }} + /> + {#if basis.loading} + <div + class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-4 justify-end items-center fade-in el-re`} + > + <LoadSymbol + basis={{ + dim: loading_dim, + }} + /> + </div> + {:else if basis.notify_inline} + {#if `glyph` in basis.notify_inline} + <div + class={`z-5 absolute el-re right-0 top-0 flex flex-row h-full pr-3 justify-end items-center translate-x-[34px] fade-in`} + > + <Glyph + basis={typeof basis.notify_inline.glyph === `string` + ? { + key: basis.notify_inline.glyph, + dim: `xs+`, + classes: `${classes_layer}`, + } + : basis.notify_inline.glyph} + /> + </div> + {/if} + {/if} +</EntryWrap> diff --git a/apps-lib/src/lib/components/form/entry-multiline.svelte b/apps-lib/src/lib/components/form/entry-multiline.svelte @@ -0,0 +1,49 @@ +<script lang="ts"> + import { Glyph, TextArea } from "$root"; + import { fmt_cl, parse_layer, type IEntryMultiLine } from "@radroots/util"; + + let { + basis, + value = $bindable(``), + }: { + basis: IEntryMultiLine; + value: string; + } = $props(); + + const layer = $derived( + parse_layer( + typeof basis.wrap?.layer === `boolean` ? 0 : basis.wrap?.layer, + ), + ); + + const classes_layer = $derived( + typeof layer === `boolean` + ? `bg-transparent` + : `bg-layer-${layer}-surface`, + ); +</script> + +<div + id={basis.wrap?.id || null} + class={`${fmt_cl(basis.wrap?.classes)} relative el-re entry-textarea-wrap ${classes_layer}`} +> + <TextArea bind:value basis={basis.el} /> + {#if basis.notify_inline} + {#if `glyph` in basis.notify_inline} + <div + class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-3 justify-end items-center translate-x-[34px] fade-in `} + > + <Glyph + basis={typeof basis.notify_inline.glyph === `string` + ? { + key: basis.notify_inline.glyph, + dim: `xs+`, + + classes: `text-layer-${layer}-glyph`, + } + : basis.notify_inline.glyph} + /> + </div> + {/if} + {/if} +</div> diff --git a/apps-lib/src/lib/components/form/entry-select.svelte b/apps-lib/src/lib/components/form/entry-select.svelte @@ -0,0 +1,48 @@ +<script lang="ts"> + import { EntryWrap, Glyph, LoadSymbol, Select } from "$root"; + import { fmt_cl, parse_layer, type IEntrySelect } from "@radroots/util"; + + let { + basis, + value = $bindable(``), + }: { + basis: IEntrySelect; + value: string; + } = $props(); + + const layer = $derived( + typeof basis?.wrap.layer === `boolean` + ? parse_layer(0) + : parse_layer(basis?.wrap.layer, 1), + ); +</script> + +<EntryWrap basis={basis?.wrap}> + {#if basis?.loading} + <div class={`flex flex-row w-full justify-center items-center`}> + <LoadSymbol basis={{ dim: `sm`, blades: 8 }} /> + </div> + {:else} + <Select + bind:value + basis={{ + ...basis.el, + classes: `${fmt_cl(basis.el.classes)} w-full`, + }} + /> + {/if} + {#if !basis?.hide_arrows} + <div + class={`z-5 absolute right-0 top-0 flex flex-row h-full pr-4 justify-end items-center`} + > + <Glyph + basis={{ + key: `caret-up-down`, + dim: `xs`, + + classes: `text-layer-${layer}-glyph_d`, + }} + /> + </div> + {/if} +</EntryWrap> diff --git a/apps-lib/src/lib/components/form/entry-wrap.svelte b/apps-lib/src/lib/components/form/entry-wrap.svelte @@ -0,0 +1,36 @@ +<script lang="ts"> + import { + fmt_cl, + parse_layer, + type IBasisOpt, + type IEntryWrap, + } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis = undefined, + children, + }: { + basis?: IBasisOpt<IEntryWrap>; + children: Snippet; + } = $props(); + + const layer = $derived( + typeof basis?.layer === `boolean` + ? parse_layer(0) + : parse_layer(basis?.layer), + ); + + const classes_layer = $derived( + typeof basis?.layer === `boolean` + ? `bg-transparent` + : `bg-layer-${layer}-surface ${basis?.style_a ? `active:bg-layer-${layer}-surface_a` : ``}`, + ); +</script> + +<button + id={basis?.id || null} + class={`${fmt_cl(basis?.classes)} relative entry-line-wrap ${!basis?.no_pad ? ` pl-6 pr-4` : ``} h-entry_${basis?.style ? basis.style : `line`} rounded-touch ${classes_layer} el-re`} +> + {@render children()} +</button> diff --git a/apps-lib/src/lib/components/form/form-entry-input.svelte b/apps-lib/src/lib/components/form/form-entry-input.svelte @@ -0,0 +1,44 @@ +<script lang="ts"> + import { EntryLine, fmt_id, LayoutTrellisLine } from "$root"; + import type { FormField, IIdOpt } from "@radroots/util"; + + let { + basis, + val = $bindable(``), + }: { + basis: IIdOpt & { + entry_label: string; + input_placeholder?: string; + input_field?: FormField; + }; + val: string; + } = $props(); + + const id = $derived(basis.id || ``); +</script> + +<LayoutTrellisLine + basis={{ + label: { + value: basis.entry_label || ``, + }, + }} +> + <EntryLine + bind:value={val} + basis={{ + wrap: { + id: id ? fmt_id(`${id}_wrap`) : undefined, + layer: 1, + }, + el: { + // id: id ? fmt_id(id) : undefined, + layer: 1, + classes: `fade-in`, + placeholder: basis.input_placeholder || ``, + field: basis.input_field || undefined, + field_constrain: !!basis.input_field, + }, + }} + /> +</LayoutTrellisLine> diff --git a/apps-lib/src/lib/components/form/form-entry-price-quantity.svelte b/apps-lib/src/lib/components/form/form-entry-price-quantity.svelte @@ -0,0 +1,113 @@ +<script lang="ts"> + import { + EntryWrap, + fmt_id, + Input, + LayoutTrellisLine, + lib_fmt_price, + lib_parse_currency_marker, + lls, + Select, + } from "$root"; + import { + fiat_currencies, + mass_units, + num_str, + type ElementCallbackValue, + type FormField, + } from "@radroots/util"; + + let { + basis, + val_input_price = $bindable(``), + val_sel_currency = $bindable(``), + val_sel_qty_unit = $bindable(``), + }: { + basis: { + id?: string; + input_field?: FormField; + callback_input: ElementCallbackValue; + }; + val_input_price: string; + val_sel_currency: string; + val_sel_qty_unit: string; + } = $props(); + + const id = $derived(basis.id ? basis.id : ``); +</script> + +<LayoutTrellisLine + basis={{ + label: { + value: `${$lls(`icu.*_price`, { value: `${$lls(`common.product`)}` })} (${val_sel_currency}/${`${$lls(`measurement.mass.unit.${val_sel_qty_unit}_ab`)}`})`, + }, + }} +> + <EntryWrap + basis={{ + id: id ? fmt_id(`${id}_wrap`) : undefined, + layer: 1, + }} + > + <div class={`flex flex-row justify-start pr-1 items-center`}> + <Select + bind:value={val_sel_currency} + basis={{ + id: id ? fmt_id(`${id}_sel_currency`) : undefined, + layer: 1, + sync: true, + classes: `w-fit font-sans font-[400] ${val_input_price ? `text-layer-1-glyph_d` : `text-layer-1-glyph_pl`} el-re`, + options: [ + { + entries: fiat_currencies.map((i) => ({ + value: `${i}`, + label: lib_parse_currency_marker(i), + })), + }, + ], + }} + /> + </div> + <Input + bind:value={val_input_price} + basis={{ + id: id ? fmt_id(`${id}_input_price`) : undefined, + layer: 1, + sync: true, + placeholder: `${$lls(`icu.enter_the_*`, { value: `${$lls(`common.price`)}`.toLowerCase() })}`, + field: basis.input_field, + callback: basis.callback_input, + callback_blur: async ({ el }) => { + if (!el.value) return; + el.value = lib_fmt_price(el.value, val_sel_currency).slice( + 1, + ); + //@todo fmt handles 'en' only + }, + }} + /> + <div class={`flex flex-row gap-2 justify-end items-center`}> + <p class={`font-sans font-[400] text-layer-1-glyph_d lowercase`}> + {num_str(1)} + </p> + <Select + bind:value={val_sel_qty_unit} + basis={{ + id: id ? fmt_id(`${id}_sel_qty_unit`) : undefined, + sync: true, + layer: 1, + classes: `w-fit font-sans font-[400]`, + show_arrows: `r`, + options: [ + { + entries: mass_units.map((i) => ({ + value: i, + label: `${$lls(`measurement.mass.unit.${i}_ab`)}`.toLowerCase(), + })), + }, + ], + }} + /> + </div> + </EntryWrap> +</LayoutTrellisLine> diff --git a/apps-lib/src/lib/components/form/form-entry-price.svelte b/apps-lib/src/lib/components/form/form-entry-price.svelte @@ -0,0 +1,118 @@ +<script lang="ts"> + import { + EntryWrap, + fmt_id, + Input, + LayoutTrellisLine, + lib_fmt_price, + lib_parse_currency_marker, + lls, + Select, + } from "$root"; + import { + fiat_currencies, + form_fields, + mass_units, + type ElementCallbackValue, + type IIdOpt, + } from "@radroots/util"; + + let { + basis, + val_input_price = $bindable(``), + val_sel_currency = $bindable(``), + val_sel_quantity_unit = $bindable(``), + }: { + basis: IIdOpt & { + display_symbol?: boolean; + entry_label?: string; + input_placeholder?: string; + callback_input?: ElementCallbackValue; + }; + val_input_price: string; + val_sel_currency: string; + val_sel_quantity_unit: string; + } = $props(); + + const id = $derived(basis.id ? basis.id : ``); +</script> + +<LayoutTrellisLine + basis={{ + label: { + value: basis.entry_label || ``, + }, + }} +> + <EntryWrap + basis={{ + id: id ? fmt_id(`${id}_wrap`) : undefined, + layer: 1, + }} + > + <Input + bind:value={val_input_price} + basis={{ + id: id ? fmt_id(`${id}_input_price`) : undefined, + layer: 1, + sync: true, + placeholder: basis.input_placeholder, + field: form_fields.price, + callback: basis.callback_input, + callback_blur: async ({ el }) => { + if (!el.value) return; + el.value = lib_fmt_price(el.value, val_sel_currency).slice( + 1, + ); + //@todo fmt handles 'en' only + }, + }} + /> + <div + class={`absolute right-4 flex flex-row justify-start items-center`} + > + <Select + bind:value={val_sel_currency} + basis={{ + id: id ? fmt_id(`${id}_sel_currency`) : undefined, + layer: 1, + sync: true, + classes: `w-fitfont-sans font-[400] el-re`, + options: [ + { + entries: fiat_currencies.map((i) => ({ + value: i, + label: basis.display_symbol + ? lib_parse_currency_marker(i) + : i.toUpperCase(), + })), + }, + ], + }} + /> + <div class={`flex flex-row px-2 justify-start items-center`}> + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {`/`} + </p> + </div> + <Select + bind:value={val_sel_quantity_unit} + basis={{ + id: id ? fmt_id(`${id}_sel_qty_unit`) : undefined, + sync: true, + layer: 1, + classes: `w-fit font-sans font-[400]`, + show_arrows: `r`, + options: [ + { + entries: mass_units.map((i) => ({ + value: i, + label: `${$lls(`measurement.mass.unit.${i}_ab`)}`.toLowerCase(), + })), + }, + ], + }} + /> + </div> + </EntryWrap> +</LayoutTrellisLine> diff --git a/apps-lib/src/lib/components/form/form-entry-quantity.svelte b/apps-lib/src/lib/components/form/form-entry-quantity.svelte @@ -0,0 +1,103 @@ +<script lang="ts"> + import { + EntryWrap, + fmt_id, + Input, + LayoutTrellisLine, + lls, + Select, + } from "$root"; + import { + form_fields, + mass_units, + type ElementCallbackValue, + type IIdOpt, + } from "@radroots/util"; + + let { + basis, + val_input_quantity = $bindable(``), + val_sel_quantity_unit = $bindable(``), + val_sel_quantity_label = $bindable(``), + }: { + basis: IIdOpt & { + display_symbol?: boolean; + entry_label?: string; + input_placeholder?: string; + callback_input?: ElementCallbackValue; + }; + val_input_quantity: string; + val_sel_quantity_unit: string; + val_sel_quantity_label: string; + } = $props(); + + const id = $derived(basis.id ? basis.id : ``); +</script> + +<LayoutTrellisLine + basis={{ + label: { + value: basis.entry_label || ``, + }, + }} +> + <EntryWrap + basis={{ + id: id ? fmt_id(`${id}_wrap`) : undefined, + layer: 1, + }} + > + <Input + bind:value={val_input_quantity} + basis={{ + id: id ? fmt_id(`${id}_input_quantity`) : undefined, + layer: 1, + sync: true, + placeholder: basis.input_placeholder, + field: form_fields.quantity, + callback: basis.callback_input, + }} + /> + <div + class={`absolute right-4 flex flex-row gap-1 justify-start items-center`} + > + <Select + bind:value={val_sel_quantity_unit} + basis={{ + id: id ? fmt_id(`${id}_sel_quantity_unit`) : undefined, + layer: 1, + sync: true, + classes: `w-fit font-sans font-[400] el-re`, + options: [ + { + entries: mass_units.map((i) => ({ + value: i, + label: `${$lls(`measurement.mass.unit.${i}_ab`)}`.toLowerCase(), + })), + }, + ], + }} + /> + <Select + bind:value={val_sel_quantity_label} + basis={{ + id: id ? fmt_id(`${id}_sel_quantity_label`) : undefined, + sync: true, + layer: 1, + classes: `w-fit font-sans font-[400] pr-2`, + show_arrows: `r`, + options: [ + { + entries: [ + { + value: `bag`, + label: `${$lls(`common.bag`)}`.toLowerCase(), + }, + ], + }, + ], + }} + /> + </div> + </EntryWrap> +</LayoutTrellisLine> diff --git a/apps-lib/src/lib/components/form/form-entry-select-input.svelte b/apps-lib/src/lib/components/form/form-entry-select-input.svelte @@ -0,0 +1,92 @@ +<script lang="ts"> + import { + EntryLine, + EntrySelect, + fmt_id, + LayoutTrellisLine, + lls, + } from "$root"; + import { + type CallbackPromiseGeneric, + type ElementCallbackSelect, + type FormField, + type ISelectOption, + } from "@radroots/util"; + + let { + basis, + val_sel = $bindable(``), + val_sel_input = $bindable(``), + }: { + basis: { + id: string; + visible_input: boolean; + callback_select: ElementCallbackSelect; + callback_visible: CallbackPromiseGeneric<boolean>; + select_entries: ISelectOption<string>[]; + input_placeholder?: string; + input_field?: FormField; + entry_label?: string; + }; + val_sel: string; + val_sel_input: string; + } = $props(); +</script> + +<LayoutTrellisLine + basis={{ + label: { + value: basis.entry_label || ``, + }, + notify: basis.visible_input + ? { + label: { + value: `${$lls(`common.close`)}`, + }, + callback: async () => { + await basis.callback_visible(false); + }, + } + : undefined, + }} +> + {#if !basis.visible_input} + <EntrySelect + bind:value={val_sel} + basis={{ + wrap: { + id: fmt_id(`${basis.id}_wrap`), + layer: 1, + }, + el: { + id: fmt_id(basis.id), + sync: true, + layer: 1, + options: [ + { + entries: basis.select_entries, + }, + ], + callback: basis.callback_select, + }, + }} + /> + {:else} + <EntryLine + bind:value={val_sel_input} + basis={{ + wrap: { + id: fmt_id(`${basis.id}_wrap`), + layer: 1, + }, + el: { + layer: 1, + classes: `fade-in`, + placeholder: basis.input_placeholder || ``, + field: basis.input_field || undefined, + field_constrain: !!basis.input_field, + }, + }} + /> + {/if} +</LayoutTrellisLine> diff --git a/apps-lib/src/lib/components/form/form-entry-select.svelte b/apps-lib/src/lib/components/form/form-entry-select.svelte @@ -0,0 +1,54 @@ +<script lang="ts"> + import { EntrySelect, fmt_id, LayoutTrellisLine } from "$root"; + import { + type ElementCallbackSelect, + type IClOpt, + type IIdOpt, + type ISelectOption, + } from "@radroots/util"; + + let { + basis, + val = $bindable(``), + }: { + basis: IIdOpt & + IClOpt & { + callback?: ElementCallbackSelect; + entries: ISelectOption<string>[]; + entry_label?: string; + }; + val: string; + } = $props(); + + const id = $derived(basis.id || ``); +</script> + +<LayoutTrellisLine + basis={{ + label: { + value: basis.entry_label || ``, + }, + }} +> + <EntrySelect + bind:value={val} + basis={{ + wrap: { + id: id ? fmt_id(`${id}_wrap`) : undefined, + layer: 1, + }, + el: { + id: id ? fmt_id(id) : undefined, + classes: basis.classes, + sync: true, + layer: 1, + options: [ + { + entries: basis.entries, + }, + ], + callback: basis.callback, + }, + }} + /> +</LayoutTrellisLine> diff --git a/apps-lib/src/lib/components/form/form-entry-textarea.svelte b/apps-lib/src/lib/components/form/form-entry-textarea.svelte @@ -0,0 +1,51 @@ +<script lang="ts"> + import { EntryMultiline, LayoutTrellisLine, fmt_id } from "$root"; + import { + type ElementCallbackValueKeydown, + type FormField, + type IIdOpt, + } from "@radroots/util"; + + let { + basis, + val = $bindable(``), + }: { + basis: IIdOpt & { + entry_label?: string; + placeholder?: string; + field?: FormField; + callback_keydown?: ElementCallbackValueKeydown<HTMLTextAreaElement>; + }; + val: string; + } = $props(); + + const id = $derived(basis.id || ``); +</script> + +<LayoutTrellisLine + basis={{ + classes: `h-full`, + label: { + value: basis.entry_label || ``, + }, + }} +> + <EntryMultiline + bind:value={val} + basis={{ + wrap: { + id: id ? fmt_id(`${id}_wrap`) : undefined, + classes: `h-full`, + }, + el: { + classes: `h-full px-2 overflow-hidden`, + id: id ? fmt_id(id) : undefined, + sync: true, + placeholder: basis.placeholder || ``, + field: basis.field || undefined, + field_constrain: !!basis.field, + callback_keydown: basis.callback_keydown, + }, + }} + /> +</LayoutTrellisLine> diff --git a/apps-lib/src/lib/components/form/form-line-ledger-label-select-label.svelte b/apps-lib/src/lib/components/form/form-line-ledger-label-select-label.svelte @@ -0,0 +1,36 @@ +<script lang="ts"> + import { ascii } from "@radroots/util"; + + let { + basis, + }: { + basis: { + label: string; + }; + } = $props(); +</script> + +<div class={`flex flex-row justify-start items-center`}> + <p + class={`pr-[13px] font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} + > + {`(${basis.label}`} + </p> + <div + class={`relative flex flex-row justify-start items-center -translate-x-[10px] -translate-y-[1px]`} + > + <p + class={`absolute font-sansd text-trellis_ti text-layer-0-glyph-label uppercase scale-y-[70%] scale-x-[80%] -translate-y-[1px]`} + > + {`${ascii.up}`} + </p> + <p + class={`absolute font-sansd text-trellis_ti text-layer-0-glyph-label uppercase scale-y-[70%] scale-x-[80%] translate-y-[2px]`} + > + {`${ascii.down}`} + </p> + </div> + <p class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`}> + {`)`} + </p> +</div> diff --git a/apps-lib/src/lib/components/form/form-line-ledger-select.svelte b/apps-lib/src/lib/components/form/form-line-ledger-select.svelte @@ -0,0 +1,86 @@ +<script lang="ts"> + import { fmt_id, Input, Select } from "$root"; + import { + type ElementCallbackSelect, + type ElementCallbackValueKeydown, + type IIdOpt, + type ISelectOption, + } from "@radroots/util"; + + let { + basis, + value_input = $bindable(``), + value_sel = $bindable(``), + }: { + basis: IIdOpt & { + display_value?: string; + label?: string; + input: { + placeholder?: string; + callback_keydown?: + | ElementCallbackValueKeydown<HTMLInputElement> + | undefined; + }; + select: { + entries: ISelectOption<string>[]; + callback?: ElementCallbackSelect; + }; + }; + value_input?: string; + value_sel?: string; + } = $props(); + + const id = $derived(basis.id || ``); +</script> + +<div class={`flex flex-col w-full gap-2 justify-start items-start`}> + {#if basis.label} + <div class={`flex flex-row w-full justify-start items-center`}> + <p + class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} + > + {basis.label} + </p> + </div> + {/if} + <div + class={`relative flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} + > + {#if basis.display_value} + <p class={`font-sans font-[400] text-layer-0-glyph text-form_base`}> + {basis.display_value} + </p> + {:else} + <Input + bind:value={value_input} + basis={{ + id: id ? fmt_id(`${id}_input`) : undefined, + layer: 0, + classes: `h-10 placeholder:text-[1.1rem]`, + placeholder: basis.input.placeholder || ``, + callback_keydown: basis.input.callback_keydown, + }} + /> + <div + class={`absolute right-0 flex flex-row justify-center items-center`} + > + <Select + bind:value={value_sel} + basis={{ + classes: `w-fit text-layer-1-glyph`, + id: id ? fmt_id(`${id}_sel`) : undefined, + sync: true, + layer: 1, + show_arrows: `r`, + options: [ + { + entries: basis.select.entries, + }, + ], + callback: basis.select.callback, + }} + /> + </div> + {/if} + </div> +</div> diff --git a/apps-lib/src/lib/components/form/form-line-ledger.svelte b/apps-lib/src/lib/components/form/form-line-ledger.svelte @@ -0,0 +1,88 @@ +<script lang="ts"> + import { fmt_id, Input, SelectMenu } from "$root"; + import { + type ElementCallbackValueKeydown, + type FormField, + type IIdOpt, + type ISelectOption, + } from "@radroots/util"; + import FormLineLedgerLabelSelectLabel from "./form-line-ledger-label-select-label.svelte"; + + let { + basis, + value = $bindable(``), + value_label_sel = $bindable(``), + }: { + basis: IIdOpt & { + display_value?: string; + label?: string; + label_select?: { + label: string; + entries: ISelectOption<string>[]; + }; + input?: { + placeholder?: string; + field?: FormField; + callback_keydown?: + | ElementCallbackValueKeydown<HTMLInputElement> + | undefined; + }; + }; + value?: string; + value_label_sel?: string; + } = $props(); + + const id = $derived(basis.id || ``); +</script> + +<div class={`flex flex-col w-full gap-2 justify-start items-start`}> + {#if basis.label} + <div class={`flex flex-row w-full justify-start gap-1 items-center`}> + <p + class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} + > + {basis.label} + </p> + {#if basis.label_select} + <SelectMenu + bind:value={value_label_sel} + basis={{ + layer: 0, + options: [ + { + entries: basis.label_select.entries, + }, + ], + }} + > + <FormLineLedgerLabelSelectLabel + basis={{ + label: basis.label_select.label, + }} + /> + </SelectMenu> + {/if} + </div> + {/if} + <div + class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} + > + {#if basis.display_value} + <p class={`font-sans font-[400] text-layer-0-glyph text-form_base`}> + {basis.display_value} + </p> + {:else if basis.input} + <Input + bind:value + basis={{ + id: id ? fmt_id(id) : undefined, + layer: 0, + classes: `h-10 placeholder:text-[1.1rem]`, + field: basis.input.field, + placeholder: basis.input.placeholder || ``, + callback_keydown: basis.input.callback_keydown, + }} + /> + {/if} + </div> +</div> diff --git a/apps-lib/src/lib/components/form/form-review-display.svelte b/apps-lib/src/lib/components/form/form-review-display.svelte @@ -0,0 +1,48 @@ +<script lang="ts"> + import { Glyph } from "$root"; + import { + fmt_cl, + type CallbackPromise, + type IClOpt, + type IClOptWrap, + } from "@radroots/util"; + + let { + basis, + }: { + basis: IClOpt & + IClOptWrap & { + label: string; + value: string; + value_pl?: string; + handle_back: CallbackPromise; + }; + } = $props(); +</script> + +<div + class={`${fmt_cl(basis.classes_wr)} flex flex-row h-6 w-full justify-between items-start`} +> + <p + class={`font-sans font-[500] text-layer-1-glyph_d tracking-tight capitalize`} + > + {basis.label} + </p> + <button + class={`flex flex-row max-w-[210px] gap-1 justify-end items-start`} + onclick={async () => {}} + > + <p + class={`${fmt_cl(basis.classes)} font-sans font-[400] text-[1.05rem] text-justify truncate text-layer-1-glyph_d`} + > + {basis.value || basis.value_pl || ``} + </p> + <Glyph + basis={{ + classes: `text-layer-0-glyph pt-1`, + dim: `xs`, + key: `caret-right`, + }} + /> + </button> +</div> diff --git a/apps-lib/src/lib/components/form/trade_field_display_kv.svelte b/apps-lib/src/lib/components/form/trade_field_display_kv.svelte @@ -0,0 +1,86 @@ +<script lang="ts"> + import { fmt_id, Glyph, idb } from "$root"; + import { + el_focus, + fmt_cl, + type CallbackPromise, + type IClOpt, + } from "@radroots/util"; + + let { + basis, + }: { + basis: IClOpt & { + visible: boolean; + label: string; + display: IClOpt & { + undef?: string; + nostyle?: boolean; + } & ( + | { + kv: string; + } + | { + value: string; + } + ); + + kv_wrap?: string; + handle_back: CallbackPromise; + }; + } = $props(); + + const classes_undef = $derived( + basis.visible && + ((`kv` in basis.display && !idb.get(fmt_id(basis.display.kv))) || + (`value` in basis.display && !basis.display.value)) + ? `opacity-60` + : ``, + ); +</script> + +<div + class={`${fmt_cl(basis.classes)} flex flex-row h-6 w-full justify-between items-start`} +> + <p + class={`font-sans font-[500] text-layer-1-glyph_d tracking-tight capitalize`} + > + {basis.label} + </p> + {#if basis.visible} + <button + class={`flex flex-row max-w-[210px] gap-1 justify-end items-start`} + onclick={async () => { + await el_focus( + fmt_id( + basis.kv_wrap + ? basis.kv_wrap + : `kv` in basis.display + ? `${basis.display.kv}_wrap` + : ``, + ), + async () => await basis.handle_back(), + ); + }} + > + <p + class={`${fmt_cl(basis.display.classes)} font-sans font-[400] text-[1.05rem] text-justify truncate text-layer-1-glyph_d ${classes_undef} ${basis.display.nostyle ? `` : `capitalize`}`} + > + {#if `kv` in basis.display} + {#await idb.get(fmt_id(basis.display.kv)) then kv_val} + {kv_val || basis.display.undef || ``} + {/await} + {:else} + {basis.display.value || basis.display.undef || ``} + {/if} + </p> + <Glyph + basis={{ + classes: `text-layer-0-glyph ${classes_undef} pt-1`, + dim: `xs`, + key: `caret-right`, + }} + /> + </button> + {/if} +</div> diff --git a/apps-lib/src/lib/components/glyph/glyph-circle.svelte b/apps-lib/src/lib/components/glyph/glyph-circle.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { GlyphButton } from "$root"; + import { type IGlyphCircle, fmt_cl, glyph_style_map } from "@radroots/util"; + + let { basis }: { basis: IGlyphCircle } = $props(); + + const styles = $derived( + basis?.glyph?.dim + ? glyph_style_map.get(basis?.glyph?.dim) + : glyph_style_map.get(`sm`), + ); +</script> + +{#if styles?.dim_1} + <div + class={`${fmt_cl(basis?.classes_wrap)} flex flex-col h-[${styles?.dim_1}px] w-[${styles?.dim_1}px] justify-center items-center rounded-full el-re`} + > + <GlyphButton basis={basis?.glyph}></GlyphButton> + </div> +{/if} diff --git a/apps-lib/src/lib/components/glyph/glyph.svelte b/apps-lib/src/lib/components/glyph/glyph.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { fmt_cl, glyph_style_map, type IGlyph } from "@radroots/util"; + + let { basis }: { basis: IGlyph } = $props(); + + const styles = $derived( + basis?.dim ? glyph_style_map.get(basis.dim) : glyph_style_map.get(`sm`), + ); + + const weight = $derived(basis.weight ? `-${basis.weight}` : `-bold`); +</script> + +<div + id={basis.id || null} + class={`${fmt_cl(basis.classes)} flex flex-row text-[${styles?.gl_1}px] justify-center items-center`} +> + <i class={`ph${weight} ph-${basis.key}`}></i> +</div> diff --git a/apps-lib/src/lib/components/label/label-display.svelte b/apps-lib/src/lib/components/label/label-display.svelte @@ -0,0 +1,30 @@ +<script lang="ts"> + import { fmt_cl, parse_layer, type ILabelDisplay } from "@radroots/util"; + + let { basis }: { basis: ILabelDisplay } = $props(); + + const layer = $derived( + typeof basis?.layer === `boolean` ? 0 : parse_layer(basis?.layer, 1), + ); + + const classes_layer = $derived( + typeof layer === `boolean` + ? `bg-transparent` + : `bg-layer-${layer}-surface`, + ); + + const clases_style = $derived( + basis.style === `guide` ? `h-entry_guide rounded-touch` : ``, + ); +</script> + +<div + id={basis.id_wrap || null} + class={`${fmt_cl(basis.classes)} relative el-re entry-line-wrap px-2 ${classes_layer} ${clases_style}`} +> + <p + class={`${fmt_cl(basis.label.classes)} font-sans font-[400] text-layer-0-glyph`} + > + {basis.label.value} + </p> +</div> diff --git a/apps-lib/src/lib/components/layout/layout-bottom-button.svelte b/apps-lib/src/lib/components/layout/layout-bottom-button.svelte @@ -0,0 +1,12 @@ +<script lang="ts"> + import { app_lo } from "$lib/store/app"; + import type { Snippet } from "svelte"; + + let { children }: { children: Snippet } = $props(); +</script> + +<div + class={`z-10 absolute bottom-0 h-lo_bottom_button_${$app_lo} flex flex-row w-full px-4 justify-center items-center`} +> + {@render children()} +</div> diff --git a/apps-lib/src/lib/components/layout/layout-page.svelte b/apps-lib/src/lib/components/layout/layout-page.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis = undefined, + children, + }: { + basis?: IBasisOpt<IClOpt>; + children: Snippet; + } = $props(); +</script> + +<div + class={`${fmt_cl(basis?.classes)} flex flex-col w-full pt-4 px-4 pb-24 gap-4 justify-center items-center`} +> + {@render children()} +</div> diff --git a/apps-lib/src/lib/components/layout/layout-trellis-line.svelte b/apps-lib/src/lib/components/layout/layout-trellis-line.svelte @@ -0,0 +1,61 @@ +<script lang="ts"> + import { Glyph } from "$root"; + import { + fmt_cl, + type IBasisOpt, + type ILayoutTrellisLine, + } from "@radroots/util"; + import type { Snippet } from "svelte"; + import { fade } from "svelte/transition"; + + let { + basis = undefined, + children, + }: { + basis?: IBasisOpt<ILayoutTrellisLine>; + children: Snippet; + } = $props(); +</script> + +<div + class={`${fmt_cl(basis?.classes)} flex flex-col w-trellis_line gap-[6px] justify-start items-center`} +> + {#if (basis?.label && `value` in basis?.label) || basis?.notify} + <div + class={`flex flex-row h-5 w-full px-2 gap-[6px] justify-start items-center`} + > + {#if basis?.label && `value` in basis?.label} + <p + class={`${fmt_cl(basis?.label.classes)} font-sans font-[400] uppercase text-layer-0-glyph text-sm`} + > + {basis?.label.value} + </p> + {/if} + {#if basis?.notify} + <button + in:fade={{ duration: 200 }} + out:fade={{ delay: 50, duration: 200 }} + onclick={async () => { + await basis?.notify?.callback(); + }} + class={`${fmt_cl(basis?.notify.classes)} flex flex-row gap-1 justify-end items-center text-layer-0-glyph/80 el-re`} + > + {#if `glyph` in basis?.notify && basis?.notify?.glyph && basis.notify.glyph_first} + <Glyph basis={basis.notify.glyph} /> + {/if} + {#if `label` in basis?.notify && basis?.notify?.label && `value` in basis?.notify?.label} + <p + class={`${fmt_cl(basis?.notify.label.classes)} font-sans font-[500] uppercase text-xs`} + > + {basis?.notify.label.value} + </p> + {/if} + {#if `glyph` in basis?.notify && basis?.notify?.glyph && !basis.notify.glyph_first} + <Glyph basis={basis.notify.glyph} /> + {/if} + </button> + {/if} + </div> + {/if} + {@render children()} +</div> diff --git a/apps-lib/src/lib/components/layout/layout-trellis.svelte b/apps-lib/src/lib/components/layout/layout-trellis.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis = undefined, + children, + }: { + basis?: IBasisOpt<IClOpt>; + children: Snippet; + } = $props(); +</script> + +<div + class={`${fmt_cl(basis?.classes)} flex flex-col pb-12 gap-6 justify-center items-center scroll-hide`} +> + {@render children()} +</div> diff --git a/apps-lib/src/lib/components/layout/layout-view.svelte b/apps-lib/src/lib/components/layout/layout-view.svelte @@ -0,0 +1,50 @@ +<script lang="ts"> + import { nav_blur, ph_blur, tabs_blur } from "$root"; + import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util"; + import { onDestroy, onMount, type Snippet } from "svelte"; + + let { + basis = undefined, + el = $bindable(null), + children, + }: { + el?: HTMLDivElement | null; + basis?: IBasisOpt<IClOpt & { fade?: boolean }>; + children: Snippet; + } = $props(); + + onMount(async () => { + try { + el?.addEventListener("scroll", scrollChange); + } catch (e) { + } finally { + } + }); + + onDestroy(async () => { + try { + el?.removeEventListener("scroll", scrollChange); + } catch (e) { + } finally { + } + }); + + const scrollChange = (): void => { + if (Math.max(el?.scrollTop || 0, 0) > 10) nav_blur.set(true); + else nav_blur.set(false); + if (Math.max(el?.scrollTop || 0, 0) > 10) tabs_blur.set(true); + else tabs_blur.set(false); + if (Math.max(el?.scrollTop || 0, 0) > 30) ph_blur.set(true); + else ph_blur.set(false); + }; + + //${$layout_view_cover ? `` : `pt-h_lo_view_${$app_lo}`} +</script> + +<div + bind:this={el} + class={`${fmt_cl(basis?.classes)} absolute top-0 left-0 flex flex-col h-[100vh] w-full justify-start items-center scroll-hide overflow-auto`} + class:fade-in={basis?.fade} +> + {@render children()} +</div> diff --git a/apps-lib/src/lib/components/layout/layout-window.svelte b/apps-lib/src/lib/components/layout/layout-window.svelte @@ -0,0 +1,38 @@ +<script lang="ts"> + import { + app_lo, + app_tilt, + app_toast, + app_win, + envelope_tilt, + envelope_visible, + } from "$root"; + import { onMount } from "svelte"; + + let { children } = $props(); + + onMount(async () => { + try { + app_win.set({ h: window.innerHeight, w: window.innerWidth }); + app_toast.set(false); + } catch (e) { + console.log(`(layout mount) `, e); + } finally { + } + }); + + envelope_visible.subscribe(async (_envelope_visible) => { + if (_envelope_visible && $envelope_tilt) app_tilt.set(true); + else app_tilt.set(false); + }); +</script> + +<div + class={`relative lg:hidden flex flex-col h-[100vh] w-full bg-layer-0-surface ${$app_tilt ? `scale-y-[96%] translate-y-4 rounded-t-[3rem]` : ``} overflow-x-hidden overflow-y-scroll scroll-hide delay-75 duration-200 el-re`} +> + {#if $app_lo} + <div class={`flex flex-col h-full w-full`}> + {@render children()} + </div> + {/if} +</div> diff --git a/apps-lib/src/lib/components/lib/css.svelte b/apps-lib/src/lib/components/lib/css.svelte @@ -0,0 +1 @@ +<div class="hidden -bottom-lo_bottom_button_ios0 -bottom-lo_bottom_button_ios1 -bottom-lo_bottom_button_webm0 -bottom-lo_bottom_button_webm1 -bottom-nav_page_header_ios0 -bottom-nav_page_header_ios1 -bottom-nav_page_header_webm0 -bottom-nav_page_header_webm1 -bottom-nav_page_toolbar_ios0 -bottom-nav_page_toolbar_ios1 -bottom-nav_page_toolbar_webm0 -bottom-nav_page_toolbar_webm1 -bottom-nav_tabs_ios0 -bottom-nav_tabs_ios1 -bottom-nav_tabs_webm0 -bottom-nav_tabs_webm1 -left-lo_ios0 -left-lo_ios1 -left-lo_textdesc_ios0 -left-lo_textdesc_ios1 -left-lo_textdesc_webm0 -left-lo_textdesc_webm1 -left-lo_webm0 -left-lo_webm1 -right-lo_ios0 -right-lo_ios1 -right-lo_textdesc_ios0 -right-lo_textdesc_ios1 -right-lo_textdesc_webm0 -right-lo_textdesc_webm1 -right-lo_webm0 -right-lo_webm1 -top-lo_bottom_button_ios0 -top-lo_bottom_button_ios1 -top-lo_bottom_button_webm0 -top-lo_bottom_button_webm1 -top-nav_page_header_ios0 -top-nav_page_header_ios1 -top-nav_page_header_webm0 -top-nav_page_header_webm1 -top-nav_page_toolbar_ios0 -top-nav_page_toolbar_ios1 -top-nav_page_toolbar_webm0 -top-nav_page_toolbar_webm1 -top-nav_tabs_ios0 -top-nav_tabs_ios1 -top-nav_tabs_webm0 -top-nav_tabs_webm1 -translate-x-w_lo_ios0 -translate-x-w_lo_ios1 -translate-x-w_lo_textdesc_ios0 -translate-x-w_lo_textdesc_ios1 -translate-x-w_lo_textdesc_webm0 -translate-x-w_lo_textdesc_webm1 -translate-x-w_lo_webm0 -translate-x-w_lo_webm1 -translate-y-h_lo_bottom_button_ios0 -translate-y-h_lo_bottom_button_ios1 -translate-y-h_lo_bottom_button_webm0 -translate-y-h_lo_bottom_button_webm1 -translate-y-h_nav_page_header_ios0 -translate-y-h_nav_page_header_ios1 -translate-y-h_nav_page_header_webm0 -translate-y-h_nav_page_header_webm1 -translate-y-h_nav_page_toolbar_ios0 -translate-y-h_nav_page_toolbar_ios1 -translate-y-h_nav_page_toolbar_webm0 -translate-y-h_nav_page_toolbar_webm1 -translate-y-h_nav_tabs_ios0 -translate-y-h_nav_tabs_ios1 -translate-y-h_nav_tabs_webm0 -translate-y-h_nav_tabs_webm1 active:bg-layer-0-glyph active:bg-layer-0-glyph-hl active:bg-layer-0-glyph-hl_a active:bg-layer-0-glyph-label active:bg-layer-0-glyph-shade active:bg-layer-0-glyph_a active:bg-layer-0-glyph_pl active:bg-layer-0-surface active:bg-layer-0-surface-blur active:bg-layer-0-surface-edge active:bg-layer-0-surface_a active:bg-layer-0-surface_w active:bg-layer-1-glyph active:bg-layer-1-glyph-hl active:bg-layer-1-glyph-hl_a active:bg-layer-1-glyph-label active:bg-layer-1-glyph-shade active:bg-layer-1-glyph_a active:bg-layer-1-glyph_d active:bg-layer-1-glyph_pl active:bg-layer-1-surface active:bg-layer-1-surface-edge active:bg-layer-1-surface-err active:bg-layer-1-surface-focus active:bg-layer-1-surface_a active:bg-layer-2-glyph active:bg-layer-2-glyph-hl active:bg-layer-2-glyph-hl_a active:bg-layer-2-glyph-shade active:bg-layer-2-glyph_a active:bg-layer-2-glyph_d active:bg-layer-2-glyph_pl active:bg-layer-2-surface active:bg-layer-2-surface-edge active:bg-layer-2-surface_a active:bg-radroots-accent-focus active:border-layer-0-glyph active:border-layer-0-glyph-hl active:border-layer-0-glyph-hl_a active:border-layer-0-glyph-label active:border-layer-0-glyph-shade active:border-layer-0-glyph_a active:border-layer-0-glyph_pl active:border-layer-0-surface active:border-layer-0-surface-blur active:border-layer-0-surface-edge active:border-layer-0-surface_a active:border-layer-0-surface_w active:border-layer-1-glyph active:border-layer-1-glyph-hl active:border-layer-1-glyph-hl_a active:border-layer-1-glyph-label active:border-layer-1-glyph-shade active:border-layer-1-glyph_a active:border-layer-1-glyph_d active:border-layer-1-glyph_pl active:border-layer-1-surface active:border-layer-1-surface-edge active:border-layer-1-surface-err active:border-layer-1-surface-focus active:border-layer-1-surface_a active:border-layer-2-glyph active:border-layer-2-glyph-hl active:border-layer-2-glyph-hl_a active:border-layer-2-glyph-shade active:border-layer-2-glyph_a active:border-layer-2-glyph_d active:border-layer-2-glyph_pl active:border-layer-2-surface active:border-layer-2-surface-edge active:border-layer-2-surface_a active:border-radroots-accent-focus active:text-layer-0-glyph active:text-layer-0-glyph-hl active:text-layer-0-glyph-hl_a active:text-layer-0-glyph-label active:text-layer-0-glyph-shade active:text-layer-0-glyph_a active:text-layer-0-glyph_pl active:text-layer-0-surface active:text-layer-0-surface-blur active:text-layer-0-surface-edge active:text-layer-0-surface_a active:text-layer-0-surface_w active:text-layer-1-glyph active:text-layer-1-glyph-hl active:text-layer-1-glyph-hl_a active:text-layer-1-glyph-label active:text-layer-1-glyph-shade active:text-layer-1-glyph_a active:text-layer-1-glyph_d active:text-layer-1-glyph_pl active:text-layer-1-surface active:text-layer-1-surface-edge active:text-layer-1-surface-err active:text-layer-1-surface-focus active:text-layer-1-surface_a active:text-layer-2-glyph active:text-layer-2-glyph-hl active:text-layer-2-glyph-hl_a active:text-layer-2-glyph-shade active:text-layer-2-glyph_a active:text-layer-2-glyph_d active:text-layer-2-glyph_pl active:text-layer-2-surface active:text-layer-2-surface-edge active:text-layer-2-surface_a active:text-radroots-accent-focus bg-layer-0-glyph bg-layer-0-glyph-hl bg-layer-0-glyph-hl_a bg-layer-0-glyph-label bg-layer-0-glyph-shade bg-layer-0-glyph_a bg-layer-0-glyph_pl bg-layer-0-surface bg-layer-0-surface-blur bg-layer-0-surface-edge bg-layer-0-surface_a bg-layer-0-surface_w bg-layer-1-glyph bg-layer-1-glyph-hl bg-layer-1-glyph-hl_a bg-layer-1-glyph-label bg-layer-1-glyph-shade bg-layer-1-glyph_a bg-layer-1-glyph_d bg-layer-1-glyph_pl bg-layer-1-surface bg-layer-1-surface-edge bg-layer-1-surface-err bg-layer-1-surface-focus bg-layer-1-surface_a bg-layer-2-glyph bg-layer-2-glyph-hl bg-layer-2-glyph-hl_a bg-layer-2-glyph-shade bg-layer-2-glyph_a bg-layer-2-glyph_d bg-layer-2-glyph_pl bg-layer-2-surface bg-layer-2-surface-edge bg-layer-2-surface_a bg-radroots-accent-focus border-layer-0-glyph border-layer-0-glyph-hl border-layer-0-glyph-hl_a border-layer-0-glyph-label border-layer-0-glyph-shade border-layer-0-glyph_a border-layer-0-glyph_pl border-layer-0-surface border-layer-0-surface-blur border-layer-0-surface-edge border-layer-0-surface_a border-layer-0-surface_w border-layer-1-glyph border-layer-1-glyph-hl border-layer-1-glyph-hl_a border-layer-1-glyph-label border-layer-1-glyph-shade border-layer-1-glyph_a border-layer-1-glyph_d border-layer-1-glyph_pl border-layer-1-surface border-layer-1-surface-edge border-layer-1-surface-err border-layer-1-surface-focus border-layer-1-surface_a border-layer-2-glyph border-layer-2-glyph-hl border-layer-2-glyph-hl_a border-layer-2-glyph-shade border-layer-2-glyph_a border-layer-2-glyph_d border-layer-2-glyph_pl border-layer-2-surface border-layer-2-surface-edge border-layer-2-surface_a border-radroots-accent-focus bottom-lo_bottom_button_ios0 bottom-lo_bottom_button_ios1 bottom-lo_bottom_button_webm0 bottom-lo_bottom_button_webm1 bottom-nav_page_header_ios0 bottom-nav_page_header_ios1 bottom-nav_page_header_webm0 bottom-nav_page_header_webm1 bottom-nav_page_toolbar_ios0 bottom-nav_page_toolbar_ios1 bottom-nav_page_toolbar_webm0 bottom-nav_page_toolbar_webm1 bottom-nav_tabs_ios0 bottom-nav_tabs_ios1 bottom-nav_tabs_webm0 bottom-nav_tabs_webm1 focus:bg-layer-0-glyph focus:bg-layer-0-glyph-hl focus:bg-layer-0-glyph-hl_a focus:bg-layer-0-glyph-label focus:bg-layer-0-glyph-shade focus:bg-layer-0-glyph_a focus:bg-layer-0-glyph_pl focus:bg-layer-0-surface focus:bg-layer-0-surface-blur focus:bg-layer-0-surface-edge focus:bg-layer-0-surface_a focus:bg-layer-0-surface_w focus:bg-layer-1-glyph focus:bg-layer-1-glyph-hl focus:bg-layer-1-glyph-hl_a focus:bg-layer-1-glyph-label focus:bg-layer-1-glyph-shade focus:bg-layer-1-glyph_a focus:bg-layer-1-glyph_d focus:bg-layer-1-glyph_pl focus:bg-layer-1-surface focus:bg-layer-1-surface-edge focus:bg-layer-1-surface-err focus:bg-layer-1-surface-focus focus:bg-layer-1-surface_a focus:bg-layer-2-glyph focus:bg-layer-2-glyph-hl focus:bg-layer-2-glyph-hl_a focus:bg-layer-2-glyph-shade focus:bg-layer-2-glyph_a focus:bg-layer-2-glyph_d focus:bg-layer-2-glyph_pl focus:bg-layer-2-surface focus:bg-layer-2-surface-edge focus:bg-layer-2-surface_a focus:bg-radroots-accent-focus focus:border-layer-0-glyph focus:border-layer-0-glyph-hl focus:border-layer-0-glyph-hl_a focus:border-layer-0-glyph-label focus:border-layer-0-glyph-shade focus:border-layer-0-glyph_a focus:border-layer-0-glyph_pl focus:border-layer-0-surface focus:border-layer-0-surface-blur focus:border-layer-0-surface-edge focus:border-layer-0-surface_a focus:border-layer-0-surface_w focus:border-layer-1-glyph focus:border-layer-1-glyph-hl focus:border-layer-1-glyph-hl_a focus:border-layer-1-glyph-label focus:border-layer-1-glyph-shade focus:border-layer-1-glyph_a focus:border-layer-1-glyph_d focus:border-layer-1-glyph_pl focus:border-layer-1-surface focus:border-layer-1-surface-edge focus:border-layer-1-surface-err focus:border-layer-1-surface-focus focus:border-layer-1-surface_a focus:border-layer-2-glyph focus:border-layer-2-glyph-hl focus:border-layer-2-glyph-hl_a focus:border-layer-2-glyph-shade focus:border-layer-2-glyph_a focus:border-layer-2-glyph_d focus:border-layer-2-glyph_pl focus:border-layer-2-surface focus:border-layer-2-surface-edge focus:border-layer-2-surface_a focus:border-radroots-accent-focus focus:text-layer-0-glyph focus:text-layer-0-glyph-hl focus:text-layer-0-glyph-hl_a focus:text-layer-0-glyph-label focus:text-layer-0-glyph-shade focus:text-layer-0-glyph_a focus:text-layer-0-glyph_pl focus:text-layer-0-surface focus:text-layer-0-surface-blur focus:text-layer-0-surface-edge focus:text-layer-0-surface_a focus:text-layer-0-surface_w focus:text-layer-1-glyph focus:text-layer-1-glyph-hl focus:text-layer-1-glyph-hl_a focus:text-layer-1-glyph-label focus:text-layer-1-glyph-shade focus:text-layer-1-glyph_a focus:text-layer-1-glyph_d focus:text-layer-1-glyph_pl focus:text-layer-1-surface focus:text-layer-1-surface-edge focus:text-layer-1-surface-err focus:text-layer-1-surface-focus focus:text-layer-1-surface_a focus:text-layer-2-glyph focus:text-layer-2-glyph-hl focus:text-layer-2-glyph-hl_a focus:text-layer-2-glyph-shade focus:text-layer-2-glyph_a focus:text-layer-2-glyph_d focus:text-layer-2-glyph_pl focus:text-layer-2-surface focus:text-layer-2-surface-edge focus:text-layer-2-surface_a focus:text-radroots-accent-focus group-active:bg-layer-0-glyph group-active:bg-layer-0-glyph-hl group-active:bg-layer-0-glyph-hl_a group-active:bg-layer-0-glyph-label group-active:bg-layer-0-glyph-shade group-active:bg-layer-0-glyph_a group-active:bg-layer-0-glyph_pl group-active:bg-layer-0-surface group-active:bg-layer-0-surface-blur group-active:bg-layer-0-surface-edge group-active:bg-layer-0-surface_a group-active:bg-layer-0-surface_w group-active:bg-layer-1-glyph group-active:bg-layer-1-glyph-hl group-active:bg-layer-1-glyph-hl_a group-active:bg-layer-1-glyph-label group-active:bg-layer-1-glyph-shade group-active:bg-layer-1-glyph_a group-active:bg-layer-1-glyph_d group-active:bg-layer-1-glyph_pl group-active:bg-layer-1-surface group-active:bg-layer-1-surface-edge group-active:bg-layer-1-surface-err group-active:bg-layer-1-surface-focus group-active:bg-layer-1-surface_a group-active:bg-layer-2-glyph group-active:bg-layer-2-glyph-hl group-active:bg-layer-2-glyph-hl_a group-active:bg-layer-2-glyph-shade group-active:bg-layer-2-glyph_a group-active:bg-layer-2-glyph_d group-active:bg-layer-2-glyph_pl group-active:bg-layer-2-surface group-active:bg-layer-2-surface-edge group-active:bg-layer-2-surface_a group-active:bg-radroots-accent-focus group-active:border-layer-0-glyph group-active:border-layer-0-glyph-hl group-active:border-layer-0-glyph-hl_a group-active:border-layer-0-glyph-label group-active:border-layer-0-glyph-shade group-active:border-layer-0-glyph_a group-active:border-layer-0-glyph_pl group-active:border-layer-0-surface group-active:border-layer-0-surface-blur group-active:border-layer-0-surface-edge group-active:border-layer-0-surface_a group-active:border-layer-0-surface_w group-active:border-layer-1-glyph group-active:border-layer-1-glyph-hl group-active:border-layer-1-glyph-hl_a group-active:border-layer-1-glyph-label group-active:border-layer-1-glyph-shade group-active:border-layer-1-glyph_a group-active:border-layer-1-glyph_d group-active:border-layer-1-glyph_pl group-active:border-layer-1-surface group-active:border-layer-1-surface-edge group-active:border-layer-1-surface-err group-active:border-layer-1-surface-focus group-active:border-layer-1-surface_a group-active:border-layer-2-glyph group-active:border-layer-2-glyph-hl group-active:border-layer-2-glyph-hl_a group-active:border-layer-2-glyph-shade group-active:border-layer-2-glyph_a group-active:border-layer-2-glyph_d group-active:border-layer-2-glyph_pl group-active:border-layer-2-surface group-active:border-layer-2-surface-edge group-active:border-layer-2-surface_a group-active:border-radroots-accent-focus group-active:text-layer-0-glyph group-active:text-layer-0-glyph-hl group-active:text-layer-0-glyph-hl_a group-active:text-layer-0-glyph-label group-active:text-layer-0-glyph-shade group-active:text-layer-0-glyph_a group-active:text-layer-0-glyph_pl group-active:text-layer-0-surface group-active:text-layer-0-surface-blur group-active:text-layer-0-surface-edge group-active:text-layer-0-surface_a group-active:text-layer-0-surface_w group-active:text-layer-1-glyph group-active:text-layer-1-glyph-hl group-active:text-layer-1-glyph-hl_a group-active:text-layer-1-glyph-label group-active:text-layer-1-glyph-shade group-active:text-layer-1-glyph_a group-active:text-layer-1-glyph_d group-active:text-layer-1-glyph_pl group-active:text-layer-1-surface group-active:text-layer-1-surface-edge group-active:text-layer-1-surface-err group-active:text-layer-1-surface-focus group-active:text-layer-1-surface_a group-active:text-layer-2-glyph group-active:text-layer-2-glyph-hl group-active:text-layer-2-glyph-hl_a group-active:text-layer-2-glyph-shade group-active:text-layer-2-glyph_a group-active:text-layer-2-glyph_d group-active:text-layer-2-glyph_pl group-active:text-layer-2-surface group-active:text-layer-2-surface-edge group-active:text-layer-2-surface_a group-active:text-radroots-accent-focus group-focus:bg-layer-0-glyph group-focus:bg-layer-0-glyph-hl group-focus:bg-layer-0-glyph-hl_a group-focus:bg-layer-0-glyph-label group-focus:bg-layer-0-glyph-shade group-focus:bg-layer-0-glyph_a group-focus:bg-layer-0-glyph_pl group-focus:bg-layer-0-surface group-focus:bg-layer-0-surface-blur group-focus:bg-layer-0-surface-edge group-focus:bg-layer-0-surface_a group-focus:bg-layer-0-surface_w group-focus:bg-layer-1-glyph group-focus:bg-layer-1-glyph-hl group-focus:bg-layer-1-glyph-hl_a group-focus:bg-layer-1-glyph-label group-focus:bg-layer-1-glyph-shade group-focus:bg-layer-1-glyph_a group-focus:bg-layer-1-glyph_d group-focus:bg-layer-1-glyph_pl group-focus:bg-layer-1-surface group-focus:bg-layer-1-surface-edge group-focus:bg-layer-1-surface-err group-focus:bg-layer-1-surface-focus group-focus:bg-layer-1-surface_a group-focus:bg-layer-2-glyph group-focus:bg-layer-2-glyph-hl group-focus:bg-layer-2-glyph-hl_a group-focus:bg-layer-2-glyph-shade group-focus:bg-layer-2-glyph_a group-focus:bg-layer-2-glyph_d group-focus:bg-layer-2-glyph_pl group-focus:bg-layer-2-surface group-focus:bg-layer-2-surface-edge group-focus:bg-layer-2-surface_a group-focus:bg-radroots-accent-focus group-focus:border-layer-0-glyph group-focus:border-layer-0-glyph-hl group-focus:border-layer-0-glyph-hl_a group-focus:border-layer-0-glyph-label group-focus:border-layer-0-glyph-shade group-focus:border-layer-0-glyph_a group-focus:border-layer-0-glyph_pl group-focus:border-layer-0-surface group-focus:border-layer-0-surface-blur group-focus:border-layer-0-surface-edge group-focus:border-layer-0-surface_a group-focus:border-layer-0-surface_w group-focus:border-layer-1-glyph group-focus:border-layer-1-glyph-hl group-focus:border-layer-1-glyph-hl_a group-focus:border-layer-1-glyph-label group-focus:border-layer-1-glyph-shade group-focus:border-layer-1-glyph_a group-focus:border-layer-1-glyph_d group-focus:border-layer-1-glyph_pl group-focus:border-layer-1-surface group-focus:border-layer-1-surface-edge group-focus:border-layer-1-surface-err group-focus:border-layer-1-surface-focus group-focus:border-layer-1-surface_a group-focus:border-layer-2-glyph group-focus:border-layer-2-glyph-hl group-focus:border-layer-2-glyph-hl_a group-focus:border-layer-2-glyph-shade group-focus:border-layer-2-glyph_a group-focus:border-layer-2-glyph_d group-focus:border-layer-2-glyph_pl group-focus:border-layer-2-surface group-focus:border-layer-2-surface-edge group-focus:border-layer-2-surface_a group-focus:border-radroots-accent-focus group-focus:text-layer-0-glyph group-focus:text-layer-0-glyph-hl group-focus:text-layer-0-glyph-hl_a group-focus:text-layer-0-glyph-label group-focus:text-layer-0-glyph-shade group-focus:text-layer-0-glyph_a group-focus:text-layer-0-glyph_pl group-focus:text-layer-0-surface group-focus:text-layer-0-surface-blur group-focus:text-layer-0-surface-edge group-focus:text-layer-0-surface_a group-focus:text-layer-0-surface_w group-focus:text-layer-1-glyph group-focus:text-layer-1-glyph-hl group-focus:text-layer-1-glyph-hl_a group-focus:text-layer-1-glyph-label group-focus:text-layer-1-glyph-shade group-focus:text-layer-1-glyph_a group-focus:text-layer-1-glyph_d group-focus:text-layer-1-glyph_pl group-focus:text-layer-1-surface group-focus:text-layer-1-surface-edge group-focus:text-layer-1-surface-err group-focus:text-layer-1-surface-focus group-focus:text-layer-1-surface_a group-focus:text-layer-2-glyph group-focus:text-layer-2-glyph-hl group-focus:text-layer-2-glyph-hl_a group-focus:text-layer-2-glyph-shade group-focus:text-layer-2-glyph_a group-focus:text-layer-2-glyph_d group-focus:text-layer-2-glyph_pl group-focus:text-layer-2-surface group-focus:text-layer-2-surface-edge group-focus:text-layer-2-surface_a group-focus:text-radroots-accent-focus h-[12px] h-[16px] h-[17px] h-[18px] h-[20px] h-[22px] h-[24px] h-[28px] h-[36px] h-lo_bottom_button_ios0 h-lo_bottom_button_ios1 h-lo_bottom_button_webm0 h-lo_bottom_button_webm1 h-nav_page_header_ios0 h-nav_page_header_ios1 h-nav_page_header_webm0 h-nav_page_header_webm1 h-nav_page_toolbar_ios0 h-nav_page_toolbar_ios1 h-nav_page_toolbar_webm0 h-nav_page_toolbar_webm1 h-nav_tabs_ios0 h-nav_tabs_ios1 h-nav_tabs_webm0 h-nav_tabs_webm1 left-lo_ios0 left-lo_ios1 left-lo_textdesc_ios0 left-lo_textdesc_ios1 left-lo_textdesc_webm0 left-lo_textdesc_webm1 left-lo_webm0 left-lo_webm1 max-h-lo_bottom_button_ios0 max-h-lo_bottom_button_ios1 max-h-lo_bottom_button_webm0 max-h-lo_bottom_button_webm1 max-h-nav_page_header_ios0 max-h-nav_page_header_ios1 max-h-nav_page_header_webm0 max-h-nav_page_header_webm1 max-h-nav_page_toolbar_ios0 max-h-nav_page_toolbar_ios1 max-h-nav_page_toolbar_webm0 max-h-nav_page_toolbar_webm1 max-h-nav_tabs_ios0 max-h-nav_tabs_ios1 max-h-nav_tabs_webm0 max-h-nav_tabs_webm1 max-w-lo_ios0 max-w-lo_ios1 max-w-lo_textdesc_ios0 max-w-lo_textdesc_ios1 max-w-lo_textdesc_webm0 max-w-lo_textdesc_webm1 max-w-lo_webm0 max-w-lo_webm1 min-h-lo_bottom_button_ios0 min-h-lo_bottom_button_ios1 min-h-lo_bottom_button_webm0 min-h-lo_bottom_button_webm1 min-h-nav_page_header_ios0 min-h-nav_page_header_ios1 min-h-nav_page_header_webm0 min-h-nav_page_header_webm1 min-h-nav_page_toolbar_ios0 min-h-nav_page_toolbar_ios1 min-h-nav_page_toolbar_webm0 min-h-nav_page_toolbar_webm1 min-h-nav_tabs_ios0 min-h-nav_tabs_ios1 min-h-nav_tabs_webm0 min-h-nav_tabs_webm1 min-w-lo_ios0 min-w-lo_ios1 min-w-lo_textdesc_ios0 min-w-lo_textdesc_ios1 min-w-lo_textdesc_webm0 min-w-lo_textdesc_webm1 min-w-lo_webm0 min-w-lo_webm1 pb-h_lo_bottom_button_ios0 pb-h_lo_bottom_button_ios1 pb-h_lo_bottom_button_webm0 pb-h_lo_bottom_button_webm1 pb-h_nav_page_header_ios0 pb-h_nav_page_header_ios1 pb-h_nav_page_header_webm0 pb-h_nav_page_header_webm1 pb-h_nav_page_toolbar_ios0 pb-h_nav_page_toolbar_ios1 pb-h_nav_page_toolbar_webm0 pb-h_nav_page_toolbar_webm1 pb-h_nav_tabs_ios0 pb-h_nav_tabs_ios1 pb-h_nav_tabs_webm0 pb-h_nav_tabs_webm1 pl-w_lo_ios0 pl-w_lo_ios1 pl-w_lo_textdesc_ios0 pl-w_lo_textdesc_ios1 pl-w_lo_textdesc_webm0 pl-w_lo_textdesc_webm1 pl-w_lo_webm0 pl-w_lo_webm1 pr-w_lo_ios0 pr-w_lo_ios1 pr-w_lo_textdesc_ios0 pr-w_lo_textdesc_ios1 pr-w_lo_textdesc_webm0 pr-w_lo_textdesc_webm1 pr-w_lo_webm0 pr-w_lo_webm1 pt-h_lo_bottom_button_ios0 pt-h_lo_bottom_button_ios1 pt-h_lo_bottom_button_webm0 pt-h_lo_bottom_button_webm1 pt-h_nav_page_header_ios0 pt-h_nav_page_header_ios1 pt-h_nav_page_header_webm0 pt-h_nav_page_header_webm1 pt-h_nav_page_toolbar_ios0 pt-h_nav_page_toolbar_ios1 pt-h_nav_page_toolbar_webm0 pt-h_nav_page_toolbar_webm1 pt-h_nav_tabs_ios0 pt-h_nav_tabs_ios1 pt-h_nav_tabs_webm0 pt-h_nav_tabs_webm1 right-lo_ios0 right-lo_ios1 right-lo_textdesc_ios0 right-lo_textdesc_ios1 right-lo_textdesc_webm0 right-lo_textdesc_webm1 right-lo_webm0 right-lo_webm1 text-[12px] text-[15px] text-[16px] text-[18px] text-[19px] text-[20px] text-[21px] text-[23px] text-[24px] text-[26px] text-[27px] text-[28px] text-[30px] text-[36px] text-[40px] text-layer-0-glyph text-layer-0-glyph-hl text-layer-0-glyph-hl_a text-layer-0-glyph-label text-layer-0-glyph-shade text-layer-0-glyph_a text-layer-0-glyph_pl text-layer-0-surface text-layer-0-surface-blur text-layer-0-surface-edge text-layer-0-surface_a text-layer-0-surface_w text-layer-1-glyph text-layer-1-glyph-hl text-layer-1-glyph-hl_a text-layer-1-glyph-label text-layer-1-glyph-shade text-layer-1-glyph_a text-layer-1-glyph_d text-layer-1-glyph_pl text-layer-1-surface text-layer-1-surface-edge text-layer-1-surface-err text-layer-1-surface-focus text-layer-1-surface_a text-layer-2-glyph text-layer-2-glyph-hl text-layer-2-glyph-hl_a text-layer-2-glyph-shade text-layer-2-glyph_a text-layer-2-glyph_d text-layer-2-glyph_pl text-layer-2-surface text-layer-2-surface-edge text-layer-2-surface_a text-radroots-accent-focus top-lo_bottom_button_ios0 top-lo_bottom_button_ios1 top-lo_bottom_button_webm0 top-lo_bottom_button_webm1 top-nav_page_header_ios0 top-nav_page_header_ios1 top-nav_page_header_webm0 top-nav_page_header_webm1 top-nav_page_toolbar_ios0 top-nav_page_toolbar_ios1 top-nav_page_toolbar_webm0 top-nav_page_toolbar_webm1 top-nav_tabs_ios0 top-nav_tabs_ios1 top-nav_tabs_webm0 top-nav_tabs_webm1 translate-x-w_lo_ios0 translate-x-w_lo_ios1 translate-x-w_lo_textdesc_ios0 translate-x-w_lo_textdesc_ios1 translate-x-w_lo_textdesc_webm0 translate-x-w_lo_textdesc_webm1 translate-x-w_lo_webm0 translate-x-w_lo_webm1 translate-y-h_lo_bottom_button_ios0 translate-y-h_lo_bottom_button_ios1 translate-y-h_lo_bottom_button_webm0 translate-y-h_lo_bottom_button_webm1 translate-y-h_nav_page_header_ios0 translate-y-h_nav_page_header_ios1 translate-y-h_nav_page_header_webm0 translate-y-h_nav_page_header_webm1 translate-y-h_nav_page_toolbar_ios0 translate-y-h_nav_page_toolbar_ios1 translate-y-h_nav_page_toolbar_webm0 translate-y-h_nav_page_toolbar_webm1 translate-y-h_nav_tabs_ios0 translate-y-h_nav_tabs_ios1 translate-y-h_nav_tabs_webm0 translate-y-h_nav_tabs_webm1 w-[12px] w-[16px] w-[17px] w-[18px] w-[20px] w-[22px] w-[24px] w-[28px] w-[36px] w-lo_ios0 w-lo_ios1 w-lo_textdesc_ios0 w-lo_textdesc_ios1 w-lo_textdesc_webm0 w-lo_textdesc_webm1 w-lo_webm0 w-lo_webm1"></div> +\ No newline at end of file diff --git a/apps-lib/src/lib/component/lib/empty.svelte b/apps-lib/src/lib/components/lib/empty.svelte diff --git a/apps-lib/src/lib/components/lib/fade.svelte b/apps-lib/src/lib/components/lib/fade.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import { fmt_cl, type IBasisOpt, type IClOpt } from "@radroots/util"; + import type { Snippet } from "svelte"; + import { fade, type FadeParams } from "svelte/transition"; + + let { + basis = undefined, + children, + }: { + basis?: IBasisOpt<IClOpt> & { in?: FadeParams; out?: FadeParams }; + children: Snippet; + } = $props(); +</script> + +<div + in:fade={basis?.in || { duration: 200 }} + out:fade={basis?.out || { delay: 50, duration: 200 }} + class={`${fmt_cl(basis?.classes)} flex`} +> + {@render children()} +</div> diff --git a/apps-lib/src/lib/components/lib/image-blob.svelte b/apps-lib/src/lib/components/lib/image-blob.svelte @@ -0,0 +1,36 @@ +<script lang="ts"> + import { type IImageBlob } from "@radroots/util"; + import { onDestroy, onMount } from "svelte"; + + let { basis }: { basis: IImageBlob } = $props(); + + let img_url = $state(``); + + onMount(async () => { + try { + if (!basis.data) return; + img_url = URL.createObjectURL( + new Blob([basis.data], { + type: "image/jpeg", + }), + ); + } catch (e) { + console.log(`(error) image blob `, e); + } + }); + + onDestroy(async () => { + try { + URL.revokeObjectURL(img_url); + } catch (e) {} + }); +</script> + +{#if img_url} + <img + id={basis.id || null} + src={img_url} + alt={basis.alt || null} + style={`height: 100%; width: 100%; object-fit: cover; display: block;`} + /> +{/if} diff --git a/apps-lib/src/lib/components/lib/image-path.svelte b/apps-lib/src/lib/components/lib/image-path.svelte @@ -0,0 +1,32 @@ +<script lang="ts"> + import { fmt_cl, type IImagePath } from "@radroots/util"; + import { onMount } from "svelte"; + + let { basis }: { basis: IImagePath } = $props(); + + let img_src = $state(``); + + onMount(async () => { + try { + if (basis.path) img_src = basis.path; + } catch (e) { + } finally { + } + }); +</script> + +{#if img_src} + <!-- svelte-ignore a11y_click_events_have_key_events --> + <!-- svelte-ignore a11y_no_noninteractive_element_interactions --> + <img + id={basis?.id || null} + class={`${fmt_cl(basis?.classes)}`} + src={img_src} + alt={basis?.alt || null} + style={`height: 100%; width: 100%; object-fit: cover; display: block;`} + onclick={async (ev) => { + ev.stopPropagation(); + if (basis?.callback) await basis.callback(ev); + }} + /> +{/if} diff --git a/apps-lib/src/lib/components/lib/input-value.svelte b/apps-lib/src/lib/components/lib/input-value.svelte @@ -0,0 +1,81 @@ +<script lang="ts"> + import { + type IInputValue, + fmt_cl, + parse_layer, + value_constrain, + } from "@radroots/util"; + + let { + basis, + el = $bindable(null), + value = $bindable(``), + }: { + basis: IInputValue<string>; + el?: HTMLInputElement | null; + value?: string; + } = $props(); + + const layer = $derived( + typeof basis?.layer === `boolean` + ? parse_layer(0) + : parse_layer(basis?.layer), + ); + + const classes_layer = $derived( + typeof basis?.layer === `boolean` || typeof basis?.layer === `undefined` + ? `` + : `bg-layer-${layer}-surface text-layer-${layer}-glyph placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`, + ); + + const handle_on_input = async (el: HTMLInputElement): Promise<void> => { + try { + let pass = true; + let val = el?.value; + if (basis?.field && el) { + val = value_constrain(basis?.field.charset, val); + el.value = val; + if ( + !basis?.field.validate.test(val) && + basis?.field_constrain + ) { + //@todo set styles + } + pass = basis?.field.validate.test(val); + } + if (basis?.callback) await basis?.callback({ value: val, pass }); + } catch (e) { + console.log(`(error) handle_on_input `, e); + } + }; + + $effect(() => { + console.log(`value `, value); + }); +</script> + +<input + bind:this={el} + bind:value + disabled={!!basis.disabled} + oninput={async ({ currentTarget: el }) => { + await handle_on_input(el); + }} + onblur={async ({ currentTarget: el }) => { + if (basis.callback_blur) await basis.callback_blur({ el }); + }} + onfocus={async ({ currentTarget: el }) => { + if (basis.callback_focus) await basis.callback_focus({ el }); + }} + onkeydown={async (ev) => { + if (basis?.callback_keydown) + await basis?.callback_keydown({ + key: ev.key, + key_s: ev.key === `Enter`, + el: ev.currentTarget, + }); + }} + type="text" + class={`${fmt_cl(basis?.classes)} el-input ${classes_layer} el-re`} + placeholder={basis?.placeholder || ``} +/> diff --git a/apps-lib/src/lib/components/lib/input.svelte b/apps-lib/src/lib/components/lib/input.svelte @@ -0,0 +1,115 @@ +<script lang="ts"> + import { browser } from "$app/environment"; + import { idb } from "$root"; + import { + type IInput, + fmt_cl, + parse_layer, + value_constrain, + } from "@radroots/util"; + import { onMount } from "svelte"; + + let { + basis, + el = $bindable(null), + value = $bindable(``), + }: { + basis: IInput<string>; + el?: HTMLInputElement | null; + value?: string; + } = $props(); + + onMount(async () => { + try { + await kv_init(); + } catch (e) { + } finally { + } + }); + + const id = $derived(basis?.id ? basis.id : null); + + const layer = $derived( + typeof basis?.layer === `boolean` + ? parse_layer(0) + : parse_layer(basis?.layer), + ); + + const classes_layer = $derived( + typeof basis?.layer === `boolean` || typeof basis?.layer === `undefined` + ? `` + : `bg-layer-${layer}-surface text-layer-${layer}-glyph_d placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`, + ); + + $effect(() => { + if (id && basis?.sync) { + (async () => { + if (browser) await idb.set(id, value || ``); + })(); + } + }); + + const kv_init = async (): Promise<void> => { + try { + if (!id) return; + if (basis?.sync && browser) { + const kv_val = await idb.get(id); + if (kv_val && el) el.value = kv_val; + else await idb.set(id, ``); + } + if (basis?.callback_mount && el) + await basis?.callback_mount({ el }); + } catch (e) { + console.log(`(error) kv_init `, e); + } + }; + + const handle_on_input = async (el: HTMLInputElement): Promise<void> => { + try { + let pass = true; + let val = el?.value; + if (basis?.field && el) { + val = value_constrain(basis?.field.charset, val); + el.value = val; + if ( + !basis?.field.validate.test(val) && + basis?.field_constrain + ) { + //@todo set styles + } + pass = basis?.field.validate.test(val); + } + if (basis?.sync && id && browser) await idb.set(id, val); + if (basis?.callback) await basis?.callback({ value: val, pass }); + } catch (e) { + console.log(`(error) handle_on_input `, e); + } + }; +</script> + +<input + bind:this={el} + bind:value + disabled={!!basis.disabled} + oninput={async ({ currentTarget: el }) => { + await handle_on_input(el); + }} + onblur={async ({ currentTarget: el }) => { + if (basis.callback_blur) await basis.callback_blur({ el }); + }} + onfocus={async ({ currentTarget: el }) => { + if (basis.callback_focus) await basis.callback_focus({ el }); + }} + onkeydown={async (ev) => { + if (basis?.callback_keydown) + await basis?.callback_keydown({ + key: ev.key, + key_s: ev.key === `Enter`, + el: ev.currentTarget, + }); + }} + {id} + type="text" + class={`${fmt_cl(basis?.classes)} el-input ${classes_layer} el-re`} + placeholder={basis?.placeholder || ``} +/> diff --git a/apps-lib/src/lib/components/lib/label-swap.svelte b/apps-lib/src/lib/components/lib/label-swap.svelte @@ -0,0 +1,41 @@ +<script lang="ts"> + import { + fmt_cl, + parse_layer, + type ILabelSwap, + type ILyOpt, + } from "@radroots/util"; + + let { + basis, + el = $bindable(null), + }: { + basis: ILabelSwap & ILyOpt; + el?: HTMLLabelElement | null; + } = $props(); + + const layer = $derived(parse_layer(basis?.layer ? basis.layer : 1)); +</script> + +<div class={`flex flex-row justify-start items-center`}> + <!-- svelte-ignore a11y_label_has_associated_control --> + <label + bind:this={el} + class={`swap${basis.swap.toggle ? ` swap-active` : ``}`} + > + <div class="swap-on"> + <p + class={`${fmt_cl(basis.swap.on.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-60`)} font-sans -translate-y-[1px] el-re`} + > + {basis.swap.on.value} + </p> + </div> + <div class="swap-off"> + <p + class={`${fmt_cl(basis.swap.off.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-60`)} font-sans -translate-y-[1px] el-re`} + > + {basis.swap.off.value} + </p> + </div> + </label> +</div> diff --git a/apps-lib/src/lib/components/lib/load-symbol-white.svelte b/apps-lib/src/lib/components/lib/load-symbol-white.svelte @@ -0,0 +1,70 @@ +<script lang="ts"> + import { loading_style_map, type ILoadSymbol } from "@radroots/util"; + + let { + basis = undefined, + }: { + basis?: ILoadSymbol; + } = $props(); + + const styles = $derived( + basis?.dim + ? loading_style_map.get(basis?.dim) + : loading_style_map.get("sm"), + ); + + const num_blades = $derived(basis?.blades || 8); +</script> + +<div + class={`relative flex flex-row justify-center items-center h-[${styles?.dim_1}px] w-[${styles?.dim_1}px] fade-in el-re`} +> + <div + class={`${num_blades === 12 ? `spinner12-white center` : `spinner8-white center`} text-[${styles?.gl_2 || styles?.dim_1}px]`} + > + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + {#if num_blades === 12} + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade-white` : `spinner8-blade-white`}`} + ></div> + {/if} + </div> +</div> diff --git a/apps-lib/src/lib/components/lib/load-symbol.svelte b/apps-lib/src/lib/components/lib/load-symbol.svelte @@ -0,0 +1,70 @@ +<script lang="ts"> + import { type ILoadSymbol, loading_style_map } from "@radroots/util"; + + let { + basis = undefined, + }: { + basis?: ILoadSymbol; + } = $props(); + + const styles = $derived( + basis?.dim + ? loading_style_map.get(basis?.dim) + : loading_style_map.get("sm"), + ); + + const num_blades = $derived(basis?.blades || 8); +</script> + +<div + class={`relative flex flex-row justify-center items-center h-[${styles?.dim_1}px] w-[${styles?.dim_1}px] fade-in el-re`} +> + <div + class={`${num_blades === 12 ? `spinner12 center` : `spinner8 center`} text-[${styles?.gl_2 || styles?.dim_1}px]`} + > + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + {#if num_blades === 12} + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + <div + class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} + ></div> + {/if} + </div> +</div> diff --git a/apps-lib/src/lib/component/lib/logo-circle-sm.svelte b/apps-lib/src/lib/components/lib/logo-circle-sm.svelte diff --git a/apps-lib/src/lib/components/lib/logo-letters.svelte b/apps-lib/src/lib/components/lib/logo-letters.svelte @@ -0,0 +1,5 @@ +<p + class={`font-sansd italic font-[700] text-[1.7rem] text-layer-0-glyph lowercase`} +> + {`radroots`} +</p> diff --git a/apps-lib/src/lib/components/lib/logo_circle.svelte b/apps-lib/src/lib/components/lib/logo_circle.svelte @@ -0,0 +1,18 @@ +<div + class={`relative flex flex-col h-[196px] w-full justify-center items-center`} +> + <div + class={`relative flex flex-row h-36 w-36 justify-center items-center bg-layer-2-surface rounded-full`} + > + <p + class={`font-sans font-[900] text-6xl text-layer-0-glyph -tracking-[0.4rem] -translate-x-[6px]`} + > + {"»`,"} + </p> + <p + class={`font-sans font-[900] text-6xl text-layer-0-glyph translate-x-[8px]`} + > + {"-"} + </p> + </div> +</div> diff --git a/apps-lib/src/lib/components/lib/select-menu.svelte b/apps-lib/src/lib/components/lib/select-menu.svelte @@ -0,0 +1,79 @@ +<script lang="ts"> + import { fmt_cl, parse_layer, type ISelect } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis, + value = $bindable(``), + el_wrap = $bindable(null), + el_select = $bindable(null), + children, + }: { + basis: ISelect; + value: string; + el_wrap?: HTMLDivElement | null; + el_select?: HTMLSelectElement | null; + children?: Snippet; + } = $props(); + + const layer = $derived( + parse_layer(typeof basis?.layer === `boolean` ? basis.layer : 0), + ); + + const classes_layer = $derived( + typeof basis?.layer === `boolean` ? `` : `text-layer-${layer}-glyph`, + ); +</script> + +<div + class={`relative flex flex-row h-max w-auto justify-center items-center`} + bind:this={el_wrap} +> + <div + class={`${fmt_cl(basis.classes)} z-20 absolute top-0 left-0 flex flex-row h-full w-full justify-end items-center ${classes_layer}`} + > + <select + class={`select select-ghost h-full w-full bg-transparent focus:border-0 focus:outline-0 text-transparent focus:text-transparent`} + bind:this={el_select} + bind:value + onchange={async (e) => { + const opt = basis.options + .map((i) => i.entries) + .reduce((_, j) => j, []) + .find( + (k: { value: string }) => + k.value === e.currentTarget?.value, + ); + if (basis.callback && opt) await basis.callback(opt); + if (el_select) el_select.value = value; + }} + > + {#each basis.options as opt_g} + {#if opt_g.group} + <optgroup> + {#each opt_g.entries as opt} + <option + label={opt_g.group === true + ? `-`.repeat(21) + : opt_g.group || ``} + > + {opt.label} + </option> + {/each} + </optgroup> + {:else} + {#each opt_g.entries as opt} + <option value={opt.value} disabled={!!opt.disabled}> + {opt.label} + </option> + {/each} + {/if} + {/each} + </select> + </div> + {#if children} + <div class={`z-10 flex flex-row h-full w-full`}> + {@render children()} + </div> + {/if} +</div> diff --git a/apps-lib/src/lib/components/lib/select.svelte b/apps-lib/src/lib/components/lib/select.svelte @@ -0,0 +1,121 @@ +<script lang="ts"> + import { browser } from "$app/environment"; + import { Glyph, handle_err, idb } from "$root"; + import { fmt_cl, parse_layer, type ISelect } from "@radroots/util"; + import { onMount } from "svelte"; + + let { + basis, + value = $bindable(``), + el = $bindable(null), + }: { + basis: ISelect; + value: string; + el?: HTMLSelectElement | null; + } = $props(); + + const id = $derived(basis?.id ? basis.id : null); + + const layer = $derived( + typeof basis?.layer === `boolean` + ? parse_layer(0) + : parse_layer(basis.layer), + ); + + const classes_layer = $derived( + typeof basis?.layer === `boolean` + ? `` + : !value + ? `text-layer-${layer}-glyph/60` + : `text-layer-${layer}-glyph_d`, + ); + + onMount(async () => { + try { + if (id && basis?.sync_init && browser) { + const sync_val = await idb.get(id); + await idb.set(id, sync_val || ``); + } + } catch (e) { + handle_err(e, `on_mount`); + } + }); + + $effect(() => { + if (browser && id && basis?.sync) { + (async () => { + await idb.set(id, value); + })(); + } + }); + + const handle_on_change = async (el: HTMLSelectElement): Promise<void> => { + try { + const opt = basis.options + .map((i) => i.entries) + .reduce((_, j) => j, []) + .find((k) => k.value === el?.value); + if (el) el.value = value; + if (basis?.sync && id && browser) await idb.set(id, value); + if (basis.callback && opt) await basis.callback(opt); + } catch (e) { + console.log(`(error) handle_on_change `, e); + } + }; +</script> + +{#if basis?.show_arrows === "l"} + <div class={`flex flex-row pr-[2px] justify-center items-center`}> + <Glyph + basis={{ + key: `caret-up-down`, + dim: `xs`, + + classes: `text-layer-${layer}-glyph translate-y-[1px]`, + }} + /> + </div> +{/if} +<select + bind:this={el} + bind:value + onchange={async ({ currentTarget: el }) => { + handle_on_change(el); + }} + {id} + class={`${fmt_cl(basis.classes)} z-10 el-select ${classes_layer}`} +> + {#each basis.options as opt_g} + {#if opt_g.group} + <optgroup> + {#each opt_g.entries as opt} + <option + label={opt_g.group === true + ? `-`.repeat(21) + : opt_g.group || ``} + > + {opt.label} + </option> + {/each} + </optgroup> + {:else} + {#each opt_g.entries as opt} + <option value={opt.value} disabled={!!opt.disabled}> + {opt.label} + </option> + {/each} + {/if} + {/each} +</select> +{#if basis?.show_arrows === "r"} + <div class={`flex flex-row pl-[2px] justify-center items-center`}> + <Glyph + basis={{ + key: `caret-up-down`, + dim: `xs`, + + classes: `text-layer-${layer}-glyph`, + }} + /> + </div> +{/if} diff --git a/apps-lib/src/lib/components/lib/text-area.svelte b/apps-lib/src/lib/components/lib/text-area.svelte @@ -0,0 +1,96 @@ +<script lang="ts"> + import { browser } from "$app/environment"; + import { handle_err, idb } from "$root"; + import { + type ITextArea, + fmt_cl, + fmt_textarea_value, + parse_layer, + sleep, + value_constrain_textarea, + } from "@radroots/util"; + import { onMount } from "svelte"; + + let { + basis, + value = $bindable(``), + el = $bindable(null), + }: { + basis: ITextArea; + value?: string; + el?: HTMLTextAreaElement | null; + } = $props(); + + const id = $derived(basis?.id ? basis.id : null); + const layer = $derived( + typeof basis.layer === `boolean` ? 0 : parse_layer(basis.layer, 1), + ); + + onMount(async () => { + try { + if (id && basis?.sync && browser) { + const sync_val = await idb.get(id); + await idb.set(id, sync_val || ``); + } + } catch (e) { + handle_err(e, `on_mount`); + } + }); + + $effect(() => { + if (browser && id && basis?.sync && value) { + (async () => { + await sleep(100); + await idb.set(id, value); + })(); + } + }); + + const handle_on_input = async (el: HTMLTextAreaElement): Promise<void> => { + try { + let pass = true; + let val = el?.value; + if (basis?.field && el) { + val = value_constrain_textarea(basis?.field.charset, val); + el.value = fmt_textarea_value(val); + if ( + !basis?.field.validate.test(val) && + basis?.field_constrain + ) { + //@todo set styles + } + pass = basis?.field.validate.test(val); + } + if (id && basis?.sync && browser) await idb.set(id, val); + if (basis?.callback) await basis?.callback({ value: val, pass }); + } catch (e) { + console.log(`(error) handle_on_input `, e); + } + }; +</script> + +<textarea + bind:this={el} + bind:value + oninput={async ({ currentTarget: el }) => { + await handle_on_input(el); + }} + onblur={async ({ currentTarget: el }) => { + if (basis.callback_blur) await basis.callback_blur({ el }); + }} + onfocus={async ({ currentTarget: el }) => { + if (basis.callback_focus) await basis.callback_focus({ el }); + }} + onkeydown={async (ev) => { + if (basis.callback_keydown) + await basis.callback_keydown({ + key: ev.key, + key_s: ev.key === `Enter`, + el: ev.currentTarget, + }); + }} + {id} + contenteditable="true" + class={`${fmt_cl(basis.classes)} el-textarea p-2 w-full bg-layer-${layer}-surface text-layer-${layer}-glyph_d placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`} + placeholder={basis.placeholder || ``} +></textarea> diff --git a/apps-lib/src/lib/components/lib/theme.svelte b/apps-lib/src/lib/components/lib/theme.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import { browser } from "$app/environment"; + import { app_th, app_thc, theme_set } from "$root"; + import { parse_color_mode, parse_theme_key } from "@radroots/theme"; + + app_thc.subscribe( + (_app_thc) => + browser && + theme_set(parse_theme_key($app_thc), parse_color_mode(_app_thc)), + ); + + app_th.subscribe( + (_app_th) => + browser && + theme_set(parse_theme_key(_app_th), parse_color_mode($app_thc)), + ); +</script> diff --git a/apps-lib/src/lib/components/lib/toast.svelte b/apps-lib/src/lib/components/lib/toast.svelte @@ -0,0 +1,54 @@ +<script lang="ts"> + import { app_lo } from "$root"; + import { + fmt_cl, + get_layout, + parse_layer, + toast_layout_map, + toast_style_map, + type IToast, + type IToastKind, + } from "@radroots/util"; + + let { basis }: { basis: IToast } = $props(); + + const styles: IToastKind[] = $derived( + basis?.styles ? basis.styles : [`simple`], + ); + const layout = $derived(get_layout($app_lo)); + const layer = $derived(basis.layer ? parse_layer(basis.layer) : 1); +</script> + +<div + class={`${fmt_cl(toast_layout_map.get(layout))} z-[1000] h-[100vh] toast w-full ${basis.position || `top-center`} `} +> + <div class={`flex flex-row w-full h-max justify-center pb-2`}> + <div + class={`${fmt_cl(basis.classes)} relative grid grid-cols-12 h-max items-center justify-center ${styles.includes(`simple`) ? `bg-layer-${layer}-surface` : ``} ${fmt_cl(styles.map((style) => fmt_cl(toast_style_map.get(style)?.outer)).join(` `))}`} + > + <div + class={`absolute top-0 left-4 flex flex-row h-full items-center text-layer-${layer}-glyph`} + > + <glyph + basis={{ + key: `info`, + weight: `regular`, + dim: `md`, + ...basis.glyph, + }} + ></glyph> + </div> + <div + class={`col-span-12 flex flex-row pl-1 ${fmt_cl(styles.map((style) => fmt_cl(toast_style_map.get(style)?.inner)).join(` `))}`} + > + {#if `value` in basis.label} + <p + class={`font-sans font-[500] truncate text-layer-${layer}-glyph -translate-y-[1px]`} + > + {basis.label.value} + </p> + {/if} + </div> + </div> + </div> +</div> diff --git a/apps-lib/src/lib/components/lib/wrap-border.svelte b/apps-lib/src/lib/components/lib/wrap-border.svelte @@ -0,0 +1,22 @@ +<script lang="ts"> + import { fmt_cl, type IClOpt } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis, + children, + }: { + basis: IClOpt; + children: Snippet; + } = $props(); +</script> + +<div + class={`${fmt_cl(basis.classes)} relative flex flex-col w-full py-[4px] px-[4px] justify-start items-center rounded-[32px] overflow-hidden bg-white shadow-sm`} +> + <div + class={`flex flex-row h-full w-full justify-center items-center bg-white/30 overflow-hidden rounded-[28px]`} + > + {@render children()} + </div> +</div> diff --git a/apps-lib/src/lib/components/map/map-marker-area-display.svelte b/apps-lib/src/lib/components/map/map-marker-area-display.svelte @@ -0,0 +1,58 @@ +<script lang="ts"> + import { Fade, Glyph } from "$root"; + import type { IBasisOpt } from "@radroots/util"; + + let { + basis = undefined, + }: { + basis?: IBasisOpt<{ + primary: string; + admin: string; + country: string; + }>; + } = $props(); +</script> + +{#if basis} + <Fade + basis={{ + classes: `flex-col w-full justify-center items-start`, + }} + > + <div + class={`flex flex-col w-fit px-5 py-[10px] justify-start items-start bg-layer-1-surface rounded-3xl shadow-lg`} + > + <div class={`flex flex-col w-full gap-1 justify-start items-start`}> + <div class={`flex flex-row gap-1 justify-start items-center`}> + <p + class={`font-sans font-[600] text-[0.95rem] text-layer-2-glyph`} + > + {basis.primary} + </p> + <Glyph + basis={{ + classes: `text-layer-2-glyph -translate-y-[2px]`, + dim: `xs`, + + key: `map-pin-simple`, + }} + /> + </div> + <div + class={`flex flex-row w-full gap-1 justify-start items-center`} + > + <p + class={`font-sans font-[600] text-[0.95rem] tracking-tight text-layer-2-glyph`} + > + {`${basis.admin},`} + </p> + <p + class={`font-sans font-[600] text-[0.95rem] tracking-tight text-layer-2-glyph`} + > + {`${basis.country}`} + </p> + </div> + </div> + </div> + </Fade> +{/if} diff --git a/apps-lib/src/lib/components/map/map-marker-area.svelte b/apps-lib/src/lib/components/map/map-marker-area.svelte @@ -0,0 +1,44 @@ +<script lang="ts"> + import { MapMarkerAreaDisplay } from "$root"; + import { Marker, Popup } from "@radroots/svelte-maplibre"; + import { + type GeocoderReverseResult, + type GeolocationPoint, + type IMapMarkerArea, + } from "@radroots/util"; + + let { + basis, + map_geop = $bindable(), + map_geoc = $bindable(undefined), + }: { + basis: IMapMarkerArea; + map_geop: GeolocationPoint; + map_geoc?: GeocoderReverseResult | undefined; + } = $props(); +</script> + +<Marker + bind:lngLat={map_geop} + draggable={!basis.no_drag} + class={`flex flex-row h-[100px] w-[100px] bg-blue-400/20 border-[2px] border-white justify-center items-center rounded-full shadow-lg`} + ondragend={async () => { + if (!map_geop) return; + const geoc = await basis.lc_geocode(map_geop); + if (geoc) map_geoc = geoc; + }} +> + {#if basis.show_display} + <Popup open={basis.show_display} offset={[0, -55]}> + <MapMarkerAreaDisplay + basis={map_geoc + ? { + primary: map_geoc.name, + admin: map_geoc.admin1_name, + country: map_geoc.country_name, + } + : undefined} + /> + </Popup> + {/if} +</Marker> diff --git a/apps-lib/src/lib/components/map/map.svelte b/apps-lib/src/lib/components/map/map.svelte @@ -0,0 +1,42 @@ +<script lang="ts"> + import { app_thc } from "$lib/store/app"; + import { MapLibre } from "@radroots/svelte-maplibre"; + import { cfg_map, fmt_cl, type IClOpt } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis = undefined, + map = $bindable(undefined), + children, + }: { + basis?: IClOpt & { + interactive?: boolean; + zoom_click_off?: boolean; + }; + map?: maplibregl.Map; + interactive?: boolean; + children: Snippet; + } = $props(); + + const interactive = $derived( + typeof basis?.interactive === `boolean` ? basis?.interactive : true, + ); + + const zoomOnDoubleClick = $derived( + typeof basis?.zoom_click_off === `boolean` + ? basis?.zoom_click_off + : true, + ); +</script> + +<MapLibre + bind:map + class="{fmt_cl(basis?.classes)} relative h-full w-full" + zoom={10} + style={cfg_map.styles.base[$app_thc]} + attributionControl={false} + {interactive} + {zoomOnDoubleClick} +> + {@render children()} +</MapLibre> diff --git a/apps-lib/src/lib/components/marker/marker-indexed-view.svelte b/apps-lib/src/lib/components/marker/marker-indexed-view.svelte @@ -0,0 +1,31 @@ +<script lang="ts"> + import { Empty } from "$root"; + import { type CallbackPromiseGeneric } from "@radroots/util"; + + let { + basis, + }: { + basis: { + index_curr: number; + index_max: number; + callback_index: CallbackPromiseGeneric<number>; + }; + } = $props(); +</script> + +<div class={`flex flex-row w-full justify-start items-center`}> + <div + class={`flex flex-row h-[5px] w-full gap-[6px] justify-start items-center`} + > + {#each Array(basis.index_max).fill(0) as _, index} + <button + class={`flex flex-row flex-grow h-full justify-start items-center ${basis.index_curr >= index ? `bg-th-black` : `bg-layer-2-surface`} rounded-full el-re`} + onclick={async () => { + await basis.callback_index(index); + }} + > + <Empty /> + </button> + {/each} + </div> +</div> diff --git a/apps-lib/src/lib/components/navigation/navigation-tabs.svelte b/apps-lib/src/lib/components/navigation/navigation-tabs.svelte @@ -0,0 +1,99 @@ +<script lang="ts"> + import { goto } from "$app/navigation"; + import { page } from "$app/state"; + import { app_lo, Empty, Glyph } from "$root"; +</script> + +<div + class={`fixed bottom-0 left-0 h-nav_tabs_${$app_lo} flex flex-row w-full pt-2 justify-center items-start`} +> + <div class={`flex flex-row justify-between gap-10 items-center`}> + <div + class={`grid grid-cols-4 flex flex-row h-[3.1rem] px-6 gap-6 justify-start items-center bg-layer-1-surface rounded-full backdrop-blur-lg`} + > + <button + class={`col-span-1 flex flex-row justify-center items-center`} + onclick={async () => { + await goto(`/`); + }} + > + <Glyph + basis={{ + classes: `text-[26px] text-layer-0-glyph/80 rotate-90`, + key: `columns`, + weight: page.url.pathname === `/` ? `fill` : `bold`, + }} + /> + </button> + <button + class={`relative col-span-1 flex flex-row justify-center items-center`} + onclick={async () => { + await goto(`/search`); + }} + > + <Glyph + basis={{ + classes: `text-[24px] text-layer-0-glyph/80`, + key: `magnifying-glass`, + weight: page.url.pathname.includes(`search`) + ? `fill` + : `bold`, + }} + /> + </button> + <button + class={`relative col-span-1 flex flex-row justify-center items-center`} + onclick={async () => { + goto(`/profile`); + }} + > + <Glyph + basis={{ + classes: `text-[24px] text-layer-0-glyph/80`, + key: `user`, + weight: page.url.pathname.includes(`profile`) + ? `fill` + : `bold`, + }} + /> + </button> + <button + class={`relative col-span-1 flex flex-row h-full justify-center items-center`} + onclick={async () => { + await goto(`/notifications`); + }} + > + <Glyph + basis={{ + classes: `text-[24px] text-layer-0-glyph/80`, + key: `bell`, + weight: page.url.pathname.includes(`notifications`) + ? `fill` + : `bold`, + }} + /> + <div + class={`absolute top-2 -right-1 flex flex-row justify-start items-center`} + > + <div + class={`flex flex-row h-2 w-2 justify-start items-center bg-yellow-400 rounded-full`} + > + <Empty /> + </div> + </div> + </button> + </div> + <button + class={`flex flex-row h-[3.1rem] w-[3.1rem] justify-center items-center bg-layer-1-surface rounded-full backdrop-blur-lg`} + onclick={async () => {}} + > + <Glyph + basis={{ + classes: `text-[22px] text-layer-0-glyph/80`, + + key: `plus`, + }} + /> + </button> + </div> +</div> diff --git a/apps-lib/src/lib/components/navigation/page-header.svelte b/apps-lib/src/lib/components/navigation/page-header.svelte @@ -0,0 +1,54 @@ +<script lang="ts"> + import { app_lo, callback_route, Empty, ph_blur } from "$root"; + import type { IPageHeader } from "@radroots/util"; + import type { Snippet } from "svelte"; + import { fade } from "svelte/transition"; + + let { + basis, + children, + }: { + basis: IPageHeader<string>; + children?: Snippet; + } = $props(); +</script> + +{#if $ph_blur} + <div + in:fade={{ duration: 50 }} + out:fade={{ delay: 50, duration: 200 }} + class={`z-20 fixed top-0 left-0 flex flex-row h-nav_page_header_${$app_lo} w-full justify-center items-center bg-layer-0-surface-blur/30 backdrop-blur-lg`} + > + <Empty /> + </div> +{/if} +<div + class={`z-20 sticky top-0 flex flex-row min-h-nav_page_header_${$app_lo} h-nav_page_header_${$app_lo} w-full px-6 justify-between items-center`} +> + <div class={`flex flex-row justify-start items-center`}> + <button + class={`flex flex-row justify-center items-center`} + onclick={async () => { + if (basis.callback_route) + await callback_route(basis.callback_route); + }} + > + <p + class={`font-sansd font-[700] text-2xl text-layer-0-glyph capitalize max-w-lo_${$app_lo} truncate`} + > + {basis.label || ``} + </p> + </button> + </div> + {#if children} + {#if !$ph_blur} + <div + in:fade={{ duration: 50 }} + out:fade={{ delay: 50, duration: 200 }} + class={`flex flex-row justify-center items-center`} + > + {@render children()} + </div> + {/if} + {/if} +</div> diff --git a/apps-lib/src/lib/components/navigation/page-toolbar.svelte b/apps-lib/src/lib/components/navigation/page-toolbar.svelte @@ -0,0 +1,52 @@ +<script lang="ts"> + import { goto } from "$app/navigation"; + import { app_lo, Glyph, LogoCircleSm, PageHeader } from "$root"; + import type { IBasisOpt, IPageToolbar } from "@radroots/util"; + import type { Snippet } from "svelte"; + import LogoLetters from "../lib/logo-letters.svelte"; + + let { + basis = undefined, + header_option, + }: { + basis?: IBasisOpt<IPageToolbar<string>>; + header_option?: Snippet; + } = $props(); +</script> + +<div + class={`flex flex-row min-h-nav_page_toolbar_${$app_lo} h-nav_page_toolbar_${$app_lo} w-full px-6 justify-between items-center`} +> + <button + class={`flex flex-row gap-2 justify-start items-center`} + onclick={async () => { + if (basis?.callback) await basis.callback(); + else await goto(`/`); + }} + > + <LogoCircleSm /> + <LogoLetters /> + </button> + <button + class={`flex flex-row justify-center items-center`} + onclick={async () => { + await goto(`/settings`); + }} + > + <Glyph + basis={{ + classes: `text-layer-0-glyph`, + dim: `lg`, + + key: `gear`, + }} + /> + </button> +</div> +{#if basis?.header} + <PageHeader basis={basis.header}> + {#if header_option} + {@render header_option()} + {/if} + </PageHeader> +{/if} diff --git a/apps-lib/src/lib/components/trellis/trellis-default-label.svelte b/apps-lib/src/lib/components/trellis/trellis-default-label.svelte @@ -0,0 +1,36 @@ +<script lang="ts"> + import type { ThemeLayer } from "@radroots/theme"; + import { fmt_cl, type ITrellisDefaultLabel } from "@radroots/util"; + + let { + layer, + labels, + classes = ``, + }: { + layer: ThemeLayer; + labels: ITrellisDefaultLabel[]; + classes?: string; + } = $props(); +</script> + +<div class={`${fmt_cl(classes)} flex flex-row`}> + <p class={`font-sans text-trellis_ti text-layer-${layer}-glyph-shade`}> + {#each labels as label} + <span class={`${fmt_cl(label.classes)} font-sans text-trellis_ti`}> + {#if `callback` in label} + <button + class={``} + onclick={async () => { + if (`callback` in label && label.callback) + await label.callback(); + }} + > + {label.label} + </button> + {:else} + {label.label} + {/if} + </span> + {/each} + </p> +</div> diff --git a/apps-lib/src/lib/components/trellis/trellis-end.svelte b/apps-lib/src/lib/components/trellis/trellis-end.svelte @@ -0,0 +1,36 @@ +<script lang="ts"> + import { Glyph } from "$root"; + import type { ThemeLayer } from "@radroots/theme"; + import { type ITrellisBasisTouchEnd } from "@radroots/util"; + + let { + basis, + layer, + hide_active, + }: { + basis: ITrellisBasisTouchEnd; + layer: ThemeLayer; + hide_active: boolean; + } = $props(); +</script> + +<div + class={`absolute top-0 right-0 h-full w-max flex flex-row justify-center items-center`} +> + <button + class={`flex pr-3`} + onclick={async (ev) => { + if (basis.callback) await basis.callback(ev); + }} + > + {#if basis.glyph} + <Glyph + basis={{ + classes: `text-layer-${layer}-glyph-shade ${hide_active ? `` : `group-active:text-layer-${layer}-glyph_a`} translate-y-[1px] opacity-70`, + dim: `xs+`, + ...basis.glyph, + }} + /> + {/if} + </button> +</div> diff --git a/apps-lib/src/lib/components/trellis/trellis-input.svelte b/apps-lib/src/lib/components/trellis/trellis-input.svelte @@ -0,0 +1,87 @@ +<script lang="ts"> + import { Glyph, Input, LoadSymbol } from "$root"; + import type { ThemeLayer } from "@radroots/theme"; + import { + fmt_cl, + fmt_trellis, + type ITrellisBasisInput, + } from "@radroots/util"; + + let { + basis, + layer, + hide_border_b, + hide_border_t, + }: { + basis: ITrellisBasisInput; + layer: ThemeLayer; + hide_border_b: boolean; + hide_border_t: boolean; + } = $props(); +</script> + +<div class={`flex flex-row flex-grow h-full w-full`}> + <div + class={`${fmt_trellis(hide_border_b, hide_border_t)} flex flex-row h-line w-full justify-start items-center border-t-line border-layer-${layer}-surface-edge overflow-hidden`} + > + {#if basis.line_label && basis.line_label.value} + <div + class={`${fmt_cl(basis.line_label.classes)} flex flex-row h-full justify-start items-center overflow-x-hidden`} + > + <p class={`font-sans text-layer-${layer}-glyph_b`}> + {basis.line_label.value} + </p> + </div> + {/if} + <div + class={`relative flex flex-row flex-grow h-full pr-12 justify-start items-center`} + > + <Input + basis={{ + ...basis.basis, + layer: layer, + }} + /> + {#if basis.action} + {#if basis.action.visible} + <div + class={`absolute top-0 right-0 flex flex-row h-full w-12 pr-4 justify-end items-center fade-in`} + > + {#if basis.action.loading} + <div class={`flex flex-row fade-in`}> + <LoadSymbol + basis={{ + dim: `glyph-send-button`, + blades: 8, + classes: `text-layer-${layer}-glyph el-re`, + }} + /> + </div> + {:else} + <button + class={`group fade-in-long`} + onclick={async () => { + if (basis.action?.callback) + await basis.action.callback(); + }} + > + <Glyph + basis={basis.action.glyph + ? { + dim: `md-`, + ...basis.action.glyph, + } + : { + key: `plus`, + classes: `text-layer-${layer}-glyph`, + dim: `md-`, + }} + /> + </button> + {/if} + </div> + {/if} + {/if} + </div> + </div> +</div> diff --git a/apps-lib/src/lib/components/trellis/trellis-line.svelte b/apps-lib/src/lib/components/trellis/trellis-line.svelte @@ -0,0 +1,56 @@ +<script lang="ts"> + import { LoadSymbol } from "$root"; + import type { ThemeLayer } from "@radroots/theme"; + import { fmt_trellis, type CallbackPromiseGeneric } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + loading = false, + layer, + callback, + hide_border_b, + hide_border_t, + children, + el_end, + }: { + loading?: boolean; + layer: ThemeLayer; + callback?: CallbackPromiseGeneric<MouseEvent>; + hide_border_b: boolean; + hide_border_t: boolean; + children: Snippet; + el_end?: Snippet; + } = $props(); +</script> + +<button + class={`flex flex-row flex-grow overflow-hidden`} + onclick={async (ev) => { + if (callback) await callback(ev); + }} +> + <div + class={`${fmt_trellis(hide_border_b, hide_border_t)} flex flex-row h-full w-full justify-center items-center border-t-line border-layer-${layer}-surface-edge el-re`} + > + {#if loading} + <div + class={`flex flex-row h-full w-full justify-center items-center`} + > + <LoadSymbol basis={{ dim: `sm` }} /> + </div> + {:else} + <div + class={`relative group flex flex-row h-line w-full pr-[2px] justify-between items-center el-re`} + > + <div + class={`flex flex-row h-full w-trellis_display justify-between items-center`} + > + {@render children()} + </div> + {#if el_end} + {@render el_end()} + {/if} + </div> + {/if} + </div> +</button> diff --git a/apps-lib/src/lib/components/trellis/trellis-offset.svelte b/apps-lib/src/lib/components/trellis/trellis-offset.svelte @@ -0,0 +1,71 @@ +<script lang="ts"> + import { Empty, Glyph, GlyphCircle, LoadSymbol } from "$root"; + import type { ThemeLayer } from "@radroots/theme"; + import { + fmt_cl, + type ITrellisBasisOffset, + type ITrellisBasisOffsetMod, + } from "@radroots/util"; + + let { + basis = undefined, + layer, + }: { + basis?: ITrellisBasisOffset; + layer: ThemeLayer; + } = $props(); + + const mod: ITrellisBasisOffsetMod = $derived(basis?.mod ? basis.mod : `sm`); +</script> + +<div class={`flex flex-row h-full`}> + {#if mod === `sm`} + <div class={`${fmt_cl(``)} flex flex-row h-full w-[22px]`}> + <Empty /> + </div> + {:else if mod === `glyph`} + <div class={`flex flex-row pr-[2px]`}> + <div class={`${fmt_cl(``)} flex flex-row h-full w-trellisOffset`}> + <Empty /> + </div> + </div> + {:else if typeof mod === `object`} + <div + class={`flex flex-row h-full min-w-[20px] w-trellisOffset justify-center items-center pr-3`} + > + <button + class={`fade-in pl-2 translate-x-[3px] translate-y-[1px]`} + onclick={async (ev) => { + if (mod.loading) return; + else if (typeof basis !== `boolean` && basis?.callback) + await basis.callback(ev); + }} + > + {#if mod.loading} + <LoadSymbol basis={{ blades: 8, dim: `xs` }} /> + {:else if `glyph` in mod} + <Glyph + basis={{ + classes: mod.glyph.classes + ? mod.glyph.classes + : `text-layer-${layer}-glyph`, + ...mod.glyph, + }} + /> + {:else if `glyph_circle` in mod} + <GlyphCircle + basis={{ + classes_wrap: mod.glyph_circle?.classes_wrap, + glyph: { + classes: mod.glyph_circle?.glyph?.classes + ? mod.glyph_circle?.glyph?.classes + : `text-layer-${layer}-glyph`, + ...mod.glyph_circle?.glyph, + }, + }} + ></GlyphCircle> + {/if} + </button> + </div> + {/if} +</div> diff --git a/apps-lib/src/lib/components/trellis/trellis-row-display-value.svelte b/apps-lib/src/lib/components/trellis/trellis-row-display-value.svelte @@ -0,0 +1,47 @@ +<script lang="ts"> + import { Glyph } from "$root"; + import type { ThemeLayer } from "@radroots/theme"; + import { + fmt_cl, + get_label_classes_kind, + type ITrellisKindDisplayValue, + } from "@radroots/util"; + + let { + basis, + layer, + hide_active, + }: { + basis: ITrellisKindDisplayValue; + layer: ThemeLayer; + hide_active: boolean; + } = $props(); +</script> + +<button + class={`z-10 flex flex-grow justify-end`} + onclick={async (ev) => { + ev.stopPropagation(); + if (basis.callback) await basis.callback(ev); + }} +> + {#if `icon` in basis} + <Glyph + basis={{ + classes: + basis.icon.classes || + `${get_label_classes_kind(layer, `shade`, hide_active)}`, + key: basis.icon.key, + dim: `sm`, + }} + /> + {:else if basis.label} + {#if `value` in basis.label} + <p + class={`${fmt_cl(basis.label.classes)} font-sans text-line_d_e line-clamp-1 text-layer-0-glyph-label el-re`} + > + {basis.label.value} + </p> + {/if} + {/if} +</button> diff --git a/apps-lib/src/lib/components/trellis/trellis-row-label.svelte b/apps-lib/src/lib/components/trellis/trellis-row-label.svelte @@ -0,0 +1,67 @@ +<script lang="ts"> + import { GlyphButton } from "$root"; + import type { ThemeLayer } from "@radroots/theme"; + import { + fmt_cl, + get_label_classes_kind, + type ILabelTupFields, + } from "@radroots/util"; + + let { + basis, + layer, + hide_active, + }: { + basis: ILabelTupFields; + layer: ThemeLayer; + hide_active: boolean; + } = $props(); +</script> + +<div class={`flex flex-row h-full items-center justify-between`}> + {#if basis.left && basis.left.length} + <div class={`flex flex-row h-full items-center truncate`}> + {#each basis.left as title_l} + <div + class={`${fmt_cl(title_l.classes_wrap)} flex flex-row h-full items-center ${get_label_classes_kind(layer, undefined, hide_active)} ${title_l.hide_truncate ? `` : `truncate`}`} + > + {#if `glyph` in title_l} + <div + class={`flex flex-row justify-start items-center pr-2`} + > + <GlyphButton basis={{ ...title_l.glyph }} + ></GlyphButton> + </div> + {:else if `value` in title_l} + <p + class={`${fmt_cl(title_l.classes)} font-sans text-line_d ${title_l.hide_truncate ? `` : `truncate`} el-re`} + > + {title_l.value || ``} + </p> + {/if} + </div> + {/each} + </div> + {/if} + {#if basis.right && basis.right.length} + <div + class={`flex flex-row h-full w-content items-center justify-end pr-4`} + > + {#each basis.right.reverse() as title_r} + <div + class={`${fmt_cl(title_r.classes_wrap)} flex flex-row h-full gap-1 items-center ${title_r.hide_truncate ? `` : `truncate`}`} + > + {#if `glyph` in title_r} + <GlyphButton basis={{ ...title_r.glyph }}></GlyphButton> + {:else if `value` in title_r} + <p + class={`${fmt_cl(title_r.classes)} font-sans text-line_d text-layer-${layer}-glyph_d ${title_r.hide_truncate ? `` : `truncate`} el-re`} + > + {title_r.value || ``} + </p> + {/if} + </div> + {/each} + </div> + {/if} +</div> diff --git a/apps-lib/src/lib/components/trellis/trellis-select.svelte b/apps-lib/src/lib/components/trellis/trellis-select.svelte @@ -0,0 +1,67 @@ +<script lang="ts"> + import { + LoadSymbol, + SelectMenu, + TrellisEnd, + TrellisLine, + TrellisRowDisplayValue, + TrellisRowLabel, + } from "$root"; + import type { ThemeLayer } from "@radroots/theme"; + import type { ITrellisBasisSelect } from "@radroots/util"; + + let { + basis, + layer, + hide_active, + hide_border_b, + hide_border_t, + }: { + basis: ITrellisBasisSelect; + layer: ThemeLayer; + hide_active: boolean; + hide_border_b: boolean; + hide_border_t: boolean; + } = $props(); + + const loading = $derived( + typeof basis?.loading === `boolean` ? basis.loading : false, + ); + + const value = $derived(basis.el.value); +</script> + +<TrellisLine + {layer} + {loading} + {hide_border_b} + {hide_border_t} + callback={basis.callback} +> + <TrellisRowLabel basis={basis.label} {layer} {hide_active} /> + {#if basis.display} + <div class={`flex flex-row pr-3 justify-center items-end`}> + <SelectMenu {value} basis={basis.el}> + {#if basis.display.loading} + <div + class={`flex flex-row h-full w-full justify-end items-center`} + > + <LoadSymbol basis={{ dim: `sm` }} /> + </div> + {:else} + <TrellisRowDisplayValue + basis={{ ...basis.display }} + {layer} + {hide_active} + /> + {/if} + </SelectMenu> + </div> + {/if} + + {#snippet el_end()} + {#if basis.end} + <TrellisEnd basis={basis.end} {layer} {hide_active} /> + {/if} + {/snippet} +</TrellisLine> diff --git a/apps-lib/src/lib/components/trellis/trellis-title.svelte b/apps-lib/src/lib/components/trellis/trellis-title.svelte @@ -0,0 +1,68 @@ +<script lang="ts"> + import { Empty, Glyph, LabelSwap } from "$root"; + import type { ThemeLayer } from "@radroots/theme"; + import { type ITrellisTitle, fmt_cl } from "@radroots/util"; + + let { + basis, + layer = 0, + }: { + basis: ITrellisTitle; + layer: ThemeLayer; + } = $props(); + + const mod = $derived(basis?.mod ? basis.mod : `sm`); +</script> + +<div + class={`${fmt_cl(basis.classes)} flex flex-row h-[24px] w-full pl-[2px] gap-1 items-center`} +> + <button + class={`flex flex-row h-full w-max items-center gap-1 ${mod === `glyph` ? `pl-[36px]` : mod === `sm` ? `pl-[16px]` : ``}`} + onclick={async () => { + if (basis && basis.callback) await basis.callback(); + }} + > + {#if basis.value === true} + <Empty /> + {:else} + <p + class={`font-sans text-trellis_ti text-layer-${layer}-glyph-label uppercase`} + > + {basis.value || ``} + </p> + {/if} + </button> + {#if basis.link} + <button + class={`${fmt_cl(basis.link.classes)} group flex flex-row h-full w-max items-center`} + onclick={async () => { + if (basis.link && basis.link.callback) + await basis.link.callback(); + }} + > + {#if basis.link.label} + {#if `swap` in basis.link.label} + <LabelSwap basis={basis.link.label} /> + {:else if `value` in basis.link.label} + <p + class={`${fmt_cl(basis.link.label.classes)} font-sans text-trellis_ti uppercase fade-in`} + > + {basis.link.label.value || ``} + </p> + {/if} + {/if} + {#if basis.link.glyph} + <div class={`flex flex-row w-max`}> + <Glyph + basis={{ + ...basis.link.glyph, + dim: `xs-`, + classes: `${fmt_cl(basis.link.glyph.classes)} fade-in`, + }} + /> + </div> + {/if} + </button> + {/if} +</div> diff --git a/apps-lib/src/lib/components/trellis/trellis-touch.svelte b/apps-lib/src/lib/components/trellis/trellis-touch.svelte @@ -0,0 +1,41 @@ +<script lang="ts"> + import { + TrellisEnd, + TrellisLine, + TrellisRowDisplayValue, + TrellisRowLabel, + } from "$root"; + import type { ITrellisBasisTouch, ThemeLayer } from "@radroots/util"; + + let { + basis, + layer, + hide_active, + hide_border_b, + hide_border_t, + }: { + basis: ITrellisBasisTouch; + layer: ThemeLayer; + hide_active: boolean; + hide_border_b: boolean; + hide_border_t: boolean; + } = $props(); +</script> + +<TrellisLine {layer} {hide_border_b} {hide_border_t} callback={basis.callback}> + <TrellisRowLabel basis={basis.label} {layer} {hide_active} /> + {#if basis.display} + <TrellisRowDisplayValue + basis={{ + ...basis.display, + }} + {layer} + {hide_active} + /> + {/if} + {#snippet el_end()} + {#if basis.end} + <TrellisEnd basis={basis.end} {layer} {hide_active} /> + {/if} + {/snippet} +</TrellisLine> diff --git a/apps-lib/src/lib/components/trellis/trellis.svelte b/apps-lib/src/lib/components/trellis/trellis.svelte @@ -0,0 +1,146 @@ +<script lang="ts"> + import { + app_lo, + lls, + TrellisDefaultLabel, + TrellisInput, + TrellisOffset, + TrellisSelect, + TrellisTitle, + TrellisTouch, + } from "$root"; + import { fmt_cl, parse_layer, type ITrellis } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis, + el_default, + el_offset, + el_append, + }: { + basis: ITrellis; + el_default?: Snippet; + el_offset?: Snippet; + el_append?: Snippet; + } = $props(); + + const hide_border_t = $derived( + typeof basis.hide_border_top === `boolean` + ? basis.hide_border_top + : true, + ); + + const hide_border_b = $derived( + typeof basis.hide_border_bottom === `boolean` + ? basis.hide_border_bottom + : true, + ); + + const hide_rounded = $derived( + typeof basis.hide_rounded === `boolean` ? basis.hide_rounded : false, + ); + + const set_title_background = $derived( + typeof basis.set_title_background === `boolean` + ? basis.set_title_background + : false, + ); + + const set_default_background = $derived( + typeof basis.set_default_background === `boolean` + ? basis.set_default_background + : false, + ); +</script> + +<div + id={basis.id || ``} + class={`${fmt_cl(basis.classes)} flex flex-col`} + data-view={basis.view || ``} +> + <div + class={`relative flex flex-col h-auto w-lo_${$app_lo} gap-[3px] ${set_title_background ? `bg-layer-${basis.layer}-surface` : ``}`} + > + {#if basis.title && (!basis.default_el || (basis.default_el && basis.default_el.show_title))} + <TrellisTitle + basis={basis.title} + layer={parse_layer(basis.layer - 1)} + /> + {/if} + {#if basis.default_el} + <div + class={`flex flex-col h-auto w-full justify-center items-center`} + > + {#if el_default} + {@render el_default()} + {:else if basis.default_el} + <TrellisDefaultLabel + layer={parse_layer(basis.layer - 1)} + labels={basis.default_el.labels + ? basis.default_el.labels + : [ + { + label: `${$lls(`common.no_items_to_display`)}.`, + }, + ]} + /> + {/if} + </div> + {:else if basis.list} + <div class={`flex flex-col w-full justify-center items-center`}> + {#each basis.list as li} + {#if li} + <div + class={`${li.hide_field ? `hidden` : ``} group flex flex-row h-full w-full justify-end items-center bg-layer-${basis.layer}-surface ${li.full_rounded ? `rounded-touch` : ``} ${hide_rounded ? `` : `first:rounded-t-2xl last:rounded-b-2xl`} ${!li.hide_active ? `active:bg-layer-${basis.layer}-surface_a` : ``} el-re`} + > + <div + class={`flex flex-row h-full w-full gap-1 items-center overflow-y-hidden`} + > + {#if !basis.hide_offset} + <TrellisOffset + basis={li.offset} + layer={basis.layer} + /> + {/if} + {#if el_offset} + {@render el_offset()} + {/if} + {#if `touch` in li && li.touch} + <TrellisTouch + basis={li.touch} + layer={basis.layer} + {hide_border_b} + {hide_border_t} + hide_active={!!li.hide_active} + /> + {:else if `input` in li && li.input} + <TrellisInput + basis={li.input} + layer={basis.layer} + {hide_border_b} + {hide_border_t} + /> + {:else if `select` in li && li.select} + <TrellisSelect + basis={li.select} + layer={basis.layer} + {hide_border_b} + {hide_border_t} + hide_active={!!li.hide_active} + /> + {/if} + </div> + </div> + {/if} + {/each} + </div> + {/if} + </div> + {#if el_append} + <div + class={`flex flex-col w-full ${set_default_background ? `bg-layer-${basis.layer}-surface` : ``}`} + > + {@render el_append()} + </div> + {/if} +</div> diff --git a/apps-lib/src/lib/components/upload/image-upload-add-photo.svelte b/apps-lib/src/lib/components/upload/image-upload-add-photo.svelte @@ -0,0 +1,41 @@ +<script lang="ts"> + import { Glyph, lls } from "$root"; + import type { LcPhotoAddCallback } from "@radroots/util"; + + let { + basis, + photo_path = $bindable(``), + }: { + basis: { + lc_handle_photo_add: LcPhotoAddCallback; + }; + photo_path: string; + } = $props(); +</script> + +<div class={`relative flex flex-row w-full justify-center items-center`}> + <button + class={`flex flex-row h-[5rem] w-[5rem] justify-center items-center bg-layer-1-surface/60 rounded-full`} + onclick={async () => { + const photo_path_add = await basis.lc_handle_photo_add(); + if (photo_path_add) photo_path = photo_path_add; + }} + > + <Glyph + basis={{ + classes: `text-[40px] text-layer-2-glyph`, + dim: `sm`, + key: `camera`, + }} + /> + <div + class={`absolute -bottom-[1.8rem] flex flex-row justify-start items-center`} + > + <p + class={`font-arch font-[600] text-sm text-layer-0-glyph capitalize`} + > + {`${$lls(`icu.add_*`, { value: `${$lls(`common.photo`)}` })}`} + </p> + </div> + </button> +</div> diff --git a/apps-lib/src/lib/components/upload/image-upload-buttons-aspect.svelte b/apps-lib/src/lib/components/upload/image-upload-buttons-aspect.svelte @@ -0,0 +1,95 @@ +<script lang="ts"> + import { ButtonLabelGlyph, Empty } from "$root"; + import { + type CallbackPromiseGeneric, + type ImageAspectRatio, + } from "@radroots/util"; + + let { + basis, + }: { + basis: { + callback: CallbackPromiseGeneric<ImageAspectRatio>; + }; + } = $props(); + + let ratio_curr: ImageAspectRatio = $state(`auto`); +</script> + +<div class={`flex flex-row w-full justify-around items-center`}> + <ButtonLabelGlyph + basis={{ + glyph: `crop`, + label: `Auto`, + ring: ratio_curr === `auto`, + callback: async () => { + ratio_curr = `auto`; + await basis.callback(`auto`); + }, + }} + /> + <ButtonLabelGlyph + basis={{ + label: `1:1`, + ring: ratio_curr === `1/1`, + callback: async () => { + ratio_curr = `1/1`; + await basis.callback(`1/1`); + }, + }} + > + <div + class={`flex flex-row h-5 w-5 justify-start items-center rounded-md bg-layer-2-surface shadow-lg`} + > + <Empty /> + </div> + </ButtonLabelGlyph> + <ButtonLabelGlyph + basis={{ + label: `4:3`, + ring: ratio_curr === `4/3`, + callback: async () => { + ratio_curr = `4/3`; + await basis.callback(`4/3`); + }, + }} + > + <div + class={`flex flex-row w-5 aspect-[4/3] justify-start items-center rounded-md bg-layer-2-surface shadow-lg rotate-90`} + > + <Empty /> + </div> + </ButtonLabelGlyph> + <ButtonLabelGlyph + basis={{ + label: `16:9`, + ring: ratio_curr === `16/9`, + callback: async () => { + ratio_curr = `16/9`; + await basis.callback(`16/9`); + }, + }} + > + <div + class={`flex flex-row w-5 aspect-[16/9] justify-start items-center rounded-md bg-layer-2-surface shadow-lg rotate-90`} + > + <Empty /> + </div> + </ButtonLabelGlyph> + <ButtonLabelGlyph + basis={{ + label: `3:4`, + ring: ratio_curr === `3/4`, + callback: async () => { + ratio_curr = `3/4`; + await basis.callback(`3/4`); + }, + }} + > + <div + class={`flex flex-row w-5 aspect-[3/4] justify-start items-center rounded-md bg-layer-2-surface shadow-lg`} + > + <Empty /> + </div> + </ButtonLabelGlyph> +</div> diff --git a/apps-lib/src/lib/components/upload/image-upload-simple.svelte b/apps-lib/src/lib/components/upload/image-upload-simple.svelte @@ -0,0 +1,65 @@ +<script lang="ts"> + import { Glyph, ImagePath, ImageUploadButtonsAspect } from "$root"; + import { + list_assign, + type LcPhotoAddMultipleCallback, + } from "@radroots/util"; + + let { + basis, + photo_paths = $bindable([]), + }: { + basis: { + lc_handle_photo_add: LcPhotoAddMultipleCallback; + }; + photo_paths: string[]; + } = $props(); + + let img_data = $state(``); +</script> + +<div class={`flex flex-col w-full gap-4 justify-center items-center`}> + {#if img_data} + <div + class={`flex flex-row h-[20rem] w-full gap-2 justify-center items-center rounded-3xl bg-layer-1-surface/60 overflow-hidden`} + > + <ImagePath basis={{ path: img_data }} /> + </div> + {:else} + <button + class={`flex flex-row w-full justify-center items-center`} + onclick={async () => { + const photo_paths_add = await basis.lc_handle_photo_add(); + if (photo_paths_add) { + photo_paths = list_assign(photo_paths, photo_paths_add); + img_data = photo_paths[0]; + } + }} + > + <div + class={`flex flex-row h-[20rem] w-full gap-2 justify-center items-center border-line border-layer-0-glyph/20 border-[3px] border-dashed rounded-3xl bg-layer-1-surface/60`} + > + <Glyph + basis={{ + classes: `text-layer-0-glyph/80`, + dim: `sm`, + + key: `upload-simple`, + }} + /> + <p + class={`font-sans font-[400] text-layer-0-glyph/80 capitalize`} + > + {`Upload image`} + </p> + </div> + </button> + {/if} + <ImageUploadButtonsAspect + basis={{ + callback: async (ratio) => { + console.log(`ratio `, ratio); + }, + }} + /> +</div> diff --git a/apps-lib/src/lib/el/glyph.svelte b/apps-lib/src/lib/el/glyph.svelte @@ -1,19 +0,0 @@ -<script lang="ts"> - import { fmt_cl, glyph_style_map, type IGlyph } from "$lib"; - - export let basis: IGlyph; - $: basis = basis; - - $: weight = - !basis?.weight || basis?.weight === `regular` ? `` : `-${basis.weight}`; - $: styles = basis?.dim - ? glyph_style_map.get(basis.dim) - : glyph_style_map.get(`sm`); -</script> - -<div - id={basis.id || null} - class={`${fmt_cl(basis.classes)} flex flex-row text-[${styles?.gl_1}px] justify-center items-center`} -> - <i class={`ph${weight} ph-${basis.key}`}></i> -</div> diff --git a/apps-lib/src/lib/el/image-blob.svelte b/apps-lib/src/lib/el/image-blob.svelte @@ -1,43 +0,0 @@ -<script lang="ts"> - import type { IIdOpt } from "$lib"; - import { onDestroy, onMount } from "svelte"; - - export let basis: IIdOpt & { - data: Uint8Array | undefined; - alt?: string; - }; - - let img_url = ``; - - onMount(async () => { - try { - if (!basis.data) return; - img_url = URL.createObjectURL( - new Blob([basis.data], { - type: "image/jpeg", - }), - ); - } catch (e) { - console.log(`(error) image blob `, e); - } - }); - - onDestroy(async () => { - try { - URL.revokeObjectURL(img_url); - } catch (e) {} - }); -</script> - -{#if img_url} - <img id={basis.id || null} src={img_url} alt={basis.alt || null} /> -{/if} - -<style> - img { - height: 100%; - width: 100%; - object-fit: cover; - display: block; - } -</style> diff --git a/apps-lib/src/lib/el/image-path.svelte b/apps-lib/src/lib/el/image-path.svelte @@ -1,48 +0,0 @@ -<!-- svelte-ignore a11y-click-events-have-key-events --> -<!-- svelte-ignore a11y-no-noninteractive-element-interactions --> -<script lang="ts"> - import { type ICbGOpt, type IClOpt, type IIdOpt, fmt_cl } from "$lib"; - import { onMount } from "svelte"; - - export let basis: IClOpt & - ICbGOpt< - MouseEvent & { - currentTarget: EventTarget & HTMLImageElement; - } - > & - IIdOpt & { - path?: string; - alt?: string; - }; - - let img_src = ``; - - onMount(async () => { - try { - if (basis.path) img_src = basis.path; - } catch (e) { - } finally { - } - }); -</script> - -{#if img_src} - <img - id={basis?.id || null} - class={`${fmt_cl(basis?.classes)}`} - src={img_src} - alt={basis?.alt || null} - on:click|stopPropagation={async (ev) => { - if (basis?.callback) await basis.callback(ev); - }} - /> -{/if} - -<style> - img { - height: 100%; - width: 100%; - object-fit: cover; - display: block; - } -</style> diff --git a/apps-lib/src/lib/el/input.svelte b/apps-lib/src/lib/el/input.svelte @@ -1,104 +0,0 @@ -<script lang="ts"> - import { browser } from "$app/environment"; - import { - type IInput, - fmt_cl, - kv_basis, - parse_layer, - value_constrain, - } from "$lib"; - import { onMount } from "svelte"; - - let el: HTMLInputElement | null = null; - - export let value: string = ``; - export let basis: IInput<string>; - - onMount(async () => { - try { - await kv_init(); - } catch (e) { - } finally { - } - }); - - $: basis = basis; - $: id = basis?.id ? basis?.id : null; - $: layer = - typeof basis?.layer === `boolean` - ? parse_layer(0) - : parse_layer(basis?.layer); - $: classes_layer = - typeof basis?.layer === `boolean` || typeof basis?.layer === `undefined` - ? `` - : `bg-layer-${layer}-surface text-layer-${layer}-glyph placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`; - $: if (basis?.id && basis?.sync && value) { - (async () => { - try { - browser && (await kv_basis.set(basis?.id, value)); - } catch (e) {} - })(); - } - - const kv_init = async (): Promise<void> => { - try { - if (!basis?.id) return; - if (basis?.sync) { - const kv_val = browser && (await kv_basis.get(basis?.id)); - if (kv_val && el) el.value = kv_val; - else browser && (await kv_basis.set(basis?.id, ``)); - } - if (basis?.callback_mount) await basis?.callback_mount({ el }); - } catch (e) { - console.log(`(error) kv_init `, e); - } - }; - - const handle_on_input = async (el: HTMLInputElement): Promise<void> => { - try { - let pass = true; - let val = el?.value; - if (basis?.field && el) { - val = value_constrain(basis?.field.charset, val); - el.value = val; - if ( - !basis?.field.validate.test(val) && - basis?.field.validate_keypress - ) { - //@todo set styles - } - pass = basis?.field.validate.test(val); - } - if (basis?.sync) browser && (await kv_basis.set(basis?.id, val)); - if (basis?.callback) await basis?.callback({ value: val, pass }); - } catch (e) { - console.log(`(error) handle_on_input `, e); - } - }; -</script> - -<input - bind:this={el} - bind:value - on:input={async ({ currentTarget: el }) => { - await handle_on_input(el); - }} - on:blur={async ({ currentTarget: el }) => { - if (basis.callback_blur) await basis.callback_blur({ el }); - }} - on:focus={async ({ currentTarget: el }) => { - if (basis.callback_focus) await basis.callback_focus({ el }); - }} - on:keydown={async (ev) => { - if (basis?.callback_keydown) - await basis?.callback_keydown({ - key: ev.key, - key_s: ev.key === `Enter`, - el: ev.currentTarget, - }); - }} - {id} - type="text" - class={`${fmt_cl(basis?.classes)} el-input ${classes_layer} el-re`} - placeholder={basis?.placeholder || ``} -/> diff --git a/apps-lib/src/lib/el/label-swap.svelte b/apps-lib/src/lib/el/label-swap.svelte @@ -1,32 +0,0 @@ -<!-- svelte-ignore a11y-label-has-associated-control --> -<script lang="ts"> - import { fmt_cl, type ILabelSwap, type ILyOpt } from "$lib"; - - export let el_swap: HTMLLabelElement | null = null; - export let basis: ILabelSwap & ILyOpt; - $: basis = basis; - - $: layer = basis?.layer ? basis.layer : 1; -</script> - -<div class={`flex flex-row justify-start items-center`}> - <label - bind:this={el_swap} - class={`swap${basis.swap.toggle ? ` swap-active` : ``}`} - > - <div class="swap-on"> - <p - class={`${fmt_cl(basis.swap.on.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-60`)} font-sans -translate-y-[1px] el-re`} - > - {basis.swap.on.value} - </p> - </div> - <div class="swap-off"> - <p - class={`${fmt_cl(basis.swap.off.classes || `text-nav_prev text-layer-${layer}-glyph-hl group-active:opacity-60`)} font-sans -translate-y-[1px] el-re`} - > - {basis.swap.off.value} - </p> - </div> - </label> -</div> diff --git a/apps-lib/src/lib/el/load-symbol.svelte b/apps-lib/src/lib/el/load-symbol.svelte @@ -1,64 +0,0 @@ -<script lang="ts"> - import { type ILoadSymbol, loading_style_map } from "$lib"; - - export let basis: ILoadSymbol | undefined = undefined; - $: basis = basis; - - $: styles = basis?.dim - ? loading_style_map.get(basis?.dim) - : loading_style_map.get("sm"); - $: num_blades = basis?.blades || 8; -</script> - -<div - class={`relative flex flex-row justify-center items-center h-[${styles?.dim_1}px] w-[${styles?.dim_1}px] fade-in el-re`} -> - <div - class={`${num_blades === 12 ? `spinner12 center` : `spinner8 center`} text-[${styles?.gl_2 || styles?.dim_1}px]`} - > - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - {#if num_blades === 12} - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - <div - class={`${num_blades === 12 ? `spinner12-blade` : `spinner8-blade`}`} - ></div> - {/if} - </div> -</div> diff --git a/apps-lib/src/lib/el/select-menu.svelte b/apps-lib/src/lib/el/select-menu.svelte @@ -1,64 +0,0 @@ -<script lang="ts"> - import { fmt_cl, parse_layer, type ISelect } from "$lib"; - - export let value: string; - export let basis: ISelect; - $: basis = basis; - - let el_wrap: HTMLDivElement | null = null; - let el_select: HTMLSelectElement | null = null; - $: layer = - typeof basis?.layer === `boolean` - ? parse_layer(0) - : parse_layer(basis.layer); - $: classes_layer = - typeof basis?.layer === `boolean` ? `` : `text-layer-${layer}-glyph`; -</script> - -<div - class={`relative flex flex-row h-max w-auto justify-center items-center`} - bind:this={el_wrap} -> - <div - class={`${fmt_cl(basis.classes)} z-20 absolute top-0 left-0 flex flex-row h-full w-full justify-end items-center ${classes_layer}`} - > - <select - class={`select select-ghost h-full w-full bg-transparent focus:border-0 focus:outline-0 text-transparent focus:text-transparent`} - bind:this={el_select} - bind:value - on:change={async (e) => { - const opt = basis.options - .map((i) => i.entries) - .reduce((_, j) => j, []) - .find((k) => k.value === e.currentTarget?.value); - if (basis.callback && opt) await basis.callback(opt); - if (el_select) el_select.value = value; - }} - > - {#each basis.options as opt_g} - {#if opt_g.group} - <optgroup> - {#each opt_g.entries as opt} - <option - label={opt_g.group === true - ? `-`.repeat(21) - : opt_g.group || ``} - > - {opt.label} - </option> - {/each} - </optgroup> - {:else} - {#each opt_g.entries as opt} - <option value={opt.value} disabled={!!opt.disabled}> - {opt.label} - </option> - {/each} - {/if} - {/each} - </select> - </div> - <div class={`z-10 flex flex-row h-full w-full`}> - <slot name="element" /> - </div> -</div> diff --git a/apps-lib/src/lib/el/select.svelte b/apps-lib/src/lib/el/select.svelte @@ -1,111 +0,0 @@ -<script lang="ts"> - import { browser } from "$app/environment"; - import { fmt_cl, Glyph, kv_basis, parse_layer, type ISelect } from "$lib"; - import { onMount } from "svelte"; - - let el: HTMLSelectElement | null = null; - - export let value: string; - export let basis: ISelect; - - $: basis = basis; - $: id = basis?.id ? basis?.id : null; - $: layer = - typeof basis?.layer === `boolean` - ? parse_layer(0) - : parse_layer(basis.layer); - $: classes_layer = - typeof basis?.layer === `boolean` - ? `` - : !value - ? `text-layer-${layer}-glyph_pl` - : `text-layer-${layer}-glyph_d`; - onMount(async () => { - try { - if (basis.id && basis.sync_init) { - const sync_val = browser && (await kv_basis.get(basis.id)); - browser && (await kv_basis.set(basis.id, sync_val || ``)); - } - } catch (e) { - } finally { - } - }); - - $: if (basis?.id && basis?.sync) { - (async () => { - try { - browser && (await kv_basis.set(basis?.id, value)); - } catch (e) {} - })(); - } - - const handle_on_change = async (el: HTMLSelectElement): Promise<void> => { - try { - const opt = basis.options - .map((i) => i.entries) - .reduce((_, j) => j, []) - .find((k) => k.value === el?.value); - if (el) el.value = value; - if (basis?.sync) browser && (await kv_basis.set(basis?.id, value)); - if (basis.callback && opt) await basis.callback(opt); - } catch (e) { - console.log(`(error) handle_on_change `, e); - } - }; -</script> - -{#if basis?.show_arrows === "l"} - <div class={`flex flex-row justify-center items-center`}> - <Glyph - basis={{ - key: `caret-up-down`, - dim: `xs`, - weight: `bold`, - classes: `text-layer-${layer}-glyph translate-y-[1px]`, - }} - ></Glyph> - </div> -{/if} -<select - bind:this={el} - bind:value - on:change={async ({ currentTarget: el }) => { - handle_on_change(el); - }} - {id} - class={`${fmt_cl(basis.classes)} z-10 el-select ${classes_layer}`} -> - {#each basis.options as opt_g} - {#if opt_g.group} - <optgroup> - {#each opt_g.entries as opt} - <option - label={opt_g.group === true - ? `-`.repeat(21) - : opt_g.group || ``} - > - {opt.label} - </option> - {/each} - </optgroup> - {:else} - {#each opt_g.entries as opt} - <option value={opt.value} disabled={!!opt.disabled}> - {opt.label} - </option> - {/each} - {/if} - {/each} -</select> -{#if basis?.show_arrows === "r"} - <div class={`flex flex-row justify-center items-center`}> - <Glyph - basis={{ - key: `caret-up-down`, - dim: `xs`, - weight: `bold`, - classes: `text-layer-${layer}-glyph`, - }} - ></Glyph> - </div> -{/if} diff --git a/apps-lib/src/lib/el/styles.svelte b/apps-lib/src/lib/el/styles.svelte @@ -1 +0,0 @@ -<div class="hidden -bottom-lo_view_mobile_base -bottom-lo_view_mobile_y -bottom-nav_mobile_base -bottom-nav_mobile_y -bottom-tabs_mobile_base -bottom-tabs_mobile_y -bottom-trellis_centered_mobile_base -bottom-trellis_centered_mobile_y -bottom-view_mobile_base -bottom-view_mobile_y -bottom-view_offset_mobile_base -bottom-view_offset_mobile_y -top-lo_view_mobile_base -top-lo_view_mobile_y -top-nav_mobile_base -top-nav_mobile_y -top-tabs_mobile_base -top-tabs_mobile_y -top-trellis_centered_mobile_base -top-trellis_centered_mobile_y -top-view_mobile_base -top-view_mobile_y -top-view_offset_mobile_base -top-view_offset_mobile_y -translate-y-h_lo_view_mobile_base -translate-y-h_lo_view_mobile_y -translate-y-h_nav_mobile_base -translate-y-h_nav_mobile_y -translate-y-h_tabs_mobile_base -translate-y-h_tabs_mobile_y -translate-y-h_trellis_centered_mobile_base -translate-y-h_trellis_centered_mobile_y -translate-y-h_view_mobile_base -translate-y-h_view_mobile_y -translate-y-h_view_offset_mobile_base -translate-y-h_view_offset_mobile_y active:bg-layer_0_glyph active:bg-layer_0_glyph_a active:bg-layer_0_glyph_hl active:bg-layer_0_glyph_hl_a active:bg-layer_0_glyph_label active:bg-layer_0_glyph_pl active:bg-layer_0_glyph_shade active:bg-layer_0_surface active:bg-layer_0_surface_a active:bg-layer_0_surface_blur active:bg-layer_0_surface_edge active:bg-layer_0_surface_w active:bg-layer_1_glyph active:bg-layer_1_glyph_a active:bg-layer_1_glyph_d active:bg-layer_1_glyph_hl active:bg-layer_1_glyph_hl_a active:bg-layer_1_glyph_label active:bg-layer_1_glyph_pl active:bg-layer_1_glyph_shade active:bg-layer_1_surface active:bg-layer_1_surface_a active:bg-layer_1_surface_edge active:bg-layer_1_surface_err active:bg-layer_1_surface_focus active:bg-layer_2_glyph active:bg-layer_2_glyph_a active:bg-layer_2_glyph_d active:bg-layer_2_glyph_hl active:bg-layer_2_glyph_hl_a active:bg-layer_2_glyph_pl active:bg-layer_2_glyph_shade active:bg-layer_2_surface active:bg-layer_2_surface_a active:bg-layer_2_surface_edge active:border-layer_0_glyph active:border-layer_0_glyph_a active:border-layer_0_glyph_hl active:border-layer_0_glyph_hl_a active:border-layer_0_glyph_label active:border-layer_0_glyph_pl active:border-layer_0_glyph_shade active:border-layer_0_surface active:border-layer_0_surface_a active:border-layer_0_surface_blur active:border-layer_0_surface_edge active:border-layer_0_surface_w active:border-layer_1_glyph active:border-layer_1_glyph_a active:border-layer_1_glyph_d active:border-layer_1_glyph_hl active:border-layer_1_glyph_hl_a active:border-layer_1_glyph_label active:border-layer_1_glyph_pl active:border-layer_1_glyph_shade active:border-layer_1_surface active:border-layer_1_surface_a active:border-layer_1_surface_edge active:border-layer_1_surface_err active:border-layer_1_surface_focus active:border-layer_2_glyph active:border-layer_2_glyph_a active:border-layer_2_glyph_d active:border-layer_2_glyph_hl active:border-layer_2_glyph_hl_a active:border-layer_2_glyph_pl active:border-layer_2_glyph_shade active:border-layer_2_surface active:border-layer_2_surface_a active:border-layer_2_surface_edge active:text-layer_0_glyph active:text-layer_0_glyph_a active:text-layer_0_glyph_hl active:text-layer_0_glyph_hl_a active:text-layer_0_glyph_label active:text-layer_0_glyph_pl active:text-layer_0_glyph_shade active:text-layer_0_surface active:text-layer_0_surface_a active:text-layer_0_surface_blur active:text-layer_0_surface_edge active:text-layer_0_surface_w active:text-layer_1_glyph active:text-layer_1_glyph_a active:text-layer_1_glyph_d active:text-layer_1_glyph_hl active:text-layer_1_glyph_hl_a active:text-layer_1_glyph_label active:text-layer_1_glyph_pl active:text-layer_1_glyph_shade active:text-layer_1_surface active:text-layer_1_surface_a active:text-layer_1_surface_edge active:text-layer_1_surface_err active:text-layer_1_surface_focus active:text-layer_2_glyph active:text-layer_2_glyph_a active:text-layer_2_glyph_d active:text-layer_2_glyph_hl active:text-layer_2_glyph_hl_a active:text-layer_2_glyph_pl active:text-layer_2_glyph_shade active:text-layer_2_surface active:text-layer_2_surface_a active:text-layer_2_surface_edge bg-layer_0_glyph bg-layer_0_glyph_a bg-layer_0_glyph_hl bg-layer_0_glyph_hl_a bg-layer_0_glyph_label bg-layer_0_glyph_pl bg-layer_0_glyph_shade bg-layer_0_surface bg-layer_0_surface_a bg-layer_0_surface_blur bg-layer_0_surface_edge bg-layer_0_surface_w bg-layer_1_glyph bg-layer_1_glyph_a bg-layer_1_glyph_d bg-layer_1_glyph_hl bg-layer_1_glyph_hl_a bg-layer_1_glyph_label bg-layer_1_glyph_pl bg-layer_1_glyph_shade bg-layer_1_surface bg-layer_1_surface_a bg-layer_1_surface_edge bg-layer_1_surface_err bg-layer_1_surface_focus bg-layer_2_glyph bg-layer_2_glyph_a bg-layer_2_glyph_d bg-layer_2_glyph_hl bg-layer_2_glyph_hl_a bg-layer_2_glyph_pl bg-layer_2_glyph_shade bg-layer_2_surface bg-layer_2_surface_a bg-layer_2_surface_edge border-layer_0_glyph border-layer_0_glyph_a border-layer_0_glyph_hl border-layer_0_glyph_hl_a border-layer_0_glyph_label border-layer_0_glyph_pl border-layer_0_glyph_shade border-layer_0_surface border-layer_0_surface_a border-layer_0_surface_blur border-layer_0_surface_edge border-layer_0_surface_w border-layer_1_glyph border-layer_1_glyph_a border-layer_1_glyph_d border-layer_1_glyph_hl border-layer_1_glyph_hl_a border-layer_1_glyph_label border-layer_1_glyph_pl border-layer_1_glyph_shade border-layer_1_surface border-layer_1_surface_a border-layer_1_surface_edge border-layer_1_surface_err border-layer_1_surface_focus border-layer_2_glyph border-layer_2_glyph_a border-layer_2_glyph_d border-layer_2_glyph_hl border-layer_2_glyph_hl_a border-layer_2_glyph_pl border-layer_2_glyph_shade border-layer_2_surface border-layer_2_surface_a border-layer_2_surface_edge bottom-lo_view_mobile_base bottom-lo_view_mobile_y bottom-nav_mobile_base bottom-nav_mobile_y bottom-tabs_mobile_base bottom-tabs_mobile_y bottom-trellis_centered_mobile_base bottom-trellis_centered_mobile_y bottom-view_mobile_base bottom-view_mobile_y bottom-view_offset_mobile_base bottom-view_offset_mobile_y focus:bg-layer_0_glyph focus:bg-layer_0_glyph_a focus:bg-layer_0_glyph_hl focus:bg-layer_0_glyph_hl_a focus:bg-layer_0_glyph_label focus:bg-layer_0_glyph_pl focus:bg-layer_0_glyph_shade focus:bg-layer_0_surface focus:bg-layer_0_surface_a focus:bg-layer_0_surface_blur focus:bg-layer_0_surface_edge focus:bg-layer_0_surface_w focus:bg-layer_1_glyph focus:bg-layer_1_glyph_a focus:bg-layer_1_glyph_d focus:bg-layer_1_glyph_hl focus:bg-layer_1_glyph_hl_a focus:bg-layer_1_glyph_label focus:bg-layer_1_glyph_pl focus:bg-layer_1_glyph_shade focus:bg-layer_1_surface focus:bg-layer_1_surface_a focus:bg-layer_1_surface_edge focus:bg-layer_1_surface_err focus:bg-layer_1_surface_focus focus:bg-layer_2_glyph focus:bg-layer_2_glyph_a focus:bg-layer_2_glyph_d focus:bg-layer_2_glyph_hl focus:bg-layer_2_glyph_hl_a focus:bg-layer_2_glyph_pl focus:bg-layer_2_glyph_shade focus:bg-layer_2_surface focus:bg-layer_2_surface_a focus:bg-layer_2_surface_edge focus:border-layer_0_glyph focus:border-layer_0_glyph_a focus:border-layer_0_glyph_hl focus:border-layer_0_glyph_hl_a focus:border-layer_0_glyph_label focus:border-layer_0_glyph_pl focus:border-layer_0_glyph_shade focus:border-layer_0_surface focus:border-layer_0_surface_a focus:border-layer_0_surface_blur focus:border-layer_0_surface_edge focus:border-layer_0_surface_w focus:border-layer_1_glyph focus:border-layer_1_glyph_a focus:border-layer_1_glyph_d focus:border-layer_1_glyph_hl focus:border-layer_1_glyph_hl_a focus:border-layer_1_glyph_label focus:border-layer_1_glyph_pl focus:border-layer_1_glyph_shade focus:border-layer_1_surface focus:border-layer_1_surface_a focus:border-layer_1_surface_edge focus:border-layer_1_surface_err focus:border-layer_1_surface_focus focus:border-layer_2_glyph focus:border-layer_2_glyph_a focus:border-layer_2_glyph_d focus:border-layer_2_glyph_hl focus:border-layer_2_glyph_hl_a focus:border-layer_2_glyph_pl focus:border-layer_2_glyph_shade focus:border-layer_2_surface focus:border-layer_2_surface_a focus:border-layer_2_surface_edge focus:text-layer_0_glyph focus:text-layer_0_glyph_a focus:text-layer_0_glyph_hl focus:text-layer_0_glyph_hl_a focus:text-layer_0_glyph_label focus:text-layer_0_glyph_pl focus:text-layer_0_glyph_shade focus:text-layer_0_surface focus:text-layer_0_surface_a focus:text-layer_0_surface_blur focus:text-layer_0_surface_edge focus:text-layer_0_surface_w focus:text-layer_1_glyph focus:text-layer_1_glyph_a focus:text-layer_1_glyph_d focus:text-layer_1_glyph_hl focus:text-layer_1_glyph_hl_a focus:text-layer_1_glyph_label focus:text-layer_1_glyph_pl focus:text-layer_1_glyph_shade focus:text-layer_1_surface focus:text-layer_1_surface_a focus:text-layer_1_surface_edge focus:text-layer_1_surface_err focus:text-layer_1_surface_focus focus:text-layer_2_glyph focus:text-layer_2_glyph_a focus:text-layer_2_glyph_d focus:text-layer_2_glyph_hl focus:text-layer_2_glyph_hl_a focus:text-layer_2_glyph_pl focus:text-layer_2_glyph_shade focus:text-layer_2_surface focus:text-layer_2_surface_a focus:text-layer_2_surface_edge group-active:bg-layer_0_glyph group-active:bg-layer_0_glyph_a group-active:bg-layer_0_glyph_hl group-active:bg-layer_0_glyph_hl_a group-active:bg-layer_0_glyph_label group-active:bg-layer_0_glyph_pl group-active:bg-layer_0_glyph_shade group-active:bg-layer_0_surface group-active:bg-layer_0_surface_a group-active:bg-layer_0_surface_blur group-active:bg-layer_0_surface_edge group-active:bg-layer_0_surface_w group-active:bg-layer_1_glyph group-active:bg-layer_1_glyph_a group-active:bg-layer_1_glyph_d group-active:bg-layer_1_glyph_hl group-active:bg-layer_1_glyph_hl_a group-active:bg-layer_1_glyph_label group-active:bg-layer_1_glyph_pl group-active:bg-layer_1_glyph_shade group-active:bg-layer_1_surface group-active:bg-layer_1_surface_a group-active:bg-layer_1_surface_edge group-active:bg-layer_1_surface_err group-active:bg-layer_1_surface_focus group-active:bg-layer_2_glyph group-active:bg-layer_2_glyph_a group-active:bg-layer_2_glyph_d group-active:bg-layer_2_glyph_hl group-active:bg-layer_2_glyph_hl_a group-active:bg-layer_2_glyph_pl group-active:bg-layer_2_glyph_shade group-active:bg-layer_2_surface group-active:bg-layer_2_surface_a group-active:bg-layer_2_surface_edge group-active:border-layer_0_glyph group-active:border-layer_0_glyph_a group-active:border-layer_0_glyph_hl group-active:border-layer_0_glyph_hl_a group-active:border-layer_0_glyph_label group-active:border-layer_0_glyph_pl group-active:border-layer_0_glyph_shade group-active:border-layer_0_surface group-active:border-layer_0_surface_a group-active:border-layer_0_surface_blur group-active:border-layer_0_surface_edge group-active:border-layer_0_surface_w group-active:border-layer_1_glyph group-active:border-layer_1_glyph_a group-active:border-layer_1_glyph_d group-active:border-layer_1_glyph_hl group-active:border-layer_1_glyph_hl_a group-active:border-layer_1_glyph_label group-active:border-layer_1_glyph_pl group-active:border-layer_1_glyph_shade group-active:border-layer_1_surface group-active:border-layer_1_surface_a group-active:border-layer_1_surface_edge group-active:border-layer_1_surface_err group-active:border-layer_1_surface_focus group-active:border-layer_2_glyph group-active:border-layer_2_glyph_a group-active:border-layer_2_glyph_d group-active:border-layer_2_glyph_hl group-active:border-layer_2_glyph_hl_a group-active:border-layer_2_glyph_pl group-active:border-layer_2_glyph_shade group-active:border-layer_2_surface group-active:border-layer_2_surface_a group-active:border-layer_2_surface_edge group-active:text-layer_0_glyph group-active:text-layer_0_glyph_a group-active:text-layer_0_glyph_hl group-active:text-layer_0_glyph_hl_a group-active:text-layer_0_glyph_label group-active:text-layer_0_glyph_pl group-active:text-layer_0_glyph_shade group-active:text-layer_0_surface group-active:text-layer_0_surface_a group-active:text-layer_0_surface_blur group-active:text-layer_0_surface_edge group-active:text-layer_0_surface_w group-active:text-layer_1_glyph group-active:text-layer_1_glyph_a group-active:text-layer_1_glyph_d group-active:text-layer_1_glyph_hl group-active:text-layer_1_glyph_hl_a group-active:text-layer_1_glyph_label group-active:text-layer_1_glyph_pl group-active:text-layer_1_glyph_shade group-active:text-layer_1_surface group-active:text-layer_1_surface_a group-active:text-layer_1_surface_edge group-active:text-layer_1_surface_err group-active:text-layer_1_surface_focus group-active:text-layer_2_glyph group-active:text-layer_2_glyph_a group-active:text-layer_2_glyph_d group-active:text-layer_2_glyph_hl group-active:text-layer_2_glyph_hl_a group-active:text-layer_2_glyph_pl group-active:text-layer_2_glyph_shade group-active:text-layer_2_surface group-active:text-layer_2_surface_a group-active:text-layer_2_surface_edge group-focus:bg-layer_0_glyph group-focus:bg-layer_0_glyph_a group-focus:bg-layer_0_glyph_hl group-focus:bg-layer_0_glyph_hl_a group-focus:bg-layer_0_glyph_label group-focus:bg-layer_0_glyph_pl group-focus:bg-layer_0_glyph_shade group-focus:bg-layer_0_surface group-focus:bg-layer_0_surface_a group-focus:bg-layer_0_surface_blur group-focus:bg-layer_0_surface_edge group-focus:bg-layer_0_surface_w group-focus:bg-layer_1_glyph group-focus:bg-layer_1_glyph_a group-focus:bg-layer_1_glyph_d group-focus:bg-layer_1_glyph_hl group-focus:bg-layer_1_glyph_hl_a group-focus:bg-layer_1_glyph_label group-focus:bg-layer_1_glyph_pl group-focus:bg-layer_1_glyph_shade group-focus:bg-layer_1_surface group-focus:bg-layer_1_surface_a group-focus:bg-layer_1_surface_edge group-focus:bg-layer_1_surface_err group-focus:bg-layer_1_surface_focus group-focus:bg-layer_2_glyph group-focus:bg-layer_2_glyph_a group-focus:bg-layer_2_glyph_d group-focus:bg-layer_2_glyph_hl group-focus:bg-layer_2_glyph_hl_a group-focus:bg-layer_2_glyph_pl group-focus:bg-layer_2_glyph_shade group-focus:bg-layer_2_surface group-focus:bg-layer_2_surface_a group-focus:bg-layer_2_surface_edge group-focus:border-layer_0_glyph group-focus:border-layer_0_glyph_a group-focus:border-layer_0_glyph_hl group-focus:border-layer_0_glyph_hl_a group-focus:border-layer_0_glyph_label group-focus:border-layer_0_glyph_pl group-focus:border-layer_0_glyph_shade group-focus:border-layer_0_surface group-focus:border-layer_0_surface_a group-focus:border-layer_0_surface_blur group-focus:border-layer_0_surface_edge group-focus:border-layer_0_surface_w group-focus:border-layer_1_glyph group-focus:border-layer_1_glyph_a group-focus:border-layer_1_glyph_d group-focus:border-layer_1_glyph_hl group-focus:border-layer_1_glyph_hl_a group-focus:border-layer_1_glyph_label group-focus:border-layer_1_glyph_pl group-focus:border-layer_1_glyph_shade group-focus:border-layer_1_surface group-focus:border-layer_1_surface_a group-focus:border-layer_1_surface_edge group-focus:border-layer_1_surface_err group-focus:border-layer_1_surface_focus group-focus:border-layer_2_glyph group-focus:border-layer_2_glyph_a group-focus:border-layer_2_glyph_d group-focus:border-layer_2_glyph_hl group-focus:border-layer_2_glyph_hl_a group-focus:border-layer_2_glyph_pl group-focus:border-layer_2_glyph_shade group-focus:border-layer_2_surface group-focus:border-layer_2_surface_a group-focus:border-layer_2_surface_edge group-focus:text-layer_0_glyph group-focus:text-layer_0_glyph_a group-focus:text-layer_0_glyph_hl group-focus:text-layer_0_glyph_hl_a group-focus:text-layer_0_glyph_label group-focus:text-layer_0_glyph_pl group-focus:text-layer_0_glyph_shade group-focus:text-layer_0_surface group-focus:text-layer_0_surface_a group-focus:text-layer_0_surface_blur group-focus:text-layer_0_surface_edge group-focus:text-layer_0_surface_w group-focus:text-layer_1_glyph group-focus:text-layer_1_glyph_a group-focus:text-layer_1_glyph_d group-focus:text-layer_1_glyph_hl group-focus:text-layer_1_glyph_hl_a group-focus:text-layer_1_glyph_label group-focus:text-layer_1_glyph_pl group-focus:text-layer_1_glyph_shade group-focus:text-layer_1_surface group-focus:text-layer_1_surface_a group-focus:text-layer_1_surface_edge group-focus:text-layer_1_surface_err group-focus:text-layer_1_surface_focus group-focus:text-layer_2_glyph group-focus:text-layer_2_glyph_a group-focus:text-layer_2_glyph_d group-focus:text-layer_2_glyph_hl group-focus:text-layer_2_glyph_hl_a group-focus:text-layer_2_glyph_pl group-focus:text-layer_2_glyph_shade group-focus:text-layer_2_surface group-focus:text-layer_2_surface_a group-focus:text-layer_2_surface_edge h-[12px] h-[16px] h-[17px] h-[18px] h-[20px] h-[22px] h-[24px] h-[28px] h-[36px] h-entry_bold h-entry_guide h-entry_line h-envelope_button h-envelope_top h-line h-line_label h-lo_view_mobile_base h-lo_view_mobile_y h-nav_mobile_base h-nav_mobile_y h-tabs_mobile_base h-tabs_mobile_y h-toast_min h-touch_bold h-touch_guide h-trellis_centered_mobile_base h-trellis_centered_mobile_y h-view_mobile_base h-view_mobile_y h-view_offset_mobile_base h-view_offset_mobile_y pb-h_lo_view_mobile_base pb-h_lo_view_mobile_y pb-h_nav_mobile_base pb-h_nav_mobile_y pb-h_tabs_mobile_base pb-h_tabs_mobile_y pb-h_trellis_centered_mobile_base pb-h_trellis_centered_mobile_y pb-h_view_mobile_base pb-h_view_mobile_y pb-h_view_offset_mobile_base pb-h_view_offset_mobile_y pt-h_lo_view_mobile_base pt-h_lo_view_mobile_y pt-h_nav_mobile_base pt-h_nav_mobile_y pt-h_tabs_mobile_base pt-h_tabs_mobile_y pt-h_trellis_centered_mobile_base pt-h_trellis_centered_mobile_y pt-h_view_mobile_base pt-h_view_mobile_y pt-h_view_offset_mobile_base pt-h_view_offset_mobile_y text-[12px] text-[15px] text-[16px] text-[18px] text-[19px] text-[20px] text-[21px] text-[23px] text-[24px] text-[26px] text-[27px] text-[28px] text-[30px] text-[36px] text-[40px] text-layer_0_glyph text-layer_0_glyph_a text-layer_0_glyph_hl text-layer_0_glyph_hl_a text-layer_0_glyph_label text-layer_0_glyph_pl text-layer_0_glyph_shade text-layer_0_surface text-layer_0_surface_a text-layer_0_surface_blur text-layer_0_surface_edge text-layer_0_surface_w text-layer_1_glyph text-layer_1_glyph_a text-layer_1_glyph_d text-layer_1_glyph_hl text-layer_1_glyph_hl_a text-layer_1_glyph_label text-layer_1_glyph_pl text-layer_1_glyph_shade text-layer_1_surface text-layer_1_surface_a text-layer_1_surface_edge text-layer_1_surface_err text-layer_1_surface_focus text-layer_2_glyph text-layer_2_glyph_a text-layer_2_glyph_d text-layer_2_glyph_hl text-layer_2_glyph_hl_a text-layer_2_glyph_pl text-layer_2_glyph_shade text-layer_2_surface text-layer_2_surface_a text-layer_2_surface_edge top-lo_view_mobile_base top-lo_view_mobile_y top-nav_mobile_base top-nav_mobile_y top-tabs_mobile_base top-tabs_mobile_y top-trellis_centered_mobile_base top-trellis_centered_mobile_y top-view_mobile_base top-view_mobile_y top-view_offset_mobile_base top-view_offset_mobile_y translate-y-h_lo_view_mobile_base translate-y-h_lo_view_mobile_y translate-y-h_nav_mobile_base translate-y-h_nav_mobile_y translate-y-h_tabs_mobile_base translate-y-h_tabs_mobile_y translate-y-h_trellis_centered_mobile_base translate-y-h_trellis_centered_mobile_y translate-y-h_view_mobile_base translate-y-h_view_mobile_y translate-y-h_view_offset_mobile_base translate-y-h_view_offset_mobile_y w-[12px] w-[16px] w-[17px] w-[18px] w-[20px] w-[22px] w-[24px] w-[28px] w-[36px] w-mobile_base w-mobile_y"></div> -\ No newline at end of file diff --git a/apps-lib/src/lib/el/text-area.svelte b/apps-lib/src/lib/el/text-area.svelte @@ -1,98 +0,0 @@ -<script lang="ts"> - import { browser } from "$app/environment"; - import { - type ITextAreaElement, - fmt_cl, - fmt_textarea_value, - kv_basis, - parse_layer, - value_constrain_textarea, - } from "$lib"; - import { onMount } from "svelte"; - - let el: HTMLTextAreaElement | null = null; - - export let value: string = ``; - export let basis: ITextAreaElement; - $: basis = basis; - - $: id = basis.id ? basis.id : null; - $: layer = - typeof basis.layer === `boolean` ? 0 : parse_layer(basis.layer, 1); //@todo - - onMount(async () => { - try { - await kv_init(); - } catch (e) {} - }); - - $: if (basis?.id && basis?.sync && value) { - (async () => { - try { - browser && (await kv_basis.set(basis?.id, value)); - } catch (e) {} - })(); - } - - const kv_init = async (): Promise<void> => { - try { - if (!basis?.id) return; - if (basis?.sync) { - const kv_val = browser && (await kv_basis.get(basis?.id)); - if (kv_val && el) el.value = fmt_textarea_value(kv_val); - else browser && (await kv_basis.set(basis?.id, ``)); - } - if (basis?.on_mount) await basis?.on_mount(el); - } catch (e) { - console.log(`(error) kv_init `, e); - } - }; - - const handle_on_input = async (el: HTMLTextAreaElement): Promise<void> => { - try { - let pass = true; - let val = el?.value; - if (basis?.field && el) { - val = value_constrain_textarea(basis?.field.charset, val); - el.value = fmt_textarea_value(val); - if ( - !basis?.field.validate.test(val) && - basis?.field.validate_keypress - ) { - //@todo set styles - } - pass = basis?.field.validate.test(val); - } - if (basis?.sync) browser && (await kv_basis.set(basis?.id, val)); - if (basis?.callback) await basis?.callback({ value: val, pass }); - } catch (e) { - console.log(`(error) handle_on_input `, e); - } - }; -</script> - -<textarea - bind:this={el} - bind:value - on:input={async ({ currentTarget: el }) => { - await handle_on_input(el); - }} - on:blur={async ({ currentTarget: el }) => { - if (basis.callback_blur) await basis.callback_blur({ el }); - }} - on:focus={async ({ currentTarget: el }) => { - if (basis.callback_focus) await basis.callback_focus({ el }); - }} - on:keydown={async (ev) => { - if (basis.callback_keydown) - await basis.callback_keydown({ - key: ev.key, - key_s: ev.key === `Enter`, - el: ev.currentTarget, - }); - }} - {id} - contenteditable="true" - class={`${fmt_cl(basis.classes)} el-textarea w-full bg-layer-${layer}-surface text-layer-${layer}-glyph placeholder:text-layer-${layer}-glyph_pl caret-layer-${layer}-glyph`} - placeholder={basis.placeholder || ``} -></textarea> diff --git a/apps-lib/src/lib/el/toast.svelte b/apps-lib/src/lib/el/toast.svelte @@ -1,55 +0,0 @@ -<script lang="ts"> - import { - app_layout, - fmt_cl, - get_layout, - Glyph, - parse_layer, - toast_layout_map, - toast_style_map, - type IToast, - type IToastKind, - } from "$lib"; - - export let basis: IToast; - $: basis = basis; - - let styles: IToastKind[] = basis.styles || [`simple`]; - $: styles = styles; - $: layer = basis.layer ? parse_layer(basis.layer) : 1; - $: layout = get_layout($app_layout); -</script> - -<div - class={`${fmt_cl(toast_layout_map.get(layout))} z-[1000] h-[100vh] toast w-full ${basis.position || `top-center`} `} -> - <div class={`flex flex-row w-full h-max justify-center pb-2`}> - <div - class={`${fmt_cl(basis.classes)} relative grid grid-cols-12 h-max items-center justify-center ${styles.includes(`simple`) ? `bg-layer-${layer}-surface` : ``} ${fmt_cl(styles.map((style) => fmt_cl(toast_style_map.get(style)?.outer)).join(` `))}`} - > - <div - class={`absolute top-0 left-4 flex flex-row h-full items-center text-layer-${layer}-glyph`} - > - <Glyph - basis={{ - key: `info`, - weight: `regular`, - dim: `md`, - ...basis.glyph, - }} - ></Glyph> - </div> - <div - class={`col-span-12 flex flex-row pl-1 ${fmt_cl(styles.map((style) => fmt_cl(toast_style_map.get(style)?.inner)).join(` `))}`} - > - {#if `value` in basis.label} - <p - class={`font-sans font-[500] truncate text-layer-${layer}-glyph -translate-y-[1px]`} - > - {basis.label.value} - </p> - {/if} - </div> - </div> - </div> -</div> diff --git a/apps-lib/src/lib/feature/image-upload-add-photo.svelte b/apps-lib/src/lib/feature/image-upload-add-photo.svelte @@ -1,36 +0,0 @@ -<script lang="ts"> - import { Glyph, lls, type CallbackPromiseReturn } from "$lib"; - - export let bv_photo_path: string; - - export let basis: { - lc_handle_photo_add: CallbackPromiseReturn<string | undefined>; - }; -</script> - -<div class={`relative flex flex-row w-full justify-center items-center`}> - <button - class={`flex flex-row h-[5rem] w-[5rem] justify-center items-center bg-layer-1-surface/60 rounded-full`} - on:click={async () => { - const photo_path_add = await basis.lc_handle_photo_add(); - if (photo_path_add) bv_photo_path = photo_path_add; - }} - > - <Glyph - basis={{ - classes: `text-[40px] text-layer-2-glyph`, - dim: `sm`, - key: `camera`, - }} - ></Glyph> - <div - class={`absolute -bottom-[1.8rem] flex flex-row justify-start items-center`} - > - <p - class={`font-arch font-[600] text-sm text-layer-0-glyph capitalize`} - > - {`${$lls(`icu.add_*`, { value: `${$lls(`common.photo`)}` })}`} - </p> - </div> - </button> -</div> diff --git a/apps-lib/src/lib/feature/search-result-container.svelte b/apps-lib/src/lib/feature/search-result-container.svelte @@ -1,16 +0,0 @@ -<script lang="ts"> - import type { CallbackPromise } from "$lib"; - - export let basis: { - callback: CallbackPromise; - }; -</script> - -<button - on:click={async () => { - await basis.callback(); - }} - class={`group flex flex-row h-[4rem] w-full px-4 justify-between items-center bg-layer-1-surface round-24 layer-1-active-surface el-re`} -> - <slot /> -</button> diff --git a/apps-lib/src/lib/feature/search-result-display.svelte b/apps-lib/src/lib/feature/search-result-display.svelte @@ -1,216 +0,0 @@ -<script lang="ts"> - import { - ascii, - Glyph, - lls, - SearchResultContainer, - type ISearchResultDisplayCallbacks, - type SearchServiceResult, - } from "$lib"; - - export let basis: ISearchResultDisplayCallbacks & { - result: SearchServiceResult; - }; -</script> - -{#if `location_gcs` in basis.result && basis.result.location_gcs.id} - <SearchResultContainer - basis={{ - callback: async () => { - await basis.lc_handle_result_location_gcs( - basis.result.location_gcs, - ); - }, - }} - > - <div class={`flex flex-row gap-4 justify-start items-center`}> - <div - class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-stone-500 round-24`} - > - <Glyph - basis={{ - classes: `text-white`, - dim: `xs`, - weight: `bold`, - key: `compass`, - }} - ></Glyph> - </div> - <div class={`flex flex-row gap-1 justify-start items-center`}> - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {`${$lls(`common.location`)}`} - </p> - </div> - <p - class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`} - > - {ascii.bullet} - </p> - - {#if basis.result.location_gcs.kind === `farm_land`} - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-lime-500 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {`${$lls(`common.farm_land`)}`} - </p> - </div> - {/if} - </div> - </div> - <div - class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`} - > - {#if basis.result.location_gcs.label} - <p - class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph`} - > - {`${basis.result.location_gcs.label}`} - </p> - {:else} - <p - class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph truncate`} - > - {`${basis.result.location_gcs.gc_name}, ${basis.result.location_gcs.gc_admin1_id}`} - </p> - {/if} - </div> - </SearchResultContainer> -{:else if `nostr_profile` in basis && basis.result.nostr_profile.id} - <SearchResultContainer - basis={{ - callback: async () => { - await basis.lc_handle_result_nostr_profile( - basis.result.nostr_profile, - ); - }, - }} - > - <div class={`flex flex-row gap-4 justify-start items-center`}> - <div - class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`} - > - <Glyph - basis={{ - classes: `text-white`, - dim: `xs`, - weight: `bold`, - key: `user`, - }} - ></Glyph> - </div> - <div class={`flex flex-row gap-1 justify-start items-center`}> - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {`${$lls(`common.profile`)}`} - </p> - </div> - <p - class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`} - > - {ascii.bullet} - </p> - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-purple-400 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {#if basis.result.result_k === `name`} - {`${$lls(`common.name`)}`} - {:else} - {`@todo`} - {/if} - </p> - </div> - </div> - </div> - <div - class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`} - > - {#if basis.result.nostr_profile.name} - <p - class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`} - > - {#if basis.result.result_k === `name`} - {`${basis.result.nostr_profile.name}`} - {:else if basis.result.result_v} - {`${basis.result.result_v}`} - {:else} - {`@todo`} - {/if} - </p> - {/if} - </div> - </SearchResultContainer> -{:else if `nostr_relay` in basis && basis.result.nostr_relay.id} - <SearchResultContainer - basis={{ - callback: async () => { - await basis.lc_handle_result_nostr_relay( - basis.result.nostr_relay, - ); - }, - }} - > - <div class={`flex flex-row gap-4 justify-start items-center`}> - <div - class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`} - > - <Glyph - basis={{ - classes: `text-white`, - dim: `xs`, - weight: `bold`, - key: `user`, - }} - ></Glyph> - </div> - <div class={`flex flex-row gap-1 justify-start items-center`}> - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {`${$lls(`common.relay`)}`} - </p> - </div> - <p - class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`} - > - {ascii.bullet} - </p> - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-yellow-400 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {`${$lls(`common.url`)}`} - </p> - </div> - </div> - </div> - <div - class={`flex flex-row flex-grow pr-2 justify-end items-center overflow-hidden`} - > - <p - class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`} - > - {`${basis.result.nostr_relay.url}`} - </p> - </div> - </SearchResultContainer> -{/if} diff --git a/apps-lib/src/lib/features/farm/farms-add-casli-detail.svelte b/apps-lib/src/lib/features/farm/farms-add-casli-detail.svelte @@ -0,0 +1,73 @@ +<script lang="ts"> + import { CarouselItem, FormLineLedger, lls } from "$root"; + import { area_units, form_fields } from "@radroots/util"; + + let { + val_farmname = $bindable(``), + val_farmarea = $bindable(``), + val_farmarea_unit = $bindable(``), + val_farmcontact = $bindable(``), + farm_geop_lat, + farm_geop_lng, + }: { + val_farmname: string; + val_farmarea: string; + val_farmarea_unit: string; + val_farmcontact: string; + farm_geop_lat: string; + farm_geop_lng: string; + } = $props(); +</script> + +<CarouselItem> + <div + class={`flex flex-col h-[100vh] w-full px-6 gap-4 justify-start items-center`} + > + <FormLineLedger + basis={{ + id: `farm_location`, + label: `${$lls(`common.farm_location`)}`, + display_value: `${farm_geop_lat}, ${farm_geop_lng}`, + }} + /> + <FormLineLedger + bind:value={val_farmname} + basis={{ + id: `farm_name`, + label: `${$lls(`common.farm_name`)}`, + input: { + placeholder: `${$lls(`icu.enter_*`, { value: `${$lls(`common.farm_name`)}`.toLowerCase() })}`, + }, + }} + /> + <FormLineLedger + bind:value={val_farmarea} + bind:value_label_sel={val_farmarea_unit} + basis={{ + id: `farm_size`, + label: `${$lls(`common.farm_size`)}`, + label_select: { + label: `${$lls(`measurement.area.${val_farmarea_unit}_ab`)}`, + entries: area_units.map((i) => ({ + value: i, + label: `${$lls(`measurement.area.${i}`)}`, + })), + }, + input: { + placeholder: `${`${$lls(`icu.enter_*`, { value: `${$lls(`common.farm_size`)}`.toLowerCase() })}`} ${`${$lls(`measurement.area.${val_farmarea_unit}_pl`)}`.toLowerCase()}`, + field: form_fields.farm_size, + }, + }} + /> + <FormLineLedger + bind:value={val_farmcontact} + basis={{ + id: `farm_contact`, + label: `${$lls(`common.farm_contact`)}`, + input: { + placeholder: `${$lls(`icu.enter_*`, { value: `${$lls(`common.contact_name`)}`.toLowerCase() })}`, + }, + }} + /> + </div> +</CarouselItem> diff --git a/apps-lib/src/lib/features/farm/farms-add-casli-map.svelte b/apps-lib/src/lib/features/farm/farms-add-casli-map.svelte @@ -0,0 +1,86 @@ +<script lang="ts"> + import { + CarouselItem, + Map, + MapMarkerArea, + WrapBorder, + handle_err, + } from "$root"; + import { + type GeocoderReverseResult, + type GeolocationPoint, + type LcGeocodeCallback, + type LcGeocodeCurrentCallback, + } from "@radroots/util"; + import { onMount } from "svelte"; + + let { + basis, + map_geoc = $bindable(undefined), + map_geop = $bindable(undefined), + farm_geop_lat, + farm_geop_lng, + }: { + basis: { + lc_geop_current: LcGeocodeCurrentCallback; + lc_geocode: LcGeocodeCallback; + }; + map_geoc: GeocoderReverseResult | undefined; + map_geop: GeolocationPoint | undefined; + farm_geop_lat: string; + farm_geop_lng: string; + } = $props(); + + let map: maplibregl.Map | undefined = $state(undefined); + + onMount(async () => { + try { + const geop = await basis.lc_geop_current(); + if (!geop) return; + map_geop = { ...geop }; + const geoc = await basis.lc_geocode(geop); + if (!geoc) return; + map_geoc = geoc; + if (map) map.setCenter([map_geop.lng, map_geop.lat]); + } catch (e) { + handle_err(e, `on_mount`); + } + }); +</script> + +<CarouselItem> + <div + class={`flex flex-col h-[100vh] w-full px-6 gap-4 justify-start items-center`} + > + <WrapBorder basis={{ classes: `h-[24rem] m_1:h-[80%]` }}> + <Map bind:map> + {#if map_geop} + <MapMarkerArea + bind:map_geop + bind:map_geoc + basis={{ + lc_geocode: basis.lc_geocode, + show_display: true, + }} + /> + {/if} + </Map> + </WrapBorder> + {#if map_geop} + <div + class={`flex flex-col w-full gap-1 justify-center items-center`} + > + <div + class={`flex flex-row w-full gap-2 justify-center items-center`} + > + <p class={`font-sans font-[500] text-layer-0-glyph`}> + {farm_geop_lat} + </p> + <p class={`font-sans font-[500] text-layer-0-glyph`}> + {farm_geop_lng} + </p> + </div> + </div> + {/if} + </div> +</CarouselItem> diff --git a/apps-lib/src/lib/features/farm/farms-display-li-el.svelte b/apps-lib/src/lib/features/farm/farms-display-li-el.svelte @@ -0,0 +1,122 @@ +<script lang="ts"> + import { liblocale, lls, Map, MapMarkerArea } from "$root"; + import { + geol_lat_fmt, + geol_lng_fmt, + lib_address_fmt, + parse_geom_point_tup, + parse_tup_geop_point, + type CallbackPromiseGeneric, + type GeolocationPointTuple, + type LcGeocodeCallback, + type ResolveFarmInfo, + } from "@radroots/util"; + import { onMount } from "svelte"; + + let { + basis, + lc_geocode, + lc_handle_farm_view, + }: { + basis: ResolveFarmInfo; + lc_geocode: LcGeocodeCallback; + lc_handle_farm_view: CallbackPromiseGeneric<string>; + } = $props(); + + let map: maplibregl.Map | undefined = $state(undefined); + let map_center: GeolocationPointTuple = $state([0, 0]); + + onMount(async () => { + if (map && basis.geolocation?.point) { + map_center = parse_geom_point_tup(basis.geolocation.point); + map.setCenter(map_center); + } else { + //@todo + } + }); + + const map_geop = $derived(parse_tup_geop_point(map_center)); + + const farm_addr_fmt = $derived( + basis.geolocation?.address + ? lib_address_fmt(basis.geolocation.address) + : ``, + ); + + const farm_geop_lat = $derived( + basis.geolocation?.point?.coordinates + ? geol_lat_fmt( + basis.geolocation.point.coordinates[1], + `dms`, + $liblocale, + 3, + ) + : ``, + ); + + const farm_geop_lng = $derived( + basis.geolocation?.point?.coordinates + ? geol_lng_fmt( + basis.geolocation.point.coordinates[0], + `dms`, + $liblocale, + 3, + ) + : ``, + ); +</script> + +<button + class={`z-10 relative flex flex-col w-full p-4 gap-3 justify-start items-center bg-layer-1-surface layer-1-active-raise-less layer-1-active-ring rounded-3xl el-re`} + onclick={async () => { + if (basis.id) await lc_handle_farm_view(basis.id); + }} +> + <div class={`flex flex-col w-full gap-2 justify-center items-center`}> + <div class={`flex flex-row w-full justify-between items-center`}> + <p class={`font-sans font-[500] text-3xl text-layer-0-glyph`}> + {basis.name} + </p> + + <div + class={`flex flex-row h-6 px-2 py-1 justify-center items-center bg-lime-400 rounded-lg`} + > + <p class={`font-sans font-[700] text-white`}> + {`${$lls(`common.farm`)}`} + </p> + </div> + </div> + <div class={`flex flex-col w-full justify-center items-center`}> + <div class={`flex flex-row w-full justify-start items-center`}> + <p class={`font-sans font-[500] text-lg text-layer-0-glyph`}> + {farm_addr_fmt} + </p> + </div> + <div class={`flex flex-row w-full justify-start items-center`}> + <p class={`font-sans font-[500] text-lg text-layer-0-glyph`}> + {farm_geop_lat && farm_geop_lng + ? `${farm_geop_lat}, ${farm_geop_lng}` + : ``} + </p> + </div> + </div> + </div> + <div + class={`flex flex-col h-[16rem] w-full justify-center items-center rounded-2xl overflow-hidden`} + > + <Map + bind:map + basis={{ + interactive: false, + }} + > + <MapMarkerArea + {map_geop} + basis={{ + no_drag: true, + lc_geocode, + }} + /> + </Map> + </div> +</button> diff --git a/apps-lib/src/lib/features/farm/farms-products-review-card.svelte b/apps-lib/src/lib/features/farm/farms-products-review-card.svelte @@ -0,0 +1,121 @@ +<script lang="ts"> + import { + app_lo, + Glyph, + ImagePath, + lib_parse_currency_marker, + lls, + } from "$root"; + import { + ascii, + type IViewFarmsProductsAddSubmission, + } from "@radroots/util"; + + let { + basis, + }: { + basis: { + data: IViewFarmsProductsAddSubmission | undefined; + }; + } = $props(); + + //@todo +</script> + +<div + class={`flex flex-col h-[20rem] w-lo_${$app_lo} justify-start items-start rounded-touch bg-layer-1-surface overflow-hidden`} +> + <div class={`flex flex-row h-[10rem] w-full justify-center items-center`}> + {#if basis.data?.photos.length} + <ImagePath basis={{ path: basis.data.photos[0] }} /> + {:else} + <div + class={`flex flex-row h-full w-full justify-center items-center bg-layer-2-surface`} + > + <div class={`flex flex-col justify-start items-center`}> + <Glyph + basis={{ + classes: `text-layer-0-glyph`, + dim: `sm`, + key: `image-broken`, + }} + /> + <p + class={`font-sans font-[400] text-sm text-layer-0-glyph`} + > + {`No photo`} + </p> + </div> + </div> + {/if} + </div> + <div + class={`flex flex-col h-[10rem] w-full px-3 py-2 justify-start items-center`} + > + {#if basis.data} + <div class={`flex flex-row w-full justify-between items-center`}> + <div class={`flex flex-row gap-1 justify-start items-center`}> + <p + class={`font-sans font-[600] text-xl text-th-black capitalize`} + > + {basis.data.product} + </p> + </div> + <div + class={`flex flex-row gap-[2px] justify-start items-center`} + > + <p class={`font-sans font-[600] text-xl text-th-black`}> + {`${lib_parse_currency_marker(basis.data.price_currency)}${basis.data.price_amount}`} + </p> + <p class={`font-sans font-[600] text-xl text-th-black`}> + {`/`} + </p> + <p class={`font-sans font-[600] text-xl text-th-black`}> + {`${$lls(`measurement.mass.unit.${basis.data.price_quantity_unit}_ab`)}`} + </p> + </div> + </div> + <div class={`flex flex-row w-full justify-between items-center`}> + <div class={`flex flex-row gap-1 justify-start items-center`}> + <p + class={`font-sans font-[600] text-lg text-layer-1-glyph capitalize`} + > + {basis.data.process} + </p> + <p + class={`font-sans font-[600] text-xl text-layer-1-glyph`} + > + {ascii.bullet} + </p> + <p + class={`font-sans font-[600] text-lg text-layer-1-glyph`} + > + {`${basis.data.quantity_amount} ${$lls(`measurement.mass.unit.${basis.data.quantity_unit}_ab`)} ${basis.data.quantity_label}`} + </p> + </div> + </div> + <div class={`flex flex-row w-full justify-start items-center`}> + <p + class={`font-sans font-[400] text-sm text-th-black capitalize line-clamp-2 overflow-hidden text-ellipsis`} + > + {basis.data.description} + </p> + </div> + <div + class={`flex flex-row w-full pt-2 justify-between items-center`} + > + <div class={`flex flex-row gap-1 justify-start items-center`}> + <p class={`font-sans font-[600] text-th-black`}> + {`${basis.data.geolocation_address.primary}, ${basis.data.geolocation_address.admin}`} + </p> + <p class={`font-sans font-[600] text-th-black`}> + {ascii.bullet} + </p> + <p class={`font-sans font-[600] text-th-black`}> + {`${basis.data.geolocation_address.country}`} + </p> + </div> + </div> + {/if} + </div> +</div> diff --git a/apps-lib/src/lib/features/search/search-result-container.svelte b/apps-lib/src/lib/features/search/search-result-container.svelte @@ -0,0 +1,21 @@ +<script lang="ts"> + import type { ICb } from "@radroots/util"; + import type { Snippet } from "svelte"; + + let { + basis, + children, + }: { + basis: ICb; + children: Snippet; + } = $props(); +</script> + +<button + onclick={async () => { + await basis.callback(); + }} + class={`group flex flex-row h-[4rem] w-full px-4 justify-between items-center bg-layer-1-surface round-24 layer-1-active-surface el-re`} +> + {@render children()} +</button> diff --git a/apps-lib/src/lib/features/search/search-result-display.svelte b/apps-lib/src/lib/features/search/search-result-display.svelte @@ -0,0 +1,225 @@ +<script lang="ts"> + import { Glyph, lls, SearchResultContainer } from "$root"; + import { + ascii, + type CallbackPromiseGeneric, + type ResolveGeolocationInfo, + type ResolveProfileInfo, + type SearchServiceResult, + } from "@radroots/util"; + + let { + basis, + }: { + basis: { + result: SearchServiceResult; + lc_handle_search_geolocation: CallbackPromiseGeneric<ResolveGeolocationInfo>; + lc_handle_search_profile: CallbackPromiseGeneric<ResolveProfileInfo>; + lc_handle_search_nostr_relay: CallbackPromiseGeneric<{ + id: string; + }>; + }; + } = $props(); +</script> + +{#if `location_gcs` in basis.result && basis.result.location_gcs.id} + <SearchResultContainer + basis={{ + callback: async () => { + await basis.lc_handle_search_geolocation( + basis.result.location_gcs, + ); + }, + }} + > + <div class={`flex flex-row gap-4 justify-start items-center`}> + <div + class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-stone-500 round-24`} + > + <Glyph + basis={{ + classes: `text-white`, + dim: `xs`, + + key: `compass`, + }} + /> + </div> + <div class={`flex flex-row gap-1 justify-start items-center`}> + <div + class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`} + > + <p + class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} + > + {`${$lls(`common.location`)}`} + </p> + </div> + <p + class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`} + > + {ascii.bullet} + </p> + + {#if basis.result.location_gcs.kind === `farm_land`} + <div + class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-lime-500 rounded-md`} + > + <p + class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} + > + {`${$lls(`common.farm_land`)}`} + </p> + </div> + {/if} + </div> + </div> + <div + class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`} + > + {#if basis.result.location_gcs.label} + <p + class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph`} + > + {`${basis.result.location_gcs.label}`} + </p> + {:else} + <p + class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph truncate`} + > + {`${basis.result.location_gcs.gc_name}, ${basis.result.location_gcs.gc_admin1_id}`} + </p> + {/if} + </div> + </SearchResultContainer> +{:else if `nostr_profile` in basis && basis.result.nostr_profile.id} + <SearchResultContainer + basis={{ + callback: async () => { + await basis.lc_handle_search_profile( + basis.result.nostr_profile, + ); + }, + }} + > + <div class={`flex flex-row gap-4 justify-start items-center`}> + <div + class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`} + > + <Glyph + basis={{ + classes: `text-white`, + dim: `xs`, + + key: `user`, + }} + /> + </div> + <div class={`flex flex-row gap-1 justify-start items-center`}> + <div + class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`} + > + <p + class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} + > + {`${$lls(`common.profile`)}`} + </p> + </div> + <p + class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`} + > + {ascii.bullet} + </p> + <div + class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-purple-400 rounded-md`} + > + <p + class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} + > + {#if basis.result.result_k === `name`} + {`${$lls(`common.name`)}`} + {:else} + {`@todo`} + {/if} + </p> + </div> + </div> + </div> + <div + class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`} + > + {#if basis.result.nostr_profile.name} + <p + class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`} + > + {#if basis.result.result_k === `name`} + {`${basis.result.nostr_profile.name}`} + {:else if basis.result.result_v} + {`${basis.result.result_v}`} + {:else} + {`@todo`} + {/if} + </p> + {/if} + </div> + </SearchResultContainer> +{:else if `nostr_relay` in basis && basis.result.nostr_relay.id} + <SearchResultContainer + basis={{ + callback: async () => { + await basis.lc_handle_search_nostr_relay( + basis.result.nostr_relay, + ); + }, + }} + > + <div class={`flex flex-row gap-4 justify-start items-center`}> + <div + class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`} + > + <Glyph + basis={{ + classes: `text-white`, + dim: `xs`, + + key: `user`, + }} + /> + </div> + <div class={`flex flex-row gap-1 justify-start items-center`}> + <div + class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`} + > + <p + class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} + > + {`${$lls(`common.relay`)}`} + </p> + </div> + <p + class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`} + > + {ascii.bullet} + </p> + <div + class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-yellow-400 rounded-md`} + > + <p + class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} + > + {`${$lls(`common.url`)}`} + </p> + </div> + </div> + </div> + <div + class={`flex flex-row flex-grow pr-2 justify-end items-center overflow-hidden`} + > + <p + class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`} + > + {`${basis.result.nostr_relay.url}`} + </p> + </div> + </SearchResultContainer> +{/if} diff --git a/apps-lib/src/lib/index.ts b/apps-lib/src/lib/index.ts @@ -1,98 +1,108 @@ -export { default as ButtonArrow } from "./component/button/button-arrow.svelte" -export { default as ButtonLayoutPair } from "./component/button/button-layout-pair.svelte" -export { default as ButtonLayout } from "./component/button/button-layout.svelte" -export { default as CarouselItem } from "./component/carousel/carousel-item.svelte" -export { default as Carousel } from "./component/carousel/carousel.svelte" -export { default as EntryLine } from "./component/entry/entry-line.svelte" -export { default as EntryWrap } from "./component/entry/entry-wrap.svelte" -export { default as FloatPageButton } from "./component/float/float-page-button.svelte" -export { default as FloatTabs } from "./component/float/float-tabs.svelte" -export { default as GlyphButtonSimple } from "./component/glyph/glyph-button-simple.svelte" -export { default as GlyphButton } from "./component/glyph/glyph-button.svelte" -export { default as GlyphCircle } from "./component/glyph/glyph-circle.svelte" -export { default as GlyphTitleSelectLabel } from "./component/glyph/glyph-title-select-label.svelte" -export { default as LabelDisplay } from "./component/label/label-display.svelte" -export { default as Empty } from "./component/lib/empty.svelte" -export { default as Fade } from "./component/lib/fade.svelte" -export { default as LoadScreen } from "./component/lib/load-screen.svelte" -export { default as LogoCircleSm } from "./component/lib/logo-circle-sm.svelte" -export { default as LogoCircle } from "./component/lib/logo-circle.svelte" -export { default as SplashScreen } from "./component/lib/splash-screen.svelte" -export { default as View } from "./component/lib/view.svelte" -export { default as MapMarkerDot } from "./component/map/map-marker-dot.svelte" -export { default as MapPointDisplay } from "./component/map/map-point-display.svelte" -export { default as MapPointSelect } from "./component/map/map-point-select.svelte" -export { default as MapPopupPointGeolocation } from "./component/map/map-popup-point-geolocation.svelte" -export { default as NavOption } from "./component/nav/nav-option.svelte" -export { default as Nav } from "./component/nav/nav.svelte" -export { default as PageHeader } from "./component/page/page-header.svelte" -export { default as PageToolbar } from "./component/page/page-toolbar.svelte" -export { default as TrellisDefaultLabel } from "./component/trellis/trellis-default-label.svelte" -export { default as TrellisEnd } from "./component/trellis/trellis-end.svelte" -export { default as TrellisInput } from "./component/trellis/trellis-input.svelte" -export { default as TrellisLine } from "./component/trellis/trellis-line.svelte" -export { default as TrellisOffset } from "./component/trellis/trellis-offset.svelte" -export { default as TrellisRowDisplayValue } from "./component/trellis/trellis-row-display-value.svelte" -export { default as TrellisRowLabel } from "./component/trellis/trellis-row-label.svelte" -export { default as TrellisSelect } from "./component/trellis/trellis-select.svelte" -export { default as TrellisTitle } from "./component/trellis/trellis-title.svelte" -export { default as TrellisTouch } from "./component/trellis/trellis-touch.svelte" -export { default as Trellis } from "./component/trellis/trellis.svelte" -export { default as Glyph } from "./el/glyph.svelte" -export { default as ImageBlob } from "./el/image-blob.svelte" -export { default as ImagePath } from "./el/image-path.svelte" -export { default as Input } from "./el/input.svelte" -export { default as LabelSwap } from "./el/label-swap.svelte" -export { default as LoadSymbol } from "./el/load-symbol.svelte" -export { default as SelectMenu } from "./el/select-menu.svelte" -export { default as Select } from "./el/select.svelte" -export { default as Styles } from "./el/styles.svelte" -export { default as TextArea } from "./el/text-area.svelte" -export { default as Toast } from "./el/toast.svelte" -export { default as ImageUploadAddPhoto } from "./feature/image-upload-add-photo.svelte" -export { default as SearchResultContainer } from "./feature/search-result-container.svelte" -export { default as SearchResultDisplay } from "./feature/search-result-display.svelte" -export { default as LayoutOverlayBlur } from "./layout/layout-overlay-blur.svelte" -export { default as LayoutOverlayLoading } from "./layout/layout-overlay-loading.svelte" -export { default as LayoutOverlaySplash } from "./layout/layout-overlay-splash.svelte" -export { default as LayoutStyles } from "./layout/layout-styles.svelte" -export { default as LayoutTrellis } from "./layout/layout-trellis.svelte" -export { default as LayoutView } from "./layout/layout-view.svelte" -export { default as LayoutWindow } from "./layout/layout-window.svelte" -export * from "./locale/i18n" -export * from "./service/search/lib" -export * from "./service/search/types" -export * from "./store/app" -export * from "./store/client" -export * from "./store/component" -export * from "./store/ndk" -export * from "./types/app" -export * from "./types/component" -export * from "./types/el" -export * from "./types/feature" -export * from "./types/interface" -export * from "./types/util" -export * from "./types/view" -export * from "./util/app" -export * from "./util/carousel" -export * from "./util/casl" -export * from "./util/component" -export * from "./util/conf" -export * from "./util/document" -export * from "./util/error" -export * from "./util/geolocation" -export * from "./util/i18n" -export * from "./util/kv" -export * from "./util/styles" -export * from "./util/view" -export { default as FarmLandAdd } from "./view/farm-land-add.svelte" -export { default as FarmLandView } from "./view/farm-land-view.svelte" -export { default as FarmLand } from "./view/farm-land.svelte" +export { default as ButtonHorizontalPairSlide } from "./components/button/button-horizontal-pair-slide.svelte" +export { default as ButtonLabelDashed } from "./components/button/button-label-dashed.svelte" +export { default as ButtonLabelGlyph } from "./components/button/button-label-glyph.svelte" +export { default as ButtonLayoutPair } from "./components/button/button-layout-pair.svelte" +export { default as ButtonLayout } from "./components/button/button-layout.svelte" +export { default as ButtonRound } from "./components/button/button-round.svelte" +export { default as ButtonSimpleGlyph } from "./components/button/button-simple-glyph.svelte" +export { default as ButtonSimple } from "./components/button/button-simple.svelte" +export { default as FloatPageButton } from "./components/button/float-page-button.svelte" +export { default as GlyphButtonSimple } from "./components/button/glyph-button-simple.svelte" +export { default as GlyphButton } from "./components/button/glyph-button.svelte" +export { default as CarouselFlowItem } from "./components/carousel/carousel-flow-item.svelte" +export { default as CarouselFlow } from "./components/carousel/carousel-flow.svelte" +export { default as CarouselItem } from "./components/carousel/carousel-item.svelte" +export { default as Carousel } from "./components/carousel/carousel.svelte" +export { default as EnvelopeLowerFull } from "./components/envelope/envelope-lower-full.svelte" +export { default as MapLocationSelectEnvelope } from "./components/envelope/map-location-select-envelope.svelte" +export { default as EntryLineIdb } from "./components/form/entry-line-idb.svelte" +export { default as EntryLine } from "./components/form/entry-line.svelte" +export { default as EntryMultiline } from "./components/form/entry-multiline.svelte" +export { default as EntrySelect } from "./components/form/entry-select.svelte" +export { default as EntryWrap } from "./components/form/entry-wrap.svelte" +export { default as FormEntryInput } from "./components/form/form-entry-input.svelte" +export { default as FormEntryPriceQuantity } from "./components/form/form-entry-price-quantity.svelte" +export { default as FormEntryPrice } from "./components/form/form-entry-price.svelte" +export { default as FormEntryQuantity } from "./components/form/form-entry-quantity.svelte" +export { default as FormEntrySelectInput } from "./components/form/form-entry-select-input.svelte" +export { default as FormEntrySelect } from "./components/form/form-entry-select.svelte" +export { default as FormEntryTextarea } from "./components/form/form-entry-textarea.svelte" +export { default as FormLineLedgerLabelSelectLabel } from "./components/form/form-line-ledger-label-select-label.svelte" +export { default as FormLineLedgerSelect } from "./components/form/form-line-ledger-select.svelte" +export { default as FormLineLedger } from "./components/form/form-line-ledger.svelte" +export { default as FormReviewDisplay } from "./components/form/form-review-display.svelte" +export { default as TradeFieldDisplayKv } from "./components/form/trade_field_display_kv.svelte" +export { default as GlyphCircle } from "./components/glyph/glyph-circle.svelte" +export { default as Glyph } from "./components/glyph/glyph.svelte" +export { default as LabelDisplay } from "./components/label/label-display.svelte" +export { default as LayoutBottomButton } from "./components/layout/layout-bottom-button.svelte" +export { default as LayoutPage } from "./components/layout/layout-page.svelte" +export { default as LayoutTrellisLine } from "./components/layout/layout-trellis-line.svelte" +export { default as LayoutTrellis } from "./components/layout/layout-trellis.svelte" +export { default as LayoutView } from "./components/layout/layout-view.svelte" +export { default as LayoutWindow } from "./components/layout/layout-window.svelte" +export { default as Css } from "./components/lib/css.svelte" +export { default as Empty } from "./components/lib/empty.svelte" +export { default as Fade } from "./components/lib/fade.svelte" +export { default as ImageBlob } from "./components/lib/image-blob.svelte" +export { default as ImagePath } from "./components/lib/image-path.svelte" +export { default as InputValue } from "./components/lib/input-value.svelte" +export { default as Input } from "./components/lib/input.svelte" +export { default as LabelSwap } from "./components/lib/label-swap.svelte" +export { default as LoadSymbolWhite } from "./components/lib/load-symbol-white.svelte" +export { default as LoadSymbol } from "./components/lib/load-symbol.svelte" +export { default as LogoCircleSm } from "./components/lib/logo-circle-sm.svelte" +export { default as LogoLetters } from "./components/lib/logo-letters.svelte" +export { default as LogoCircle } from "./components/lib/logo_circle.svelte" +export { default as SelectMenu } from "./components/lib/select-menu.svelte" +export { default as Select } from "./components/lib/select.svelte" +export { default as TextArea } from "./components/lib/text-area.svelte" +export { default as Theme } from "./components/lib/theme.svelte" +export { default as Toast } from "./components/lib/toast.svelte" +export { default as WrapBorder } from "./components/lib/wrap-border.svelte" +export { default as MapMarkerAreaDisplay } from "./components/map/map-marker-area-display.svelte" +export { default as MapMarkerArea } from "./components/map/map-marker-area.svelte" +export { default as Map } from "./components/map/map.svelte" +export { default as MarkerIndexedView } from "./components/marker/marker-indexed-view.svelte" +export { default as NavigationTabs } from "./components/navigation/navigation-tabs.svelte" +export { default as PageHeader } from "./components/navigation/page-header.svelte" +export { default as PageToolbar } from "./components/navigation/page-toolbar.svelte" +export { default as TrellisDefaultLabel } from "./components/trellis/trellis-default-label.svelte" +export { default as TrellisEnd } from "./components/trellis/trellis-end.svelte" +export { default as TrellisInput } from "./components/trellis/trellis-input.svelte" +export { default as TrellisLine } from "./components/trellis/trellis-line.svelte" +export { default as TrellisOffset } from "./components/trellis/trellis-offset.svelte" +export { default as TrellisRowDisplayValue } from "./components/trellis/trellis-row-display-value.svelte" +export { default as TrellisRowLabel } from "./components/trellis/trellis-row-label.svelte" +export { default as TrellisSelect } from "./components/trellis/trellis-select.svelte" +export { default as TrellisTitle } from "./components/trellis/trellis-title.svelte" +export { default as TrellisTouch } from "./components/trellis/trellis-touch.svelte" +export { default as Trellis } from "./components/trellis/trellis.svelte" +export { default as ImageUploadAddPhoto } from "./components/upload/image-upload-add-photo.svelte" +export { default as ImageUploadButtonsAspect } from "./components/upload/image-upload-buttons-aspect.svelte" +export { default as ImageUploadSimple } from "./components/upload/image-upload-simple.svelte" +export { default as FarmsAddCasliDetail } from "./features/farm/farms-add-casli-detail.svelte" +export { default as FarmsAddCasliMap } from "./features/farm/farms-add-casli-map.svelte" +export { default as FarmsDisplayLiEl } from "./features/farm/farms-display-li-el.svelte" +export { default as FarmsProductsReviewCard } from "./features/farm/farms-products-review-card.svelte" +export { default as SearchResultContainer } from "./features/search/search-result-container.svelte" +export { default as SearchResultDisplay } from "./features/search/search-result-display.svelte" +export * from "./locale/i18n.js" +export * from "./store/app.js" +export * from "./store/client.js" +export * from "./store/component.js" +export * from "./store/ndk.js" +export * from "./util/carousel.js" +export * from "./util/casl.js" +export * from "./util/idb.js" +export * from "./util/lib.js" +export { default as FarmsAdd } from "./view/farms-add.svelte" +export { default as FarmsProductsAdd } from "./view/farms-products-add.svelte" +export { default as FarmsProductsView } from "./view/farms-products-view.svelte" +export { default as FarmsView } from "./view/farms-view.svelte" +export { default as Farms } from "./view/farms.svelte" export { default as Home } from "./view/home.svelte" export { default as Notifications } from "./view/notifications.svelte" +export { default as Profile } from "./view/profile.svelte" export { default as Search } from "./view/search.svelte" -export { default as SettingsNostr } from "./view/settings-nostr.svelte" -export { default as SettingsProfileEdit } from "./view/settings-profile-edit.svelte" -export { default as SettingsProfile } from "./view/settings-profile.svelte" export { default as Settings } from "./view/settings.svelte" - diff --git a/apps-lib/src/lib/layout/layout-overlay-blur.svelte b/apps-lib/src/lib/layout/layout-overlay-blur.svelte @@ -1,13 +0,0 @@ -<script lang="ts"> - import { app_blur, Empty } from "$lib"; -</script> - -<div - class={`z-10 absolute top-0 left-0 modal modal-bottom ${$app_blur ? `modal-open` : ``} h-[100vh] w-full m-0 p-0 backdrop-blur-sm overflow-y-hidden el-re`} -> - <div - class={`modal-box h-full w-full m-0 p-0 bg-transparent overflow-hidden el-re`} - > - <Empty></Empty> - </div> -</div> diff --git a/apps-lib/src/lib/layout/layout-overlay-loading.svelte b/apps-lib/src/lib/layout/layout-overlay-loading.svelte @@ -1,7 +0,0 @@ -<script lang="ts"> - import { app_loading, LoadScreen } from "$lib"; -</script> - -{#if $app_loading} - <LoadScreen /> -{/if} diff --git a/apps-lib/src/lib/layout/layout-overlay-splash.svelte b/apps-lib/src/lib/layout/layout-overlay-splash.svelte @@ -1,7 +0,0 @@ -<script lang="ts"> - import { app_splash, SplashScreen } from "$lib"; -</script> - -{#if $app_splash} - <SplashScreen /> -{/if} diff --git a/apps-lib/src/lib/layout/layout-styles.svelte b/apps-lib/src/lib/layout/layout-styles.svelte @@ -1 +0,0 @@ -<div class="hidden -bottom-lo_view_mobile_base -bottom-lo_view_mobile_y -bottom-nav_mobile_base -bottom-nav_mobile_y -bottom-tabs_mobile_base -bottom-tabs_mobile_y -bottom-trellis_centered_mobile_base -bottom-trellis_centered_mobile_y -bottom-view_mobile_base -bottom-view_mobile_y -bottom-view_offset_mobile_base -bottom-view_offset_mobile_y -top-lo_view_mobile_base -top-lo_view_mobile_y -top-nav_mobile_base -top-nav_mobile_y -top-tabs_mobile_base -top-tabs_mobile_y -top-trellis_centered_mobile_base -top-trellis_centered_mobile_y -top-view_mobile_base -top-view_mobile_y -top-view_offset_mobile_base -top-view_offset_mobile_y -translate-y-h_lo_view_mobile_base -translate-y-h_lo_view_mobile_y -translate-y-h_nav_mobile_base -translate-y-h_nav_mobile_y -translate-y-h_tabs_mobile_base -translate-y-h_tabs_mobile_y -translate-y-h_trellis_centered_mobile_base -translate-y-h_trellis_centered_mobile_y -translate-y-h_view_mobile_base -translate-y-h_view_mobile_y -translate-y-h_view_offset_mobile_base -translate-y-h_view_offset_mobile_y active:bg-layer_0_glyph active:bg-layer_0_glyph_a active:bg-layer_0_glyph_hl active:bg-layer_0_glyph_hl_a active:bg-layer_0_glyph_label active:bg-layer_0_glyph_pl active:bg-layer_0_glyph_shade active:bg-layer_0_surface active:bg-layer_0_surface_a active:bg-layer_0_surface_blur active:bg-layer_0_surface_edge active:bg-layer_0_surface_w active:bg-layer_1_glyph active:bg-layer_1_glyph_a active:bg-layer_1_glyph_d active:bg-layer_1_glyph_hl active:bg-layer_1_glyph_hl_a active:bg-layer_1_glyph_label active:bg-layer_1_glyph_pl active:bg-layer_1_glyph_shade active:bg-layer_1_surface active:bg-layer_1_surface_a active:bg-layer_1_surface_edge active:bg-layer_1_surface_err active:bg-layer_1_surface_focus active:bg-layer_2_glyph active:bg-layer_2_glyph_a active:bg-layer_2_glyph_d active:bg-layer_2_glyph_hl active:bg-layer_2_glyph_hl_a active:bg-layer_2_glyph_pl active:bg-layer_2_glyph_shade active:bg-layer_2_surface active:bg-layer_2_surface_a active:bg-layer_2_surface_edge active:border-layer_0_glyph active:border-layer_0_glyph_a active:border-layer_0_glyph_hl active:border-layer_0_glyph_hl_a active:border-layer_0_glyph_label active:border-layer_0_glyph_pl active:border-layer_0_glyph_shade active:border-layer_0_surface active:border-layer_0_surface_a active:border-layer_0_surface_blur active:border-layer_0_surface_edge active:border-layer_0_surface_w active:border-layer_1_glyph active:border-layer_1_glyph_a active:border-layer_1_glyph_d active:border-layer_1_glyph_hl active:border-layer_1_glyph_hl_a active:border-layer_1_glyph_label active:border-layer_1_glyph_pl active:border-layer_1_glyph_shade active:border-layer_1_surface active:border-layer_1_surface_a active:border-layer_1_surface_edge active:border-layer_1_surface_err active:border-layer_1_surface_focus active:border-layer_2_glyph active:border-layer_2_glyph_a active:border-layer_2_glyph_d active:border-layer_2_glyph_hl active:border-layer_2_glyph_hl_a active:border-layer_2_glyph_pl active:border-layer_2_glyph_shade active:border-layer_2_surface active:border-layer_2_surface_a active:border-layer_2_surface_edge active:text-layer_0_glyph active:text-layer_0_glyph_a active:text-layer_0_glyph_hl active:text-layer_0_glyph_hl_a active:text-layer_0_glyph_label active:text-layer_0_glyph_pl active:text-layer_0_glyph_shade active:text-layer_0_surface active:text-layer_0_surface_a active:text-layer_0_surface_blur active:text-layer_0_surface_edge active:text-layer_0_surface_w active:text-layer_1_glyph active:text-layer_1_glyph_a active:text-layer_1_glyph_d active:text-layer_1_glyph_hl active:text-layer_1_glyph_hl_a active:text-layer_1_glyph_label active:text-layer_1_glyph_pl active:text-layer_1_glyph_shade active:text-layer_1_surface active:text-layer_1_surface_a active:text-layer_1_surface_edge active:text-layer_1_surface_err active:text-layer_1_surface_focus active:text-layer_2_glyph active:text-layer_2_glyph_a active:text-layer_2_glyph_d active:text-layer_2_glyph_hl active:text-layer_2_glyph_hl_a active:text-layer_2_glyph_pl active:text-layer_2_glyph_shade active:text-layer_2_surface active:text-layer_2_surface_a active:text-layer_2_surface_edge bg-layer_0_glyph bg-layer_0_glyph_a bg-layer_0_glyph_hl bg-layer_0_glyph_hl_a bg-layer_0_glyph_label bg-layer_0_glyph_pl bg-layer_0_glyph_shade bg-layer_0_surface bg-layer_0_surface_a bg-layer_0_surface_blur bg-layer_0_surface_edge bg-layer_0_surface_w bg-layer_1_glyph bg-layer_1_glyph_a bg-layer_1_glyph_d bg-layer_1_glyph_hl bg-layer_1_glyph_hl_a bg-layer_1_glyph_label bg-layer_1_glyph_pl bg-layer_1_glyph_shade bg-layer_1_surface bg-layer_1_surface_a bg-layer_1_surface_edge bg-layer_1_surface_err bg-layer_1_surface_focus bg-layer_2_glyph bg-layer_2_glyph_a bg-layer_2_glyph_d bg-layer_2_glyph_hl bg-layer_2_glyph_hl_a bg-layer_2_glyph_pl bg-layer_2_glyph_shade bg-layer_2_surface bg-layer_2_surface_a bg-layer_2_surface_edge border-layer_0_glyph border-layer_0_glyph_a border-layer_0_glyph_hl border-layer_0_glyph_hl_a border-layer_0_glyph_label border-layer_0_glyph_pl border-layer_0_glyph_shade border-layer_0_surface border-layer_0_surface_a border-layer_0_surface_blur border-layer_0_surface_edge border-layer_0_surface_w border-layer_1_glyph border-layer_1_glyph_a border-layer_1_glyph_d border-layer_1_glyph_hl border-layer_1_glyph_hl_a border-layer_1_glyph_label border-layer_1_glyph_pl border-layer_1_glyph_shade border-layer_1_surface border-layer_1_surface_a border-layer_1_surface_edge border-layer_1_surface_err border-layer_1_surface_focus border-layer_2_glyph border-layer_2_glyph_a border-layer_2_glyph_d border-layer_2_glyph_hl border-layer_2_glyph_hl_a border-layer_2_glyph_pl border-layer_2_glyph_shade border-layer_2_surface border-layer_2_surface_a border-layer_2_surface_edge bottom-lo_view_mobile_base bottom-lo_view_mobile_y bottom-nav_mobile_base bottom-nav_mobile_y bottom-tabs_mobile_base bottom-tabs_mobile_y bottom-trellis_centered_mobile_base bottom-trellis_centered_mobile_y bottom-view_mobile_base bottom-view_mobile_y bottom-view_offset_mobile_base bottom-view_offset_mobile_y focus:bg-layer_0_glyph focus:bg-layer_0_glyph_a focus:bg-layer_0_glyph_hl focus:bg-layer_0_glyph_hl_a focus:bg-layer_0_glyph_label focus:bg-layer_0_glyph_pl focus:bg-layer_0_glyph_shade focus:bg-layer_0_surface focus:bg-layer_0_surface_a focus:bg-layer_0_surface_blur focus:bg-layer_0_surface_edge focus:bg-layer_0_surface_w focus:bg-layer_1_glyph focus:bg-layer_1_glyph_a focus:bg-layer_1_glyph_d focus:bg-layer_1_glyph_hl focus:bg-layer_1_glyph_hl_a focus:bg-layer_1_glyph_label focus:bg-layer_1_glyph_pl focus:bg-layer_1_glyph_shade focus:bg-layer_1_surface focus:bg-layer_1_surface_a focus:bg-layer_1_surface_edge focus:bg-layer_1_surface_err focus:bg-layer_1_surface_focus focus:bg-layer_2_glyph focus:bg-layer_2_glyph_a focus:bg-layer_2_glyph_d focus:bg-layer_2_glyph_hl focus:bg-layer_2_glyph_hl_a focus:bg-layer_2_glyph_pl focus:bg-layer_2_glyph_shade focus:bg-layer_2_surface focus:bg-layer_2_surface_a focus:bg-layer_2_surface_edge focus:border-layer_0_glyph focus:border-layer_0_glyph_a focus:border-layer_0_glyph_hl focus:border-layer_0_glyph_hl_a focus:border-layer_0_glyph_label focus:border-layer_0_glyph_pl focus:border-layer_0_glyph_shade focus:border-layer_0_surface focus:border-layer_0_surface_a focus:border-layer_0_surface_blur focus:border-layer_0_surface_edge focus:border-layer_0_surface_w focus:border-layer_1_glyph focus:border-layer_1_glyph_a focus:border-layer_1_glyph_d focus:border-layer_1_glyph_hl focus:border-layer_1_glyph_hl_a focus:border-layer_1_glyph_label focus:border-layer_1_glyph_pl focus:border-layer_1_glyph_shade focus:border-layer_1_surface focus:border-layer_1_surface_a focus:border-layer_1_surface_edge focus:border-layer_1_surface_err focus:border-layer_1_surface_focus focus:border-layer_2_glyph focus:border-layer_2_glyph_a focus:border-layer_2_glyph_d focus:border-layer_2_glyph_hl focus:border-layer_2_glyph_hl_a focus:border-layer_2_glyph_pl focus:border-layer_2_glyph_shade focus:border-layer_2_surface focus:border-layer_2_surface_a focus:border-layer_2_surface_edge focus:text-layer_0_glyph focus:text-layer_0_glyph_a focus:text-layer_0_glyph_hl focus:text-layer_0_glyph_hl_a focus:text-layer_0_glyph_label focus:text-layer_0_glyph_pl focus:text-layer_0_glyph_shade focus:text-layer_0_surface focus:text-layer_0_surface_a focus:text-layer_0_surface_blur focus:text-layer_0_surface_edge focus:text-layer_0_surface_w focus:text-layer_1_glyph focus:text-layer_1_glyph_a focus:text-layer_1_glyph_d focus:text-layer_1_glyph_hl focus:text-layer_1_glyph_hl_a focus:text-layer_1_glyph_label focus:text-layer_1_glyph_pl focus:text-layer_1_glyph_shade focus:text-layer_1_surface focus:text-layer_1_surface_a focus:text-layer_1_surface_edge focus:text-layer_1_surface_err focus:text-layer_1_surface_focus focus:text-layer_2_glyph focus:text-layer_2_glyph_a focus:text-layer_2_glyph_d focus:text-layer_2_glyph_hl focus:text-layer_2_glyph_hl_a focus:text-layer_2_glyph_pl focus:text-layer_2_glyph_shade focus:text-layer_2_surface focus:text-layer_2_surface_a focus:text-layer_2_surface_edge group-active:bg-layer_0_glyph group-active:bg-layer_0_glyph_a group-active:bg-layer_0_glyph_hl group-active:bg-layer_0_glyph_hl_a group-active:bg-layer_0_glyph_label group-active:bg-layer_0_glyph_pl group-active:bg-layer_0_glyph_shade group-active:bg-layer_0_surface group-active:bg-layer_0_surface_a group-active:bg-layer_0_surface_blur group-active:bg-layer_0_surface_edge group-active:bg-layer_0_surface_w group-active:bg-layer_1_glyph group-active:bg-layer_1_glyph_a group-active:bg-layer_1_glyph_d group-active:bg-layer_1_glyph_hl group-active:bg-layer_1_glyph_hl_a group-active:bg-layer_1_glyph_label group-active:bg-layer_1_glyph_pl group-active:bg-layer_1_glyph_shade group-active:bg-layer_1_surface group-active:bg-layer_1_surface_a group-active:bg-layer_1_surface_edge group-active:bg-layer_1_surface_err group-active:bg-layer_1_surface_focus group-active:bg-layer_2_glyph group-active:bg-layer_2_glyph_a group-active:bg-layer_2_glyph_d group-active:bg-layer_2_glyph_hl group-active:bg-layer_2_glyph_hl_a group-active:bg-layer_2_glyph_pl group-active:bg-layer_2_glyph_shade group-active:bg-layer_2_surface group-active:bg-layer_2_surface_a group-active:bg-layer_2_surface_edge group-active:border-layer_0_glyph group-active:border-layer_0_glyph_a group-active:border-layer_0_glyph_hl group-active:border-layer_0_glyph_hl_a group-active:border-layer_0_glyph_label group-active:border-layer_0_glyph_pl group-active:border-layer_0_glyph_shade group-active:border-layer_0_surface group-active:border-layer_0_surface_a group-active:border-layer_0_surface_blur group-active:border-layer_0_surface_edge group-active:border-layer_0_surface_w group-active:border-layer_1_glyph group-active:border-layer_1_glyph_a group-active:border-layer_1_glyph_d group-active:border-layer_1_glyph_hl group-active:border-layer_1_glyph_hl_a group-active:border-layer_1_glyph_label group-active:border-layer_1_glyph_pl group-active:border-layer_1_glyph_shade group-active:border-layer_1_surface group-active:border-layer_1_surface_a group-active:border-layer_1_surface_edge group-active:border-layer_1_surface_err group-active:border-layer_1_surface_focus group-active:border-layer_2_glyph group-active:border-layer_2_glyph_a group-active:border-layer_2_glyph_d group-active:border-layer_2_glyph_hl group-active:border-layer_2_glyph_hl_a group-active:border-layer_2_glyph_pl group-active:border-layer_2_glyph_shade group-active:border-layer_2_surface group-active:border-layer_2_surface_a group-active:border-layer_2_surface_edge group-active:text-layer_0_glyph group-active:text-layer_0_glyph_a group-active:text-layer_0_glyph_hl group-active:text-layer_0_glyph_hl_a group-active:text-layer_0_glyph_label group-active:text-layer_0_glyph_pl group-active:text-layer_0_glyph_shade group-active:text-layer_0_surface group-active:text-layer_0_surface_a group-active:text-layer_0_surface_blur group-active:text-layer_0_surface_edge group-active:text-layer_0_surface_w group-active:text-layer_1_glyph group-active:text-layer_1_glyph_a group-active:text-layer_1_glyph_d group-active:text-layer_1_glyph_hl group-active:text-layer_1_glyph_hl_a group-active:text-layer_1_glyph_label group-active:text-layer_1_glyph_pl group-active:text-layer_1_glyph_shade group-active:text-layer_1_surface group-active:text-layer_1_surface_a group-active:text-layer_1_surface_edge group-active:text-layer_1_surface_err group-active:text-layer_1_surface_focus group-active:text-layer_2_glyph group-active:text-layer_2_glyph_a group-active:text-layer_2_glyph_d group-active:text-layer_2_glyph_hl group-active:text-layer_2_glyph_hl_a group-active:text-layer_2_glyph_pl group-active:text-layer_2_glyph_shade group-active:text-layer_2_surface group-active:text-layer_2_surface_a group-active:text-layer_2_surface_edge group-focus:bg-layer_0_glyph group-focus:bg-layer_0_glyph_a group-focus:bg-layer_0_glyph_hl group-focus:bg-layer_0_glyph_hl_a group-focus:bg-layer_0_glyph_label group-focus:bg-layer_0_glyph_pl group-focus:bg-layer_0_glyph_shade group-focus:bg-layer_0_surface group-focus:bg-layer_0_surface_a group-focus:bg-layer_0_surface_blur group-focus:bg-layer_0_surface_edge group-focus:bg-layer_0_surface_w group-focus:bg-layer_1_glyph group-focus:bg-layer_1_glyph_a group-focus:bg-layer_1_glyph_d group-focus:bg-layer_1_glyph_hl group-focus:bg-layer_1_glyph_hl_a group-focus:bg-layer_1_glyph_label group-focus:bg-layer_1_glyph_pl group-focus:bg-layer_1_glyph_shade group-focus:bg-layer_1_surface group-focus:bg-layer_1_surface_a group-focus:bg-layer_1_surface_edge group-focus:bg-layer_1_surface_err group-focus:bg-layer_1_surface_focus group-focus:bg-layer_2_glyph group-focus:bg-layer_2_glyph_a group-focus:bg-layer_2_glyph_d group-focus:bg-layer_2_glyph_hl group-focus:bg-layer_2_glyph_hl_a group-focus:bg-layer_2_glyph_pl group-focus:bg-layer_2_glyph_shade group-focus:bg-layer_2_surface group-focus:bg-layer_2_surface_a group-focus:bg-layer_2_surface_edge group-focus:border-layer_0_glyph group-focus:border-layer_0_glyph_a group-focus:border-layer_0_glyph_hl group-focus:border-layer_0_glyph_hl_a group-focus:border-layer_0_glyph_label group-focus:border-layer_0_glyph_pl group-focus:border-layer_0_glyph_shade group-focus:border-layer_0_surface group-focus:border-layer_0_surface_a group-focus:border-layer_0_surface_blur group-focus:border-layer_0_surface_edge group-focus:border-layer_0_surface_w group-focus:border-layer_1_glyph group-focus:border-layer_1_glyph_a group-focus:border-layer_1_glyph_d group-focus:border-layer_1_glyph_hl group-focus:border-layer_1_glyph_hl_a group-focus:border-layer_1_glyph_label group-focus:border-layer_1_glyph_pl group-focus:border-layer_1_glyph_shade group-focus:border-layer_1_surface group-focus:border-layer_1_surface_a group-focus:border-layer_1_surface_edge group-focus:border-layer_1_surface_err group-focus:border-layer_1_surface_focus group-focus:border-layer_2_glyph group-focus:border-layer_2_glyph_a group-focus:border-layer_2_glyph_d group-focus:border-layer_2_glyph_hl group-focus:border-layer_2_glyph_hl_a group-focus:border-layer_2_glyph_pl group-focus:border-layer_2_glyph_shade group-focus:border-layer_2_surface group-focus:border-layer_2_surface_a group-focus:border-layer_2_surface_edge group-focus:text-layer_0_glyph group-focus:text-layer_0_glyph_a group-focus:text-layer_0_glyph_hl group-focus:text-layer_0_glyph_hl_a group-focus:text-layer_0_glyph_label group-focus:text-layer_0_glyph_pl group-focus:text-layer_0_glyph_shade group-focus:text-layer_0_surface group-focus:text-layer_0_surface_a group-focus:text-layer_0_surface_blur group-focus:text-layer_0_surface_edge group-focus:text-layer_0_surface_w group-focus:text-layer_1_glyph group-focus:text-layer_1_glyph_a group-focus:text-layer_1_glyph_d group-focus:text-layer_1_glyph_hl group-focus:text-layer_1_glyph_hl_a group-focus:text-layer_1_glyph_label group-focus:text-layer_1_glyph_pl group-focus:text-layer_1_glyph_shade group-focus:text-layer_1_surface group-focus:text-layer_1_surface_a group-focus:text-layer_1_surface_edge group-focus:text-layer_1_surface_err group-focus:text-layer_1_surface_focus group-focus:text-layer_2_glyph group-focus:text-layer_2_glyph_a group-focus:text-layer_2_glyph_d group-focus:text-layer_2_glyph_hl group-focus:text-layer_2_glyph_hl_a group-focus:text-layer_2_glyph_pl group-focus:text-layer_2_glyph_shade group-focus:text-layer_2_surface group-focus:text-layer_2_surface_a group-focus:text-layer_2_surface_edge h-[12px] h-[16px] h-[17px] h-[18px] h-[20px] h-[22px] h-[24px] h-[28px] h-[36px] h-entry_bold h-entry_guide h-entry_line h-envelope_button h-envelope_top h-line h-line_label h-lo_view_mobile_base h-lo_view_mobile_y h-nav_mobile_base h-nav_mobile_y h-tabs_mobile_base h-tabs_mobile_y h-toast_min h-touch_bold h-touch_guide h-trellis_centered_mobile_base h-trellis_centered_mobile_y h-view_mobile_base h-view_mobile_y h-view_offset_mobile_base h-view_offset_mobile_y pb-h_lo_view_mobile_base pb-h_lo_view_mobile_y pb-h_nav_mobile_base pb-h_nav_mobile_y pb-h_tabs_mobile_base pb-h_tabs_mobile_y pb-h_trellis_centered_mobile_base pb-h_trellis_centered_mobile_y pb-h_view_mobile_base pb-h_view_mobile_y pb-h_view_offset_mobile_base pb-h_view_offset_mobile_y pt-h_lo_view_mobile_base pt-h_lo_view_mobile_y pt-h_nav_mobile_base pt-h_nav_mobile_y pt-h_tabs_mobile_base pt-h_tabs_mobile_y pt-h_trellis_centered_mobile_base pt-h_trellis_centered_mobile_y pt-h_view_mobile_base pt-h_view_mobile_y pt-h_view_offset_mobile_base pt-h_view_offset_mobile_y text-[12px] text-[15px] text-[16px] text-[18px] text-[19px] text-[20px] text-[21px] text-[23px] text-[24px] text-[26px] text-[27px] text-[28px] text-[30px] text-[36px] text-[40px] text-layer_0_glyph text-layer_0_glyph_a text-layer_0_glyph_hl text-layer_0_glyph_hl_a text-layer_0_glyph_label text-layer_0_glyph_pl text-layer_0_glyph_shade text-layer_0_surface text-layer_0_surface_a text-layer_0_surface_blur text-layer_0_surface_edge text-layer_0_surface_w text-layer_1_glyph text-layer_1_glyph_a text-layer_1_glyph_d text-layer_1_glyph_hl text-layer_1_glyph_hl_a text-layer_1_glyph_label text-layer_1_glyph_pl text-layer_1_glyph_shade text-layer_1_surface text-layer_1_surface_a text-layer_1_surface_edge text-layer_1_surface_err text-layer_1_surface_focus text-layer_2_glyph text-layer_2_glyph_a text-layer_2_glyph_d text-layer_2_glyph_hl text-layer_2_glyph_hl_a text-layer_2_glyph_pl text-layer_2_glyph_shade text-layer_2_surface text-layer_2_surface_a text-layer_2_surface_edge top-lo_view_mobile_base top-lo_view_mobile_y top-nav_mobile_base top-nav_mobile_y top-tabs_mobile_base top-tabs_mobile_y top-trellis_centered_mobile_base top-trellis_centered_mobile_y top-view_mobile_base top-view_mobile_y top-view_offset_mobile_base top-view_offset_mobile_y translate-y-h_lo_view_mobile_base translate-y-h_lo_view_mobile_y translate-y-h_nav_mobile_base translate-y-h_nav_mobile_y translate-y-h_tabs_mobile_base translate-y-h_tabs_mobile_y translate-y-h_trellis_centered_mobile_base translate-y-h_trellis_centered_mobile_y translate-y-h_view_mobile_base translate-y-h_view_mobile_y translate-y-h_view_offset_mobile_base translate-y-h_view_offset_mobile_y w-[12px] w-[16px] w-[17px] w-[18px] w-[20px] w-[22px] w-[24px] w-[28px] w-[36px] w-mobile_base w-mobile_y"></div> -\ No newline at end of file diff --git a/apps-lib/src/lib/layout/layout-trellis.svelte b/apps-lib/src/lib/layout/layout-trellis.svelte @@ -1,11 +0,0 @@ -<script lang="ts"> - import { fmt_cl, type IBasisOpt, type IClOpt } from "$lib"; - - export let basis: IBasisOpt<IClOpt> = undefined; -</script> - -<div - class={`${fmt_cl(basis?.classes)} flex flex-col pb-12 gap-4 justify-center items-center scroll-hide`} -> - <slot /> -</div> diff --git a/apps-lib/src/lib/layout/layout-view.svelte b/apps-lib/src/lib/layout/layout-view.svelte @@ -1,55 +0,0 @@ -<script lang="ts"> - import { - app_layout, - fmt_cl, - layout_view_cover, - nav_blur, - ph_blur, - tabs_blur, - type IBasisOpt, - type IClOpt, - } from "$lib"; - import { onDestroy, onMount } from "svelte"; - - export let basis: IBasisOpt<IClOpt & { fade?: boolean }> = undefined; - let el: HTMLElement | null; - - /*$: classes_resp = $layout_view_cover - ? `` - : $nav_visible - ? `pt-h_nav_${$app_layout}` - : `pt-h_lo_view_${$app_layout}`; - */ - onMount(async () => { - try { - el?.addEventListener("scroll", scrollChange); - } catch (e) { - } finally { - } - }); - - onDestroy(async () => { - try { - el?.removeEventListener("scroll", scrollChange); - } catch (e) { - } finally { - } - }); - - const scrollChange = (): void => { - if (Math.max(el?.scrollTop || 0, 0) > 10) nav_blur.set(true); - else nav_blur.set(false); - if (Math.max(el?.scrollTop || 0, 0) > 10) tabs_blur.set(true); - else tabs_blur.set(false); - if (Math.max(el?.scrollTop || 0, 0) > 30) ph_blur.set(true); - else ph_blur.set(false); - }; -</script> - -<div - bind:this={el} - class={`${fmt_cl(basis?.classes)} absolute top-0 left-0 flex flex-col h-[100vh] w-full justify-start items-center overflow-y-scroll scroll-hide ${$layout_view_cover ? `` : `pt-h_lo_view_${$app_layout}`}`} - class:fade-in={basis?.fade} -> - <slot /> -</div> diff --git a/apps-lib/src/lib/layout/layout-window.svelte b/apps-lib/src/lib/layout/layout-window.svelte @@ -1,52 +0,0 @@ -<script> - import { - app_layout, - app_tilt, - app_toast, - app_win, - cfg_app, - envelope_tilt, - envelope_visible, - LayoutOverlayBlur, - LayoutOverlayLoading, - LayoutOverlaySplash, - LayoutStyles, - Toast, - } from "$lib"; - import { onMount } from "svelte"; - - onMount(async () => { - try { - app_win.set({ h: window.innerHeight, w: window.innerWidth }); - app_toast.set(false); - } catch (e) { - console.log(`(layout mount) `, e); - } finally { - } - }); - - app_win.subscribe((_app_win) => { - if (_app_win.h > cfg_app.layout.mobile_y.h) app_layout.set(`mobile_y`); - }); - - envelope_visible.subscribe(async (_envelope_visible) => { - if (_envelope_visible && $envelope_tilt) app_tilt.set(true); - else app_tilt.set(false); - }); -</script> - -<div - class={`relative flex flex-col h-[100vh] w-full overflow-x-hidden overflow-y-hidden bg-layer-0-surface ${$app_tilt ? `scale-y-[96%] translate-y-4 rounded-t-[3rem]` : ``} delay-75 duration-200 el-re`} -> - <div class={`flex flex-col h-full w-full`}> - <slot /> - </div> - <slot name="overlay" /> - <LayoutOverlayBlur /> -</div> -<LayoutOverlayLoading /> -<LayoutOverlaySplash /> -<LayoutStyles /> -{#if $app_toast} - <Toast basis={$app_toast} /> -{/if} diff --git a/apps-lib/src/lib/locale/en/common.json b/apps-lib/src/lib/locale/en/common.json @@ -1,4 +1,14 @@ { + "farm_name": "Farm name", + "farm_contact": "Farm contact", + "farm_location": "Farm location", + "farm_size": "Farm size", + "contact_name": "Contact name", + "describe_your_product": "Describe your product", + "add_from_existing": "Add from existing", + "create_product": "Create product", + "logout": "Logout", + "farms": "Farms", "loading": "Loading", "disable_uploads": "Disable uploads", "this_action_is_irreversible": "This action is irreversible", diff --git a/apps-lib/src/lib/locale/en/farm.json b/apps-lib/src/lib/locale/en/farm.json @@ -0,0 +1,15 @@ +{ + "product": { + "validation": { + "key": { + "required": "Enter the product" + }, + "process": { + "required": "Enter the product process" + }, + "summary": { + "required": "Enter the product description" + } + } + } +} +\ No newline at end of file diff --git a/apps-lib/src/lib/locale/en/measurement.json b/apps-lib/src/lib/locale/en/measurement.json @@ -1,11 +1,17 @@ { "area": { "ac": "Acre", + "ac_pl": "Acres", "ac_ab": "Ac.", "ha": "Hectare", + "ha_pl": "Hectares", "ha_ab": "Ha.", "m2": "Square metre", - "m2_ab": "Sq. M." + "m2_pl": "Square metres", + "m2_ab": "Sq. M.", + "ft2": "Square foot", + "ft2_pl": "Square feet", + "ft2_ab": "Sq. Ft." }, "length": { "ft": "Foot", diff --git a/apps-lib/src/lib/locale/en/trade.json b/apps-lib/src/lib/locale/en/trade.json @@ -10,6 +10,13 @@ "err_invalid": "Enter the price" } }, + "default": { + "process": { + "natural": "Natural", + "dried": "Dried", + "roasted": "Roasted" + } + }, "key": { "cacao": { "name": "Cacao", diff --git a/apps-lib/src/lib/locale/i18n.ts b/apps-lib/src/lib/locale/i18n.ts @@ -19,7 +19,7 @@ const config: Config<LanguageConfig> = { translations: libtranslations, parser: parser(), loaders: [ - ...Object.keys(libtranslations).map((locale) => [`common`, `icu`, `measurement`, `model`, `trade`].map(key => ({ + ...Object.keys(libtranslations).map((locale) => [`common`, `farm`, `icu`, `measurement`, `model`, `trade`].map(key => ({ locale, key, loader: async () => (await import(`./${locale}/${key}.json`)).default diff --git a/apps-lib/src/lib/service/search/lib.ts b/apps-lib/src/lib/service/search/lib.ts @@ -1,67 +0,0 @@ -import { type ISearchService, type SearchServiceFlattenedData, type SearchServiceResult } from "$lib"; - -export class SearchService implements ISearchService { - private _flattened_data: SearchServiceFlattenedData[] = []; - private _index_map: Map<string, Record<string, any>[]> = new Map(); - - constructor(data: Record<string, any[]>) { - this.flatten_data(data); - this.index_data(); - } - - private get flattened_data() { - return this._flattened_data; - } - - private flatten_data(data: Record<string, any[]>) { - Object.keys(data).forEach((list_group) => { - const list = data[list_group]; - list.forEach((item) => { - const flattened_item: SearchServiceFlattenedData = { ...item, group: list_group }; - this._flattened_data.push(flattened_item); - }); - }); - } - - private index_data(): void { - this.flattened_data.forEach((item) => { - Object.keys(item).forEach((key) => { - const key_lower = key.toLowerCase(); - const value = item[key]; - if (value != null) { - if (!this._index_map.has(key_lower)) this._index_map.set(key_lower, []); - this._index_map.get(key_lower)?.push(item); - } - }); - }); - } - - public search(query: string): SearchServiceResult[] { - if (!query) return []; - const search_query = query.toLowerCase().trim(); - let results: SearchServiceResult[] = []; - const results_seen = new Set<string>(); - this._index_map.forEach((items) => { - items.forEach((item) => { - for (const [key, value] of Object.entries(item)) { - if (key === `id` || key === `created_at` || key === `updated_at` || key === `public_key` || key === `group`) continue; - if (value && value.toString().replace(/[()_-]/gi, ` `).toLowerCase().includes(search_query)) { - const { group, ...rest } = item; - if (!(`id` in item)) continue; - const result_key = item.id; - if (results_seen.has(result_key)) continue; - results_seen.add(result_key); - const reshaped_result: SearchServiceResult = { - id: item.id, - result_k: key, - result_v: value, - [group]: { ...rest }, - }; - results.push(reshaped_result); - } - }; - }); - }); - return results; - } -} diff --git a/apps-lib/src/lib/service/search/types.ts b/apps-lib/src/lib/service/search/types.ts @@ -1,6 +0,0 @@ -export type SearchServiceResult = Record<string, any> & { id: string, result_k: string; result_v: string; }; -export type SearchServiceFlattenedData = Record<string, any> & { list: string; }; - -export type ISearchService = { - search(input: string): SearchServiceResult[] -}; -\ No newline at end of file diff --git a/apps-lib/src/lib/store/app.ts b/apps-lib/src/lib/store/app.ts @@ -1,8 +1,8 @@ -import type { AppConfigType, AppLayoutKey, IToast } from "$lib"; import type { ColorMode, ThemeKey } from "@radroots/theme"; +import type { AppConfigType, AppLayoutKey, IToast } from "@radroots/util"; import { writable } from "svelte/store"; -export const app_layout = writable<AppLayoutKey>(`mobile_base`); +export const app_lo = writable<AppLayoutKey>(); export const app_cfg_type = writable<AppConfigType>(`personal`); export const app_init = writable<boolean>(false); export const app_tilt = writable<boolean>(false); @@ -15,6 +15,8 @@ export const app_blur = writable<boolean>(false); export const app_db = writable<boolean>(false); export const app_geoc = writable<boolean>(false); export const app_thc = writable<ColorMode>(`light`); -export const app_th = writable<ThemeKey>(`os`); +export const app_th = writable<ThemeKey>(); export const key_nostr = writable<string>(``); export const app_nostr_profiles = writable<string[]>([]); + +export const search_results = writable<any[]>([]); diff --git a/apps-lib/src/lib/store/client.ts b/apps-lib/src/lib/store/client.ts @@ -1,4 +1,4 @@ -import { type NavigationPreviousParam } from "$lib"; +import type { NavigationPreviousParam } from "@radroots/util"; import { writable } from "svelte/store"; import { queryParam, queryParameters } from "sveltekit-search-params"; @@ -6,6 +6,7 @@ export const qp = queryParameters(); export const qp_nostr_pk = queryParam<string>("nostr_pk"); export const qp_rkey = queryParam<string>("rkey"); export const qp_id = queryParam<string>("id"); +export const qp_ref = queryParam<string>("ref"); export const qp_lat = queryParam<string>("lat"); export const qp_lng = queryParam<string>("lng"); diff --git a/apps-lib/src/lib/store/component.ts b/apps-lib/src/lib/store/component.ts @@ -1,4 +1,4 @@ import { writable } from "svelte/store"; -export const casl_index = writable<number>(0); -export const casl_index_max = writable<number>(0); -\ No newline at end of file +export const casl_i = writable<number>(0); +export const casl_imax = writable<number>(0); +\ No newline at end of file diff --git a/apps-lib/src/lib/store/ndk.ts b/apps-lib/src/lib/store/ndk.ts @@ -15,10 +15,10 @@ if (!ndk_client_name) throw new Error('Error: VITE_PUBLIC_NDK_CLIENT_NAME is req let cacheAdapter: NDKCacheAdapter | undefined; if (typeof window !== `undefined`) cacheAdapter = new NDKCacheAdapterDexie({ dbName: ndk_cache_name }); -const _ndk = new NDKSvelte({ +const ndk_svelte = new NDKSvelte({ cacheAdapter, clientName: ndk_client_name, }); -export const ndk = writable<NDKSvelte>(_ndk); +export const ndk = writable<NDKSvelte>(ndk_svelte); export const ndk_user = writable<NDKUser>(); diff --git a/apps-lib/src/lib/types/app.ts b/apps-lib/src/lib/types/app.ts @@ -1,183 +0,0 @@ -//%types% -export type NavigationRoute = string; -//%types% - -export type INavigationRoutePreventRouteNav = { - prevent_route?: { - callback: CallbackPromise; - }; -} - -export type INavigationRoutePreventRoute = { - prevent_route: CallbackPromise; -} -export type INavigationRoute = { - route: NavigationRoute | [NavigationRoute, NavigationParamTuple[]]; -}; - -export type GlyphKey = | - `images-square` | - `bell` | - `columns` | - `bold` | - `article` | - `grid-four` | - `link-simple` | - `seal-check` | - `selection-foreground` | - `image-square` | - `image-broken` | - `funnel` | - `users-three` | - `note-blank` | - `user-circle-plus` | - `user-circle` | - `receipt` | - `invoice` | - `note` | - `arrow-left` | - `arrows-down-up` | - `basket` | - `arrow-right` | - `upload-simple` | - `printer` | - `download-simple` | - `list` | - `asterisk` | - `asterisk-simple` | - `subtitles-slash` | - `cardholder` | - `globe-x` | - `exclamation-mark` | - `network-x` | - `x-circle` | - `address-book-tabs` | - `paper-plane-tilt` | - `note-pencil` | - `share-fat` | - `folder` | - `trash` | - `plus-circle` | - `currency-${GlyphKeyCurrency}` | - `arrow-down` | - `caret-circle-down` | - `caret-circle-up` | - `shopping-bag-open` | - `coffee-bean` | - `compass` | - `map-pin-simple` | - `handbag-simple` | - `devices` | - `lock-key` | - `gear` | - `gear-fine` | - `bell-simple` | - `envelope` | - `house-line` | - `arrows-left-right` | - `list-plus` | - `squares-four` | - `list-plus` | - `app-window` | - `circle-notch` | - `subtract-square` | - `device-tablet-speaker` | - `weather-cloud` | - `warning` | - `circle-notch` | - `minus` | - `key` | - `arrow-u-up-left` | - `arrow-counter-clockwise` | - `circle` | - `check-circle` | - `circle-dashed` | - `dots-three` | - `cards-three` | - `lightning` | - `cards` | - `note-pencil` | - `tray` | - `calendar-dots` | - `notepad` | - `network` | - `calendar-blank` | - `chats-circle` | - `plant` | - `farm` | - `magnifying-glass` | - `chat-circle-dots` | - `dots-three-outline` | - `copy` | - `circles-four` | - `waveform` | - `film-strip` | - `arrow-up` | - `arrow-circle-up` | - `plus` | - `funnel-simple` | - `user` | - `camera` | - `check` | - `file` | - `share-network` | - `question` | - `minus-circle` | - `globe-simple` | - `globe` | - `warning-circle` | - `x` | - `info` | - `caret-${GeometryCardinalDirection}` | - `caret-up-down`; -export type GlyphKeyCurrency = `dollar` | `eur`; - -export type AppConfigType = `farmer` | `personal` -export type AppLayoutKey = 'mobile_base' | 'mobile_y'; - -export type CallbackPromiseFull<Ti, Tr> = (value: Ti) => Promise<Tr>; -export type CallbackPromiseGeneric<T> = (value: T) => Promise<void>; -export type CallbackPromiseReturn<T> = () => Promise<T>; -export type CallbackPromise = () => Promise<void>; - -export type ElementCallbackValue = CallbackPromiseGeneric<{ value: string; pass: boolean; }>; -export type ElementCallbackValueKeydown<T extends HTMLElement> = CallbackPromiseGeneric<{ key: string; key_s: boolean; el: T }>; -export type ElementCallbackValueBlur<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>; -export type ElementCallbackValueFocus<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>; -export type ElementCallbackMount<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>; - -export type EntryStyle = `guide` | `line`; - -export type GeometryScreenPositionHorizontal = `left` | `center` | `right`; -export type GeometryScreenPositionVertical = `top` | `center` | `bottom`; -export type GeometryScreenPosition = `${GeometryScreenPositionVertical}-${GeometryScreenPositionHorizontal}`; -export type GeometryCardinalDirection = `up` | `down` | `left` | `right`; -export type GeometryDimension = - `xs` | - `sm` | - `md` | - `lg` | - `xl`; -export type GeometryGlyphDimension = - | `${GeometryDimension}` - | `${GeometryDimension}-` - | `${GeometryDimension}--` - | `${GeometryDimension}+`; - -export type GlyphWeight = `light` | `regular` | `fill` | `bold`; // `thin` `duotone` - -export type LoadingBlades = 8 | 12; -export type LoadingDimension = GeometryDimension | `glyph-send-button`; //@todo remove - -export type LayerGlyphBasisKind = `_a` | `_d` | `_pl`; - -export type NavigationRouteParamNostrPublicKey = `nostr_pk`; -export type NavigationRouteParamRecordKey = `rkey`; -export type NavigationRouteParamId = `id`; -export type NavigationRouteParamLat = `lat`; -export type NavigationRouteParamLng = `lng`; -export type NavigationRouteParamKey = NavigationRouteParamNostrPublicKey | NavigationRouteParamId | NavigationRouteParamRecordKey | NavigationRouteParamLat | NavigationRouteParamLng; -export type NavigationParamTuple = [NavigationRouteParamKey, string]; -export type NavigationPreviousParam<T extends string> = { route: T, label?: string; params?: NavigationParamTuple[] } - -export type CallbackRoute = CallbackPromise | INavigationRoute; -\ No newline at end of file diff --git a/apps-lib/src/lib/types/component.ts b/apps-lib/src/lib/types/component.ts @@ -1,156 +0,0 @@ -import type { CallbackPromise, CallbackRoute, GlyphKey, ICbG, ICbGOpt, ICbOpt, IClOpt, IDisabledOpt, IGl, IGlOpt, IGlyph, IInput, ILabel, ILabelOpt, ILabelTup, ILoadingOpt, ILy, INavigationRoute, INavigationRoutePreventRouteNav, ISelect } from "$lib"; - -export type IBasisOpt<T extends object> = T | undefined; - -export type IPageToolbar = ICbOpt & { - header?: IPageHeader; -}; - -export type IPageHeader = { - label: string; - callback_route?: CallbackRoute; -}; - -export type IGlyphCircle = { - classes_wrap: string; - glyph: IGlyph -}; - -export type ITrellis = ILy & - IClOpt & - ITrellisStyles & { - id?: string; - view?: string; - title?: ITrellisTitle; - description?: ITrellisDescription; - default_el?: ITrellisDefault; - list?: (ITrellisKind | undefined)[]; - hide_offset?: true; - }; - -export type ITrellisStyles = { - hide_rounded?: boolean; - hide_border_top?: boolean; - hide_border_bottom?: boolean; - set_title_background?: boolean; - set_default_background?: boolean; -}; - -export type ITrellisTitle = ICbOpt & - IClOpt & { - mod?: ITrellisBasisOffsetMod, - value: string | true; - link?: ICbOpt & - IClOpt & - IGlOpt & ILabelOpt; - }; - -export type ITrellisDescription = string | true; - -export type ITrellisBasisOffsetModKey = 'sm' | 'glyph'; -export type ITrellisBasisOffsetMod = ITrellisBasisOffsetModKey | (({ glyph: IGlyph } | { glyph_circle: IGlyphCircle }) & { - loading?: boolean; -}); - -export type ITrellisDefault = { - labels?: ITrellisDefaultLabel[]; - show_title?: boolean; -}; - -export type ITrellisDefaultLabel = ICbOpt & { - label: string; - classes?: string; -}; - -export type ITrellisKind = ( - | ITrellisKindTouch - | ITrellisKindInput - | ITrellisKindSelect -); - -export type ITrellisBasis = { - loading?: boolean; - hide_active?: boolean; - hide_field?: boolean; - offset?: ITrellisBasisOffset; - full_rounded?: boolean; -}; - -export type ITrellisBasisOffset = ICbGOpt<MouseEvent> & - IClOpt & { - mod?: ITrellisBasisOffsetMod; - classes?: string; - hide_space?: boolean; - hide_offset?: boolean; - }; - -export type ITrellisKindDisplay = { - display?: ITrellisKindDisplayValue; -} -export type ITrellisKindDisplayValue = ICbGOpt<MouseEvent> & ILoadingOpt & - (ITrellisKindDisplayValueIcon | ILabel); - - -export type ITrellisKindDisplayValueIcon = { - icon: { - classes?: string; - key: GlyphKey; - }; -}; -export type ITrellisKindTouch = ITrellisBasis & { - touch: ITrellisBasisTouch; -}; - -export type ITrellisBasisTouch = ICbGOpt<MouseEvent> & - ILabelTup & ITrellisKindDisplay & { - end?: ITrellisBasisTouchEnd; - }; - -export type ITrellisKindInput = ITrellisBasis & { - input: ITrellisBasisInput; -}; - -export type ITrellisBasisInput = { - basis: IInput<string>; - line_label?: { - classes?: string; - value: string; - }; - action?: { - visible: boolean; - loading?: boolean; - callback?: CallbackPromise; - glyph?: IGlyph - }; -}; - -export type ITrellisKindSelect = ITrellisBasis & { - select: ITrellisBasisSelect; -}; - -export type ITrellisBasisSelect = ICbGOpt<MouseEvent> & - ILabelTup & ITrellisKindDisplay & ILoadingOpt & { - end?: ITrellisBasisTouchEnd; - el: ISelect & { value: string; }; - }; - -export type ITrellisBasisTouchEnd = ICbGOpt<MouseEvent> & IGl; - -export type INavBasisPrev = IClOpt & ICbG< - HTMLLabelElement | null -> & IGlOpt & ILabelOpt & IDisabledOpt & { - loading?: boolean; -}; -export type INavBasisOption = IClOpt & ICbG< - HTMLLabelElement | null -> & IGlOpt & ILabelOpt & IDisabledOpt & { - loading?: boolean; -}; -export type INavBasis = { - prev: ICbOpt & ILoadingOpt & INavigationRoute & INavigationRoutePreventRouteNav & { - label?: string; - kind?: 'arrow' - }; - title?: ICbOpt & ILabel; - option?: INavBasisOption; -}; diff --git a/apps-lib/src/lib/types/el.ts b/apps-lib/src/lib/types/el.ts @@ -1,56 +0,0 @@ -import type { CallbackPromiseGeneric, ElementCallbackMount, ElementCallbackValue, ElementCallbackValueBlur, ElementCallbackValueKeydown, FormField, GeometryGlyphDimension, GeometryScreenPosition, GlyphKey, GlyphWeight, ICbGOpt, ICbOpt, IClOpt, IDisabledOpt, IGlOpt, IId, IIdGOpt, IIdOpt, ILabel, ILyOpt } from "$lib"; - -export type IToastKind = `simple`; - -export type IToast = IClOpt & - ILabel & IGlOpt & ILyOpt & { - styles?: IToastKind[]; - position?: GeometryScreenPosition; - }; - -export type IGlyph = ICbOpt & IIdOpt & ILyOpt & IClOpt & { - weight?: GlyphWeight; - key: GlyphKey; - dim?: GeometryGlyphDimension; -}; - -export type IInput<T extends string> = IIdGOpt<T> & IClOpt & ILyOpt & { - placeholder?: string; - label?: string; - hidden?: boolean; - validate?: RegExp; - sync?: boolean; - field?: FormField; - callback?: ElementCallbackValue, - callback_keydown?: ElementCallbackValueKeydown<HTMLInputElement>, - callback_blur?: ElementCallbackValueBlur<HTMLInputElement>; - callback_focus?: ElementCallbackValueBlur<HTMLInputElement>; - callback_mount?: ElementCallbackMount<HTMLInputElement>; -}; - -export type ISelectOption<T extends string> = IDisabledOpt & { - value: T; - label: string; -}; - -export type ISelect = IIdOpt & IClOpt & ILyOpt & - ICbGOpt<ISelectOption<string>> & { - sync?: boolean; - sync_init?: boolean; - options: { group?: string | true; entries: ISelectOption<string>[] }[]; - show_arrows?: 'l' | 'r'; - }; - -export type ITextAreaElement = IId & IClOpt & ILyOpt & { - placeholder?: string; - label?: string; - hidden?: boolean; - validate?: RegExp; - sync?: true; - field?: FormField; - callback?: CallbackPromiseGeneric<{ value: string; pass: boolean; }>; - callback_keydown?: CallbackPromiseGeneric<{ key: string; key_s: boolean; el: HTMLTextAreaElement }>; - callback_blur?: CallbackPromiseGeneric<{ el: HTMLTextAreaElement }>; - callback_focus?: CallbackPromiseGeneric<{ el: HTMLTextAreaElement }>; - on_mount?: CallbackPromiseGeneric<HTMLTextAreaElement>; -}; -\ No newline at end of file diff --git a/apps-lib/src/lib/types/feature.ts b/apps-lib/src/lib/types/feature.ts @@ -1,8 +0,0 @@ -import type { CallbackPromiseGeneric } from "./app"; -import type { ModelLocationGcs, ModelNostrProfile, ModelNostrRelay } from "./model"; - -export type ISearchResultDisplayCallbacks = { - lc_handle_result_location_gcs: CallbackPromiseGeneric<ModelLocationGcs>; - lc_handle_result_nostr_profile: CallbackPromiseGeneric<ModelNostrProfile>; - lc_handle_result_nostr_relay: CallbackPromiseGeneric<ModelNostrRelay>; -} -\ No newline at end of file diff --git a/apps-lib/src/lib/types/interface.ts b/apps-lib/src/lib/types/interface.ts @@ -1,156 +0,0 @@ -import type { CallbackPromise, CallbackPromiseGeneric, EntryStyle, GlyphKey, IGlyph, IInput, LayerGlyphBasisKind, LoadingBlades, LoadingDimension } from "$lib"; -import type { ThemeLayer } from "@radroots/theme"; -import type { TransitionConfig } from "svelte/transition"; - -export type IDisabledOpt = { - disabled?: boolean | never; -} - -export type IBasis<T> = { - basis: T; -}; - -export type ICb = { - callback: CallbackPromise | never; -}; - -export type ICbOpt = Partial<ICb>; - -export type ICbG<T> = { - callback: CallbackPromiseGeneric<T> | never; -}; - -export type ICbGOpt<T> = Partial<ICbG<T>>; - -export type ICl = { - classes: string | never; -}; - -export type IClOpt = Partial<ICl>; - - -export type IId = { - id: string | never; -}; - -export type IIdOpt = Partial<IId>; - -export type IGl = { - glyph: IGlyph | never; -}; - -export type IGlOpt = Partial<IGl>; - -export type IGlyphKey = { - glyph: GlyphKey -}; - -export type ILy = { - layer: ThemeLayer | never; -}; - -export type ILyOpt = Partial<ILy>; - -export type ILableFieldsSwap = { - toggle: boolean; - on: IClOpt & { - value: string; - }, - off: IClOpt & { - value: string; - }, -}; - -export type ILabelSwap = { - swap: ILableFieldsSwap; -} - -export type ILabelTupFields = { - left?: ILableFields[]; - right?: ILableFields[]; -}; - -export type ILabelTup = { - label: ILabelTupFields; -}; - -export type LabelFieldKind = `link` | `on` | `shade`; - -export type ILableFields = & { - classes_wrap?: string - classes?: string; - kind?: LayerGlyphBasisKind; - hide_truncate?: boolean; - hide_active?: boolean; -} & ( - ({ - value: string; - } | ILabelSwap) - | IGl - ); - -export type ILabel = { - label: ILableFields; -}; - -export type ILabelOpt = Partial<ILabel>; - -export type ILoadSymbol = IClOpt & { - color?: 'white'; - blades?: LoadingBlades; - dim?: LoadingDimension; -}; - -export type IIdG<T extends string> = { - id: T | never; -}; - -export type FormField = { - validate: RegExp; - charset: RegExp; - validate_keypress?: boolean; -}; - -export type IIdGOpt<T extends string> = Partial<IIdG<T>>; - - -export type IIdWrap = { - id_wrap: string | never; -}; - -export type IIdWrapOpt = Partial<IIdWrap>; - -export type ILabelValue = { - label: IClOpt & { - value: string; - }; -}; - -export type ILabelDisplay = IIdWrapOpt & IClOpt & ILabelValue & ILyOpt & { - style?: EntryStyle; -}; - - -export type ILoading = { - loading: boolean | never; -}; - -export type ILoadingOpt = Partial<ILoading>; - -export type IEntryWrap = IClOpt & IIdOpt & ILyOpt & { - style?: EntryStyle; - style_a?: true; - no_pad?: true; - fade?: { - in?: TransitionConfig; - out?: TransitionConfig; - }; -} - -export type IEntryLine = ILoadingOpt & { - wrap?: IEntryWrap; - el: IInput<string>; - notify_inline?: { - glyph: GlyphKey | IGlyph; - }; -}; diff --git a/apps-lib/src/lib/types/model.ts b/apps-lib/src/lib/types/model.ts @@ -1,34 +0,0 @@ -export type ModelNostrProfile = { - id: string; - name?: string; - display_name?: string; - about?: string; - picture?: string; -} - -export type ModelNostrRelay = { - id: string; -} - -export type ModelLocationGcs = { - id: string; - lat: number; - lng: number; - geohash: string; - kind: string; - label?: string; - area?: number; - elevation?: number; - soil?: string; - climate?: string; - gc_id?: string; - gc_name?: string; - gc_admin1_id?: string; - gc_admin1_name?: string; - gc_country_id?: string; - gc_country_name?: string; -} - -export type ModelTradeProduct = { - id: string; -} -\ No newline at end of file diff --git a/apps-lib/src/lib/types/util.ts b/apps-lib/src/lib/types/util.ts @@ -1,24 +0,0 @@ -import { type CallbackPromiseFull } from "$lib"; - -export type GeolocationLatitudeFmtOption = 'dms' | 'd' | 'dm'; - -export type GeolocationPoint = { - lat: number; - lng: number; -}; - -export type GeocoderReverseResult = { - id: number; - name: string; - admin1_id: string | number; - admin1_name: string; - country_id: string; - country_name: string; - latitude: number; - longitude: number; -}; - -export type ILcGeocodeCallback = CallbackPromiseFull< - GeolocationPoint, - GeocoderReverseResult | undefined -> -\ No newline at end of file diff --git a/apps-lib/src/lib/types/view.ts b/apps-lib/src/lib/types/view.ts @@ -1,41 +0,0 @@ -import { type CallbackPromise, type CallbackPromiseFull, type CallbackPromiseReturn, type IBasisOpt } from ".."; -import type { ModelLocationGcs, ModelNostrProfile, ModelNostrRelay, ModelTradeProduct } from "./model"; - -export type ViewBasis<T extends object> = { - kv_init_prevent?: boolean; - lc_on_mount?: CallbackPromise; - lc_on_destroy?: CallbackPromise; - lc_gui_alert?: CallbackPromiseFull<string, boolean>; - lc_gui_confirm?: CallbackPromiseFull<string, boolean>; -} & T; - -export type ViewBasisLoadData<TView extends object, TLoadData extends object> = ViewBasis<TView> & { - lc_load_data: CallbackPromiseReturn<TLoadData>; - lc_handle_back?: CallbackPromise; -}; - -export type IFarmLoadData = IBasisOpt<{ - location_gcs: ModelLocationGcs[]; -}>; - -export type IFarmViewLoadData = IBasisOpt<{ - location_gcs: ModelLocationGcs; -}>; - -export type ISearchLoadData = IBasisOpt<{ - location_gcs: ModelLocationGcs[]; - nostr_profile: ModelNostrProfile[]; - nostr_relay: ModelNostrRelay[]; - trade_product: ModelTradeProduct[]; -}>; - -export type ISettingsNostrProfileLoadData = IBasisOpt<{ - nostr_profile: ModelNostrProfile; -}>; - -export type ISettingsNostrProfileEditLoadData = IBasisOpt<{ - nostr_profile: ModelNostrProfile; - field_key: string; - field_val?: string; -}>; - diff --git a/apps-lib/src/lib/util/app.ts b/apps-lib/src/lib/util/app.ts @@ -1,143 +0,0 @@ -import { goto } from "$app/navigation"; -import { nav_prev, type AppLayoutKey, type CallbackPromise, type CallbackRoute, type INavigationRoute, type INavigationRoutePreventRoute, type NavigationParamTuple } from "$lib"; -import type { ColorMode, ThemeKey, ThemeLayer } from "@radroots/theme"; -import { get } from "svelte/store"; - -export const get_store = get; - -export const sleep = async (ms: number): Promise<void> => { - await new Promise((resolve) => setTimeout(resolve, ms)); -}; - -export const theme_set = (theme_key: ThemeKey, color_mode: ColorMode): void => { - const data_theme = `${theme_key}_${color_mode}`; - document.documentElement.setAttribute("data-theme", data_theme); -}; - -export const fmt_id = (id?: string): string => { - const pref = location.pathname.slice(1, -1).replaceAll(`-`, `_`).replaceAll(`/`, `-`).replaceAll(`--`, `-`); - return `*${pref}${id ? `-${id}` : ``}` -}; - -export const fmt_cl = (classes?: string): string => { - return classes ? classes : ``; -}; - -export const parse_layer = (layer?: number, layer_default?: ThemeLayer): ThemeLayer => { - switch (layer) { - case 0: - case 1: - case 2: - return layer; - default: - return layer_default ? layer_default : 0; - }; -}; - -export const encode_qp = (params_list?: NavigationParamTuple[]): string => { - const params = (params_list || []).filter(i => i[0] && i[1]) - if (!params.length) return ``; - return params.map(([k, v], index) => `${index === 0 ? `?` : ``}&${k.trim()}=${encodeURI(v.trim())}`).join(``).trim(); -}; - -export const encode_qp_route = <T extends string>(route: T, params_list?: NavigationParamTuple[]): string => { - return `${route}/${encode_qp(params_list)}`.replaceAll(`//`, `/`) -}; - -export const exe_iter = async (callback: CallbackPromise, num: number = 1, delay: number = 400): Promise<void> => { - try { - const iter_fn = (count: number) => { - if (count > 0) { - callback(); - if (count > 1) { - setTimeout(() => { - iter_fn(count - 1); - }, delay); - } - } - }; - iter_fn(num); - } catch (e) { - console.log(`(error) exe_iter `, e); - } -}; - -export const value_constrain = (regex_charset: RegExp, value: string): string => { - return value - .split(``) - .filter((char) => regex_charset.test(char)) - .join(``); -}; - -export const value_constrain_textarea = (regex_charset: RegExp, value: string): string => { - return value - .replace(/\u00A0/g, ` `) - .split(/[\n]/) - .map(line => line - .split(``) - .filter((char) => regex_charset.test(char)) - .join(``) - ) - .join("\n"); -}; - -export const fmt_textarea_value = (value: string): string => { - return value.replace(/ /g, `\u00A0`); -}; - -export const route_prev = async (opts: (INavigationRoute | INavigationRoutePreventRoute)): Promise<void> => { - let route_to = `/` - if (`prevent_route` in opts && opts.prevent_route) return void await opts.prevent_route(); - else if (`route` in opts) { - const $nav_prev = get_store(nav_prev); - console.log(JSON.stringify($nav_prev, null, 4), `$nav_prev`) - let route_to = - typeof opts.route === `string` - ? opts.route - : encode_qp_route(opts.route[0], opts.route[1]); - if ($nav_prev.length) { - const nav_prev_li = $nav_prev[$nav_prev.length - 1]; - console.log(JSON.stringify(nav_prev_li, null, 4), `nav_prev_li`) - nav_prev.set([...$nav_prev.slice(0, -1)]); - if (nav_prev_li) - route_to = encode_qp_route( - nav_prev_li.route, - nav_prev_li.params, - ); - console.log(JSON.stringify($nav_prev, null, 4), `$nav_prev`) - } - } - console.log(JSON.stringify(route_to, null, 4), `route_to`) - await goto(route_to); -}; - -export const callback_route = async (callback_route: CallbackRoute): Promise<void> => { - if (`route` in callback_route) { - if (typeof callback_route.route === `string`) return void await goto(callback_route.route); - else return void await goto( - encode_qp_route( - callback_route.route[0], - callback_route.route[1], - ), - ); - } - return void await callback_route(); -}; - -export const get_layout = (val: string | false): AppLayoutKey => { - switch (val) { - case `mobile_base`: - case `mobile_y`: - return val; - default: - return `mobile_base`; - }; -}; - -export const debounce_input = (func: Function, delay: number) => { - let timer: ReturnType<typeof setTimeout>; - return function (this: any, ...args: any) { - clearTimeout(timer); - timer = setTimeout(() => func.apply(this, args), delay); - }; -}; diff --git a/apps-lib/src/lib/util/carousel.ts b/apps-lib/src/lib/util/carousel.ts @@ -3,9 +3,10 @@ import { carousel_index, carousel_index_max, carousel_num, - exe_iter, get_store -} from "$lib"; +} from "$root"; +import { exe_iter } from "@radroots/util"; + const CAROUSEL_DELAY_MS = 150; const get_slide_container = <T extends string>( diff --git a/apps-lib/src/lib/util/casl.ts b/apps-lib/src/lib/util/casl.ts @@ -1,19 +1,20 @@ -import { casl_index, casl_index_max } from "$lib/store/component"; -import { get_store } from "./app"; +import { casl_i, casl_imax, get_store } from "$root"; -export const casl_inc = async (opts?: 'reflow'): Promise<void> => { - const $casl_index = get_store(casl_index); - const $casl_index_max = get_store(casl_index_max); - casl_index.set(($casl_index + 1) % $casl_index_max); +export const casl_inc = async (opts?: 'noflow'): Promise<void> => { + const $casl_i = get_store(casl_i); + const $casl_imax = get_store(casl_imax); + if (opts === 'noflow' && $casl_i < $casl_imax) casl_i.set($casl_i + 1); + else casl_i.set(($casl_i + 1) % ($casl_imax + 1)); }; -export const casl_dec = async (opts?: 'reflow'): Promise<void> => { - const $casl_index = get_store(casl_index); - const $casl_index_max = get_store(casl_index_max); - casl_index.set(($casl_index - 1 + $casl_index_max) % $casl_index_max); +export const casl_dec = async (opts?: 'noflow'): Promise<void> => { + const $casl_i = get_store(casl_i); + const $casl_imax = get_store(casl_imax); + if (opts === 'noflow' && $casl_i > 0) casl_i.set($casl_i - 1); + else casl_i.set(($casl_i - 1 + ($casl_imax + 1)) % ($casl_imax + 1)); }; export const casl_init = (index_curr: number, index_max: number): void => { - casl_index.set(index_curr); - casl_index_max.set(index_max); -}; -\ No newline at end of file + casl_i.set(index_curr); + casl_imax.set(index_max); +}; diff --git a/apps-lib/src/lib/util/component.ts b/apps-lib/src/lib/util/component.ts @@ -1,10 +0,0 @@ -import { type LabelFieldKind } from "$lib"; -import type { ThemeLayer } from "@radroots/theme"; - -export const fmt_trellis = (hide_border_t: boolean, hide_border_b: boolean): string => { - return `${hide_border_t ? `group-first:border-t-0` : `group-first:border-t-line`} ${hide_border_b ? `group-last:border-b-0` : `group-last:border-b-line`}`; -}; - -export const get_label_classes_kind = (layer: ThemeLayer, label_kind: LabelFieldKind | undefined, hide_active: boolean): string => { - return `text-layer-${layer}-glyph${label_kind ? `-${label_kind}` : ``} ${hide_active ? `` : `group-active:text-layer-${layer}-glyph${label_kind ? `-${label_kind}_a` : `_a`}`}` -}; -\ No newline at end of file diff --git a/apps-lib/src/lib/util/conf.ts b/apps-lib/src/lib/util/conf.ts @@ -1,51 +0,0 @@ -import { type AppLayoutKey } from "$lib"; - -export const ascii = { - bullet: '•', - dash: `—`, - up: `↑`, - down: `↓` -} - -type ConfigWindow = { - layout: Record<AppLayoutKey, { - h: number; - }>; - debounce: { - search: number; - } -}; - -export const cfg_app: ConfigWindow = { - layout: { - mobile_base: { - h: 600 - }, - mobile_y: { - h: 750 - } - }, - debounce: { - search: 200 - }, -}; - -export const cfg_map = { - styles: { - base: { - light: `https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json`, - dark: `https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json` - } - }, - popup: { - dot: { - offset: [0, -10] as [number, number] - } - }, - coords: { - default: { - lat: 0, - lng: 0, - } - } -}; -\ No newline at end of file diff --git a/apps-lib/src/lib/util/document.ts b/apps-lib/src/lib/util/document.ts @@ -1,52 +0,0 @@ -import { browser } from "$app/environment"; - -export const el_id = (id: string): HTMLElement | undefined => { - const el = document.getElementById(id); - return el ? el : undefined; -}; - -export const el_toggle = (id: string, toggle_class: string): void => { - const el = document.getElementById(id); - if (el) el.classList.toggle(toggle_class); -}; - -export const els_id_pref = (id_pref: string): Element[] | undefined => { - const els = document.querySelectorAll(`[id^="${id_pref}"]`); - if (els && els.length) return Array.from(els); - return undefined; -}; - -export const els_id_pref_index = (id_pref: string, num_index: number, orientation: `greater` | `lesser` | `not` = `greater`, inclusive: boolean = true): Element[] | undefined => { - const els = document.querySelectorAll(`[id^="${`${id_pref}-`.replaceAll(`--`, `-`)}"]`); - if (els && els.length) return Array.from(els).filter(el => { - const match = el.id.match(/(?<=^|\-)[0-9]\d*(?=\-)/) - if (match) { - const num = parseInt(match[0], 10); - switch (orientation) { - case `greater`: { - if (inclusive) return num >= num_index; - else return num > num_index; - } - case `lesser`: { - if (inclusive) return num <= num_index; - else return num < num_index; - } - case `not`: { - return num !== num_index; - } - } - } - return false; - }); - return undefined; -}; - -export const view_effect = <T extends string>(view: T): void => { - console.log(`view_effect `, view) - if (!browser) return; - for (const el of document.querySelectorAll(`[data-view]`)) { - if (el.getAttribute(`data-view`) !== view) el.classList.add(`hidden`) - else el.classList.remove(`hidden`) - } -}; - diff --git a/apps-lib/src/lib/util/error.ts b/apps-lib/src/lib/util/error.ts @@ -1,36 +0,0 @@ -import { page } from "$app/stores"; -import { get_store } from "$lib"; - -export type IErrorCatchCallback = { - name: string; - message: string; - stack: string; - url: string; - func: string; -}; - -export const catch_err = async (e: unknown, func: string, callback: (opts: IErrorCatchCallback) => Promise<void>): Promise<void> => { - const $page = get_store(page) as any; - let name = ``; - let message = ``; - let stack = ``; - let url = ``; - if (e instanceof Error) { - name = e.name; - message = e.message; - stack = e.stack; - url = $page.url.pathname; - } - await callback({ name, message, stack, url, func }); -}; - -export const handle_err = async (e: unknown, fcall: string): Promise<void> => { - try { - return void await catch_err(e, fcall, async (opts) => { - console.log(`handle_err e `, e) - console.log(JSON.stringify(opts, null, 4), `handle_err opts`) - }); - } catch (e) { - console.log(`(handle_err) `, e) - } -}; -\ No newline at end of file diff --git a/apps-lib/src/lib/util/geolocation.ts b/apps-lib/src/lib/util/geolocation.ts @@ -1,47 +0,0 @@ -import { get_store, liblocale, type GeolocationLatitudeFmtOption } from "$lib"; - -export const geol_lat_fmt = (lat: number, fmt_opt: GeolocationLatitudeFmtOption, precision: number = 5): string => { - const $locale = get_store(liblocale) - const options: Intl.NumberFormatOptions = { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }; - const fmt_deg = new Intl.NumberFormat($locale, { maximumFractionDigits: 0 }); - const fmt_min = new Intl.NumberFormat($locale, options); - const fmt_sec = new Intl.NumberFormat($locale, options); - if (fmt_opt === 'dms') { - const deg = Math.floor(Math.abs(lat)); - const min = Math.floor((Math.abs(lat) - deg) * 60); - const sec = ((Math.abs(lat) - deg - min / 60) * 3600); - return `${fmt_deg.format(deg)}° ${fmt_min.format(min)}' ${fmt_sec.format(sec)}" ${lat >= 0 ? 'N' : 'S'}`; - } else if (fmt_opt === 'dm') { - const deg = Math.floor(Math.abs(lat)); - const min = (Math.abs(lat) - deg) * 60; - return `${fmt_deg.format(deg)}° ${fmt_min.format(min)}' ${lat >= 0 ? 'N' : 'S'}`; - } else { - return `${lat.toLocaleString($locale, { maximumFractionDigits: precision })}° ${lat >= 0 ? 'N' : 'S'}`; - } -}; - -export const geol_lng_fmt = (lng: number, fmt_opt: GeolocationLatitudeFmtOption, precision: number = 5): string => { - const $locale = get_store(liblocale) - const options: Intl.NumberFormatOptions = { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }; - const fmt_deg = new Intl.NumberFormat($locale, { maximumFractionDigits: 0 }); - const fmt_min = new Intl.NumberFormat($locale, options); - const fmt_sec = new Intl.NumberFormat($locale, options); - if (fmt_opt === 'dms') { - const degrees = Math.floor(Math.abs(lng)); - const minutes = Math.floor((Math.abs(lng) - degrees) * 60); - const seconds = ((Math.abs(lng) - degrees - minutes / 60) * 3600); - return `${fmt_deg.format(degrees)}° ${fmt_min.format(minutes)}' ${fmt_sec.format(seconds)}" ${lng >= 0 ? 'E' : 'W'}`; - } else if (fmt_opt === 'dm') { - const degrees = Math.floor(Math.abs(lng)); - const minutes = (Math.abs(lng) - degrees) * 60; - return `${fmt_deg.format(degrees)}° ${fmt_min.format(minutes)}' ${lng >= 0 ? 'E' : 'W'}`; - } else { - return `${lng.toLocaleString($locale, { maximumFractionDigits: precision })}° ${lng >= 0 ? 'E' : 'W'}`; - } -}; -\ No newline at end of file diff --git a/apps-lib/src/lib/util/i18n.ts b/apps-lib/src/lib/util/i18n.ts @@ -1,22 +0,0 @@ -import i18n, { type Loader } from '@sveltekit-i18n/base'; -import type { Config, Parser } from '@sveltekit-i18n/parser-icu'; -import parser from '@sveltekit-i18n/parser-icu'; - -type LanguageConfig = { - default?: string; - value?: string; -}; -export const i18n_conf = <T extends string>(opts: { - default_locale: T; - translations: Record<T, any>; - loaders: Loader.LoaderModule[] -}): i18n<Parser.Params<LanguageConfig>> => { - const { default_locale: initLocale, translations, loaders } = opts; - const config: Config<LanguageConfig> = { - initLocale, - translations, - parser: parser(), - loaders, - }; - return new i18n(config); -}; -\ No newline at end of file diff --git a/apps-lib/src/lib/util/idb.ts b/apps-lib/src/lib/util/idb.ts @@ -0,0 +1,54 @@ +import { browser } from "$app/environment"; +import { fmt_id } from "$root"; + +//@ts-ignore +const kv_name = import.meta.env.VITE_PUBLIC_KV_NAME; +if (!kv_name) throw new Error('Error: VITE_PUBLIC_KV_NAME is required'); + +export let idb: Keyva; +if (browser) idb = new Keyva({ name: kv_name }); + +export const kv_init = async (): Promise<void> => { + if (!browser) return; + const range = Keyva.prefix(`*`); + const kv_list = await idb.each({ range }, `keys`); + await Promise.all(kv_list.map((i) => idb.delete(i))); +}; + +export const kv_init_page = async (): Promise<void> => { + if (!browser) return; + const kv_pref = fmt_id(); + const range = Keyva.prefix(kv_pref); + const kv_list = await idb.each({ range }, `keys`); + await Promise.all(kv_list.map((i) => idb.delete(i))); +}; + +export const kv_sync = async (list: [string, string][]): Promise<void> => { + if (!browser) return; + for (const [key, val] of list) await idb.set(key, val); +}; + +export class KvLib<T extends string> { + private _kv: Keyva; + + constructor(kv: Keyva) { + this._kv = kv; + } + public init = async () => { + await kv_init_page(); + } + + public save = async (key: T, value: string) => { + await this._kv.set(fmt_id(key), value); + } + + public read = async (key: T): Promise<string | undefined> => { + const result = await this._kv.get<string>(fmt_id(key)); + if (result) return result; + return undefined; + } + + public del = async (key: T) => { + await this._kv.delete(fmt_id(key)); + } +} +\ No newline at end of file diff --git a/apps-lib/src/lib/util/kv.ts b/apps-lib/src/lib/util/kv.ts @@ -1,54 +0,0 @@ -import { browser } from "$app/environment"; -import { fmt_id } from "$lib"; - -//@ts-ignore -const kv_name = import.meta.env.VITE_PUBLIC_KV_NAME; -if (!kv_name) throw new Error('Error: VITE_PUBLIC_KV_NAME is required'); - -export let kv_basis: Keyva; -if (browser) kv_basis = new Keyva({ name: kv_name }); - -export const kv_init = async (): Promise<void> => { - if (!browser) return; - const range = Keyva.prefix(`*`); - const kv_list = await kv_basis.each({ range }, `keys`); - await Promise.all(kv_list.map((i) => kv_basis.delete(i))); -}; - -export const kv_init_page = async (): Promise<void> => { - if (!browser) return; - const kv_pref = fmt_id(); - const range = Keyva.prefix(kv_pref); - const kv_list = await kv_basis.each({ range }, `keys`); - await Promise.all(kv_list.map((i) => kv_basis.delete(i))); -}; - -export const kv_sync = async (list: [string, string][]): Promise<void> => { - if (!browser) return; - for (const [key, val] of list) await kv_basis.set(key, val); -}; - -export class KvLib<T extends string> { - private _kv: Keyva; - - constructor(kv: Keyva) { - this._kv = kv; - } - public init = async () => { - await kv_init_page(); - } - - public save = async (key: T, value: string) => { - await this._kv.set(fmt_id(key), value); - } - - public read = async (key: T): Promise<string | undefined> => { - const result = await this._kv.get<string>(fmt_id(key)); - if (result) return result; - return undefined; - } - - public del = async (key: T) => { - await this._kv.delete(fmt_id(key)); - } -} -\ No newline at end of file diff --git a/apps-lib/src/lib/util/lib.ts b/apps-lib/src/lib/util/lib.ts @@ -0,0 +1,85 @@ +import { browser } from "$app/environment"; +import { goto } from "$app/navigation"; +import { page } from "$app/state"; +import { liblocale } from "$root"; +import type { ColorMode, ThemeKey } from "@radroots/theme"; +import { encode_qp_route, fmt_geometry_point_coords, fmt_price, parse_currency_marker, type CallbackRoute, type GeometryPoint, type IErrorCatchCallback } from "@radroots/util"; +import { get } from "svelte/store"; + +export const get_store = get; + +export const theme_set = (theme_key: ThemeKey, color_mode: ColorMode): void => { + const data_theme = `${theme_key}_${color_mode}`; + document.documentElement.setAttribute("data-theme", data_theme); +}; + +export const fmt_id = (id?: string): string => { + if (!browser) return ``; + const pref = location?.pathname.slice(1, -1).replaceAll(`-`, `_`).replaceAll(`/`, `-`).replaceAll(`--`, `-`); + return `*${pref}${id ? `-${id}` : ``}` +}; + +export const catch_err = async (e: unknown, func: string, callback: (opts: IErrorCatchCallback) => Promise<void>): Promise<void> => { + let name = ``; + let message = ``; + let stack = ``; + let url = ``; + if (e instanceof Error) { + name = e.name; + message = e.message; + stack = e.stack || ``; + url = page.url.pathname; + } + await callback({ name, message, stack, url, func }); +}; + +export const handle_err = async (e: unknown, fcall: string): Promise<void> => { + try { + return void await catch_err(e, fcall, async (opts) => { + console.log(`handle_err e `, e) + console.log(JSON.stringify(opts, null, 4), `handle_err opts`) + }); + } catch (e) { + console.log(`(handle_err) `, e) + } +}; + +export const callback_route = async <T extends string>(callback_route: CallbackRoute<T>): Promise<void> => { + if (`route` in callback_route) { + if (typeof callback_route.route === `string`) return void await goto(callback_route.route); + else return void await goto( + encode_qp_route( + callback_route.route[0], + callback_route.route[1], + ), + ); + } + return void await callback_route(); +}; + +export const query_params_clear = async (): Promise<void> => { + page.url && await goto(page.url.pathname, { replaceState: true }) +}; + +export const lib_fmt_price = (value: string, currency: string): string => { + const $locale = get_store(liblocale); + return fmt_price($locale, value, currency); +}; + +export const lib_parse_currency_marker = (currency: string): string => { + const $locale = get_store(liblocale); + return parse_currency_marker($locale, currency); +}; + +export const lib_fmt_geometry_point_coords = (point: GeometryPoint): string => { + const $locale = get_store(liblocale); + return fmt_geometry_point_coords(point, $locale); +}; + +export const view_effect = <T extends string>(view: T): void => { + if (!browser) return; + for (const el of document.querySelectorAll(`[data-view]`)) { + if (el.getAttribute(`data-view`) !== view) el.classList.add(`hidden`) + else el.classList.remove(`hidden`) + } +}; +\ No newline at end of file diff --git a/apps-lib/src/lib/util/styles.ts b/apps-lib/src/lib/util/styles.ts @@ -1,42 +0,0 @@ -import type { AppLayoutKey, GeometryGlyphDimension, IToastKind, LoadingDimension } from "$lib"; - -export const glyph_style_map: Map<GeometryGlyphDimension, { gl_1: number; dim_1?: number; }> = new Map([ - ["xs--", { gl_1: 12 }], - ["xs-", { gl_1: 12, dim_1: 17 }], - ["xs", { gl_1: 15, dim_1: 18 }], - ["xs+", { gl_1: 18, dim_1: 20 }], - ["sm-", { gl_1: 19, dim_1: 22 }], - ["sm", { gl_1: 20, dim_1: 24 }], - ["sm+", { gl_1: 21 }], - ["md-", { gl_1: 23 }], - ["md", { gl_1: 24 }], - ["md+", { gl_1: 26 }], - ["lg-", { gl_1: 27 }], - ["lg", { gl_1: 28 }], - ["xl", { gl_1: 30 }], - ["xl+", { gl_1: 40 }], -]); - -export const loading_style_map: Map<LoadingDimension, { dim_1: number; gl_2: number }> = new Map([ - ["glyph-send-button", { dim_1: 20, gl_2: 20 }], - ["xs", { dim_1: 12, gl_2: 12 }], - ["sm", { dim_1: 16, gl_2: 16 }], - ["md", { dim_1: 20, gl_2: 20 }], - ["lg", { dim_1: 28, gl_2: 28 }], - ["xl", { dim_1: 36, gl_2: 36 }], -]); - -export const toast_layout_map: Map<AppLayoutKey, string> = new Map([ - [`mobile_base`, `pt-8`], - [`mobile_y`, `pt-16`], -]); - -export const toast_style_map: Map<IToastKind, { inner: string; outer: string }> = new Map([ - [ - `simple`, - { - inner: `justify-center`, - outer: `min-h-toast_min w-full px-4 rounded-2xl shadow-sm`, - }, - ], -]); -\ No newline at end of file diff --git a/apps-lib/src/lib/util/view.ts b/apps-lib/src/lib/util/view.ts @@ -1,7 +0,0 @@ -import { casl_init, view_effect } from "$lib"; - -export const handle_view = <T extends string>(view: T, casl_rec: Record<T, { max_index: number }>): T => { - casl_init(0, casl_rec[view].max_index); - view_effect<T>(view); - return view; -}; -\ No newline at end of file diff --git a/apps-lib/src/lib/view/farm-land-add.svelte b/apps-lib/src/lib/view/farm-land-add.svelte @@ -1,422 +0,0 @@ -<script lang="ts"> - import { - Carousel, - casl_dec, - casl_inc, - casl_index, - FloatTabs, - fmt_id, - geol_lat_fmt, - geol_lng_fmt, - GlyphButtonSimple, - GlyphTitleSelectLabel, - handle_err, - handle_view, - Input, - kv_init_page, - LayoutView, - lls, - MapPointSelect, - PageToolbar, - SelectMenu, - type CallbackPromiseReturn, - type GeocoderReverseResult, - type GeolocationPoint, - type ILcGeocodeCallback, - type ViewBasis, - } from "$lib"; - import CarouselItem from "$lib/component/carousel/carousel-item.svelte"; - import View from "$lib/component/lib/view.svelte"; - import { onDestroy, onMount } from "svelte"; - - const casl_rec: Record<View, { max_index: number }> = { - map_init: { - max_index: 2, - }, - loc_form: { - max_index: 1, - }, - }; - - type View = `map_init` | `loc_form`; - let view: View = `map_init`; - - export let bv_map_geol_p: GeolocationPoint | undefined = undefined; - export let bv_map_geoc_r: GeocoderReverseResult | undefined = undefined; - - let lgcs_label = ``; - let lgcs_area = ``; - let lgcs_area_unit = `ha`; - let lgcs_elevation = ``; - let lgcs_elevation_unit = `m`; - let lgcs_climate = ``; - - export let basis: ViewBasis<{ - lc_geocode: ILcGeocodeCallback; - lc_submit: CallbackPromiseReturn<{ id: string } | void>; - }>; - - onMount(async () => { - if (!basis.kv_init_prevent) await kv_init_page(); - if (basis.lc_on_mount) await basis.lc_on_mount(); - handle_view(view, casl_rec); - }); - - onDestroy(async () => { - if (basis.lc_on_destroy) await basis.lc_on_destroy(); - }); - - const handle_dec = async (): Promise<void> => { - await casl_dec(); - }; - - const handle_inc = async (): Promise<void> => { - await casl_inc(); - }; - - const submit = async (): Promise<void> => { - try { - const result = await basis.lc_submit(); - if (result) view = handle_view(`loc_form`, casl_rec); - } catch (e) { - await handle_err(e, `submit`); - } - }; -</script> - -<LayoutView> - <PageToolbar - basis={{ - header: { - label: `${$lls(`icu.add_*`, { value: `${$lls(`common.farm_land`)}` })}`, - callback_route: { - route: `/farm/land`, - }, - }, - }} - > - <div - slot="header-option" - class={`flex flex-row gap-4 justify-start items-center`} - > - <div - class={`${$casl_index > 0 ? `fade-in` : `hidden`} flex flex-row justify-start items-center`} - > - <GlyphButtonSimple - basis={{ - classes: `gap-1`, - kind: `neutral`, - label: `${$lls(`common.map`)}`, - glyph: `arrow-left`, - callback: handle_dec, - }} - /> - </div> - <GlyphButtonSimple - basis={{ - label: `${$lls(`common.add`)}`, - callback: async () => { - if (view === `map_init` && $casl_index === 0) { - if (!(bv_map_geol_p && bv_map_geoc_r)) - return void (await basis.lc_gui_alert( - `Please select a location.`, //@todo - )); - return void (await handle_inc()); - } else if (view === `map_init` && $casl_index === 1) { - if (bv_map_geol_p && bv_map_geoc_r) - return void (await submit()); - } - }, - }} - /> - </div> - </PageToolbar> - <View key={`map_init`}> - <Carousel> - <CarouselItem> - <div - class={`flex flex-col w-full px-4 justify-center items-center`} - > - <div - class={`relative flex flex-col w-full justify-start items-center rounded-[20px] overflow-hidden border-x-[3px] border-y-[5px] border-white/30 shadow-sm`} - > - <div - class={`flex flex-row h-[32rem] w-full justify-center items-center bg-base-100/60 overflow-hidden rounded-2xl`} - > - <MapPointSelect - bind:map_point={bv_map_geol_p} - bind:map_point_geoc_r={bv_map_geoc_r} - basis={{ - lc_geocode: basis.lc_geocode, - }} - /> - </div> - </div> - </div> - </CarouselItem> - <CarouselItem> - <div - class={`flex flex-col w-full pt-4 px-4 gap-4 justify-center items-center`} - > - <div - class={`flex flex-col w-full px-2 gap-4 justify-start items-center`} - > - {#if bv_map_geoc_r && bv_map_geol_p} - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$lls(`common.location`)}`} - </p> - </div> - <div - class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} - > - <p - class={`font-sans font-[400] text-[1.1rem] text-layer-0-glyph`} - > - {`${bv_map_geoc_r.name}, ${bv_map_geoc_r.admin1_id}, ${bv_map_geoc_r.country_name}`} - </p> - </div> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$lls(`common.coordinates`)}`} - </p> - <div - class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} - > - <p - class={`font-sans font-[400] text-[1.1rem] text-layer-0-glyph`} - > - {`${geol_lat_fmt( - bv_map_geol_p.lat, - `d`, - 4, - )}, ${geol_lng_fmt( - bv_map_geol_p.lng, - `d`, - 4, - )}`} - </p> - </div> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$lls(`common.farm`)}/${`${$lls(`common.estate`)}`}`} - </p> - </div> - <div - class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} - > - <Input - bind:value={lgcs_label} - basis={{ - id: fmt_id(`label`), - sync: true, - layer: 0, - classes: `h-10 placeholder:text-[1.1rem]`, - placeholder: `${$lls(`common.name_of_farm_or_estate`)}`, - /*field: { - charset: - location_gcs_form_fields - .label.charset, - validate: - location_gcs_form_fields - .label.validation, - validate_keypress: true, - },*/ - }} - /> - </div> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full gap-1 justify-start items-center`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$lls(`common.area`)}`} - </p> - <SelectMenu - bind:value={lgcs_area_unit} - basis={{ - layer: 0, - options: [ - { - entries: [ - { - label: `${$lls(`measurement.area.ac`)}`, - value: `ac`, - }, - { - label: `${$lls(`measurement.area.ha`)}`, - value: `ha`, - }, - { - label: `${$lls(`measurement.area.m2`)}`, - value: `m2`, - }, - ], - }, - ], - }} - > - <svelte:fragment slot="element"> - <GlyphTitleSelectLabel - basis={{ - label: `${$lls(`measurement.area.${lgcs_area_unit}_ab`)}`, - }} - /> - </svelte:fragment> - </SelectMenu> - </div> - <div - class={`relative flex flex-row h-12 w-full justify-between items-center border-y-line border-layer-0-surface-edge`} - > - <Input - bind:value={lgcs_area} - basis={{ - id: fmt_id(`area`), - sync: true, - layer: 0, - classes: `h-10 placeholder:text-[1.1rem]`, - placeholder: `${$lls(`common.land_area`)}`, - /*field: { - charset: regex.num, - validate: regex.num, - validate_keypress: true, - },*/ - }} - /> - </div> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full gap-1 justify-start items-center`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$lls(`common.elevation`)}`} - </p> - <SelectMenu - bind:value={lgcs_elevation_unit} - basis={{ - layer: 0, - options: [ - { - entries: [ - { - label: `${$lls(`measurement.length.m`)}`, - value: `m`, - }, - { - label: `${$lls(`measurement.length.ft`)}`, - value: `ft`, - }, - ], - }, - ], - }} - > - <svelte:fragment slot="element"> - <GlyphTitleSelectLabel - basis={{ - label: `${$lls(`measurement.length.${lgcs_elevation_unit}_ab`)}`, - }} - /> - </svelte:fragment> - </SelectMenu> - </div> - <div - class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} - > - <Input - bind:value={lgcs_elevation} - basis={{ - id: fmt_id(`elevation`), - sync: true, - layer: 0, - classes: `h-10 placeholder:text-[1.1rem]`, - placeholder: `${$lls(`common.elevation`)}`, - /*field: { - charset: regex.num, - validate: regex.num, - validate_keypress: true, - },*/ - }} - /> - </div> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$lls(`common.climate`)}`} - </p> - </div> - <div - class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} - > - <Input - bind:value={lgcs_climate} - basis={{ - id: fmt_id(`climate`), - sync: true, - layer: 0, - classes: `h-10 placeholder:text-[1.1rem]`, - placeholder: `${$lls(`common.climate`)}`, - /*field: { - charset: regex.description, - validate: regex.description_ch, - validate_keypress: true, - },*/ - }} - /> - </div> - </div> - {/if} - </div> - </div> - </CarouselItem> - </Carousel> - </View> - <View key={`loc_form`}> - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - view = handle_view(`map_init`, casl_rec); - }} - > - <p class={`font-sans font-[400] text-layer-0-glyph`}>{`two`}</p> - </button> - </View> -</LayoutView> -<FloatTabs /> diff --git a/apps-lib/src/lib/view/farm-land-view.svelte b/apps-lib/src/lib/view/farm-land-view.svelte @@ -1,82 +0,0 @@ -<script lang="ts"> - import { - app_notify, - GlyphButtonSimple, - kv_init_page, - LayoutView, - lls, - MapPointDisplay, - PageToolbar, - qp_id, - type IFarmViewLoadData, - type ViewBasisLoadData, - } from "$lib"; - import { onDestroy, onMount } from "svelte"; - type LoadData = IFarmViewLoadData; - - export let basis: ViewBasisLoadData<{}, LoadData>; - let load_data: LoadData = undefined; - - onMount(async () => { - if (!basis.kv_init_prevent) await kv_init_page(); - if (basis.lc_on_mount) await basis.lc_on_mount(); - if (!$qp_id) - return void app_notify.set( - `${$lls(`error.page.load.query_param`)}`, - ); - load_data = await basis.lc_load_data(); - }); - - onDestroy(async () => { - if (basis.lc_on_destroy) await basis.lc_on_destroy(); - }); -</script> - -<LayoutView> - <PageToolbar - basis={{ - header: { - label: `${$lls(`icu.add_*`, { value: `${$lls(`common.farm_land`)}` })}`, - callback_route: { - route: `/farm/land`, - }, - }, - }} - > - <div - slot="header-option" - class={`flex flex-row gap-4 justify-start items-center`} - > - <GlyphButtonSimple - basis={{ - label: `${$lls(`common.edit`)}`, - callback: async () => { - alert(`@todo!`); - }, - }} - /> - </div> - </PageToolbar> - {#if load_data?.location_gcs} - <div class={`flex flex-col w-full px-4 justify-center items-center`}> - <div - class={`flex flex-row h-[20rem] w-full justify-center items-center bg-layer-2-surface round-44 overflow-hidden`} - > - <MapPointDisplay - basis={{ - zoom: 12, - point: { - lat: load_data.location_gcs.lat, - lng: load_data.location_gcs.lng, - }, - }} - /> - </div> - <div class={`flex flex-col w-full justify-center items-center`}> - <div class={`flex flex-row w-full justify-start items-center`}> - <p class={`font-sans font-[400] text-layer-0-glyph`}>hi</p> - </div> - </div> - </div> - {/if} -</LayoutView> diff --git a/apps-lib/src/lib/view/farm-land.svelte b/apps-lib/src/lib/view/farm-land.svelte @@ -1,141 +0,0 @@ -<script lang="ts"> - import { - Fade, - FloatTabs, - geol_lat_fmt, - geol_lng_fmt, - GlyphButtonSimple, - kv_init_page, - LayoutView, - lls, - MapPointDisplay, - PageToolbar, - type CallbackPromise, - type IFarmLoadData, - type ViewBasisLoadData, - } from "$lib"; - import { onDestroy, onMount } from "svelte"; - type LoadData = IFarmLoadData; - - export let basis: ViewBasisLoadData< - { - lc_handle_farm_land_add: CallbackPromise; - lc_handle_farm_land_view: CallbackPromise; - }, - LoadData - >; - let load_data: LoadData = undefined; - - onMount(async () => { - if (!basis.kv_init_prevent) await kv_init_page(); - if (basis.lc_on_mount) await basis.lc_on_mount(); - load_data = await basis.lc_load_data(); - }); - - onDestroy(async () => { - if (basis.lc_on_destroy) await basis.lc_on_destroy(); - }); -</script> - -<LayoutView> - <PageToolbar - basis={{ - header: { label: `${$lls(`common.farm_land`)}` }, - }} - > - <div - slot="header-option" - class={`flex flex-row justify-start items-center`} - > - <Fade> - <GlyphButtonSimple - basis={{ - label: `${$lls(`common.add`)}`, - callback: basis.lc_handle_farm_land_add, - }} - ></GlyphButtonSimple> - </Fade> - </div> - </PageToolbar> - <div - class={`flex flex-col w-full pt-2 px-4 gap-5 justify-start items-center`} - > - {#if load_data && load_data.location_gcs.length} - {#each load_data.location_gcs.filter((i) => i.kind === `farm_land`) as li} - <button - class={`group flex flex-row h-[5rem] w-full px-8 gap-8 justify-start items-center bg-layer-1-surface layer-1-active-surface round-36 el-re`} - on:click={basis.lc_handle_farm_land_view} - > - <div - class={`flex flex-col h-[4rem] w-[4rem] justify-start items-center bg-layer-2-surface round-24`} - > - <MapPointDisplay - basis={{ - point: { - lat: li.lat, - lng: li.lng, - }, - }} - /> - </div> - <div - class={`flex flex-col flex-grow h-[3.25rem] justify-between items-start`} - > - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-sans font-[500] text-layer-0-glyph`} - > - {`${ - li.label || - `${geol_lat_fmt( - li.lat, - `d`, - 4, - )}, ${geol_lng_fmt(li.lng, `d`, 4)}` - }`} - </p> - </div> - <div - class={`flex flex-row w-full gap-2 justify-start items-center`} - > - {#if li.kind === `farm_land`} - <div - class={`flex flex-row h-5 px-2 justify-center items-center bg-layer-2-surface rounded-md`} - > - <p - class={`font-sans font-[700] text-[0.8rem] text-white`} - > - {`${$lls(`common.farm`)}`} - </p> - </div> - {/if} - <p - class={`font-sansd font-[500] text-layer-0-glyph`} - > - {`${li.gc_name}, ${li.gc_admin1_id}, ${li.gc_country_id}`} - </p> - </div> - </div> - </button> - {/each} - {:else} - <div class={`flex flex-col w-full justify-center items-center`}> - <div class={`flex flex-row w-full justify-center items-center`}> - <button - class={`flex flex-row justify-center items-center`} - on:click={basis.lc_handle_farm_land_add} - > - <p - class={`font-sans font-[400] text-lg text-layer-0-glyph`} - > - {`- Add land -`} - </p> - </button> - </div> - </div> - {/if} - </div> -</LayoutView> -<FloatTabs /> diff --git a/apps-lib/src/lib/view/farms-add.svelte b/apps-lib/src/lib/view/farms-add.svelte @@ -0,0 +1,165 @@ +<script lang="ts"> + import { + ButtonLayoutPair, + Carousel, + casl_dec, + casl_i, + casl_inc, + casl_init, + FarmsAddCasliDetail, + FarmsAddCasliMap, + handle_err, + LayoutView, + liblocale, + lls, + PageToolbar, + } from "$root"; + import { + geol_lat_fmt, + geol_lng_fmt, + parse_float, + parse_geocode_address, + vs_view_farms_add_submission, + type CallbackPromiseGeneric, + type CallbackRoute, + type GeocoderReverseResult, + type GeolocationAddress, + type GeolocationPoint, + type IViewFarmsAddSubmission, + type LcGeocodeCallback, + type LcGeocodeCurrentCallback, + type LcGuiAlertCallback, + } from "@radroots/util"; + import { onMount } from "svelte"; + + let { + basis, + }: { + basis: { + callback_route?: CallbackRoute<string>; + lc_gui_alert: LcGuiAlertCallback; + lc_geop_current: LcGeocodeCurrentCallback; + lc_geocode: LcGeocodeCallback; + lc_submit: CallbackPromiseGeneric<{ + data_s: IViewFarmsAddSubmission; + }>; + }; + } = $props(); + + let loading = $state(false); + + let map_geop: GeolocationPoint = $state({ lat: 0, lng: 0 }); + let map_geoc: GeocoderReverseResult | undefined = $state(undefined); + + let val_farmname = $state(``); + let val_farmcontact = $state(``); + let val_farmarea = $state(``); + let val_farmarea_unit = $state(`ac`); + + const disabled_submit = $derived($casl_i === 1 && !val_farmname); + + onMount(async () => { + try { + casl_init(0, 2); + } catch (e) { + handle_err(e, `on_mount`); + } + }); + + const farm_geop_lat = $derived( + map_geop ? geol_lat_fmt(map_geop.lat, `dms`, $liblocale, 3) : ``, + ); + + const farm_geop_lng = $derived( + map_geop ? geol_lng_fmt(map_geop.lng, `dms`, $liblocale, 3) : ``, + ); + + const farm_geolocation_address: GeolocationAddress | undefined = $derived( + parse_geocode_address(map_geoc), + ); + + const handle_continue_1 = async (): Promise<void> => { + if (!map_geop) return; //@todo + if (!farm_geolocation_address) return; //@todo + + const vp_obj_submit = vs_view_farms_add_submission.safeParse({ + farm_name: val_farmname, + farm_area: parse_float(val_farmarea), + farm_area_unit: val_farmarea_unit, + farm_contact_name: val_farmcontact, + geolocation_point: map_geop, + geolocation_address: farm_geolocation_address, + } satisfies IViewFarmsAddSubmission); + + if (!vp_obj_submit.success) { + return void basis.lc_gui_alert( + `Please correct the following errors: ${vp_obj_submit.error}`, + ); + } + loading = true; + await basis.lc_submit({ data_s: vp_obj_submit.data }); + loading = false; + }; + + const handle_continue = async (): Promise<void> => { + switch ($casl_i) { + case 1: + return handle_continue_1(); + default: + return await casl_inc(); + } + }; + + const handle_back = async (): Promise<void> => { + await casl_dec(); + }; +</script> + +<LayoutView> + <PageToolbar + basis={{ + header: { + label: `Farm / Add`, + callback_route: basis.callback_route, + }, + }} + /> + <Carousel> + <FarmsAddCasliMap + bind:map_geop + bind:map_geoc + {farm_geop_lat} + {farm_geop_lng} + basis={{ + lc_geocode: basis.lc_geocode, + lc_geop_current: basis.lc_geop_current, + }} + /> + <FarmsAddCasliDetail + bind:val_farmname + bind:val_farmcontact + bind:val_farmarea + bind:val_farmarea_unit + {farm_geop_lat} + {farm_geop_lng} + /> + </Carousel> + <div + class="absolute bottom-4 left-0 flex flex-row w-full gap-1 justify-center items-center" + > + <ButtonLayoutPair + basis={{ + continue: { + label: `${$lls(`common.continue`)}`, + disabled: disabled_submit, + callback: handle_continue, + }, + back: { + label: `${$lls(`common.back`)}`, + visible: $casl_i > 0, + callback: handle_back, + }, + }} + /> + </div> +</LayoutView> diff --git a/apps-lib/src/lib/view/farms-products-add.svelte b/apps-lib/src/lib/view/farms-products-add.svelte @@ -0,0 +1,648 @@ +<script lang="ts"> + import { + app_lo, + ButtonHorizontalPairSlide, + ButtonRound, + Carousel, + CarouselItem, + casl_i, + casl_imax, + casl_inc, + casl_init, + FarmsProductsReviewCard, + fmt_id, + FormEntryPrice, + FormEntryQuantity, + FormEntrySelect, + FormEntrySelectInput, + FormEntryTextarea, + handle_err, + idb, + ImageUploadSimple, + LayoutBottomButton, + LayoutView, + lib_fmt_geometry_point_coords, + lls, + MapLocationSelectEnvelope, + MarkerIndexedView, + PageToolbar, + } from "$root"; + import { + el_id, + fmt_geocode_address, + fmt_geolocation_address, + form_fields, + obj_keys_maxnum, + parse_float, + parse_geocode_address, + parse_geolocation_address, + parse_geolocation_point, + parse_geop_point, + parse_int, + parse_trade_key, + sleep, + str_trunc, + trade, + trade_keys, + vs_view_farms_products_add_submission, + type CallbackPromiseFull, + type CallbackPromiseGeneric, + type CallbackRoute, + type GeocoderReverseResult, + type GeolocationAddress, + type GeolocationPoint, + type ISelectOption, + type IViewBasis, + type IViewFarmsProductsAddSubmission, + type LcGeocodeCallback, + type LcGeocodeCurrentCallback, + type LcGuiAlertCallback, + type LcGuiConfirmCallback, + type LcPhotoAddMultipleCallback, + type ResolveFarmInfo, + type ResolveGeolocationInfo, + } from "@radroots/util"; + import { onMount } from "svelte"; + + const casl_param: Record< + "default", + { + index_map: Record<number, { label_desc: string }>; + } + > = { + default: { + index_map: { + 0: { + label_desc: `Add product information`, + }, + 1: { + label_desc: `Upload images`, + }, + 2: { + label_desc: `Product Price`, + }, + 3: { + label_desc: `Add Location`, + }, + 4: { + label_desc: `Review`, + }, + }, + }, + }; + + let { + basis, + }: { + basis: IViewBasis<{ + data: ResolveFarmInfo; + callback_route?: CallbackRoute<string>; + lc_gui_alert: LcGuiAlertCallback; + lc_gui_confirm: LcGuiConfirmCallback; + lc_geocode: LcGeocodeCallback; + lc_handle_farm_lot_add: CallbackPromiseGeneric<string>; + lc_handle_photo_add: LcPhotoAddMultipleCallback; + lc_handle_photo_envelope_edit: CallbackPromiseGeneric<number>; + lc_img_bin: CallbackPromiseFull<string, Uint8Array | undefined>; + lc_handle_tradepr_key_toggle: CallbackPromiseFull<boolean, string>; + lc_geop_current: LcGeocodeCurrentCallback; + lc_submit: CallbackPromiseGeneric<{ + data_s: IViewFarmsProductsAddSubmission; + farm_id: string; + farm_lot_id?: string; + geolocation_id?: string; + }>; + }>; + } = $props(); + + let loading_submit = $state(false); + + let data_s: IViewFarmsProductsAddSubmission | undefined = $state(undefined); + + let photo_paths = $state([]); + + let toggle_opt_addexisting = $state(false); + + let product_key_sel = $state(`cacao`); + let product_key_sel_input = $state(``); + let product_key_sel_toggle = $state(false); + + let product_process_sel = $state(`raw`); + let product_process_sel_input = $state(``); + let product_process_sel_toggle = $state(false); + + let product_description_input = $state(`cool description`); + + let product_location_sel = $state(``); + let product_location_map_toggle = $state(false); + + let product_price_cur_sel = $state(`usd`); + let product_price_input = $state(`4.50`); + let product_price_qty_unit_sel = $state(`lb`); + + let product_quantity_input = $state(`60`); + let product_quantity_unit_sel = $state(`kg`); + let product_quantity_label_sel = $state(`bag`); + + let tradepr_lgc_map_point: GeolocationPoint | undefined = $state(undefined); + let tradepr_lgc_map_geoc: GeocoderReverseResult | undefined = + $state(undefined); + + const tradepr_key_parsed = $derived(parse_trade_key(product_key_sel)); + + const product_process_list = $derived( + tradepr_key_parsed ? trade.key[tradepr_key_parsed].process : [], + ); + + const list_farm_geolocations: ResolveGeolocationInfo[] = $derived( + [ + basis.data?.geolocation ? basis.data?.geolocation : undefined, + ...(basis.data?.farm_lots?.length + ? [...basis.data?.farm_lots.map((i) => i.geolocation)] + : []), + ].filter((i) => typeof i !== `undefined`), + ); + + const product_farm_lot_id: string | undefined = $derived( + product_location_sel === `*map-location` || + !basis.data?.farm_lots?.length + ? undefined + : basis.data?.farm_lots.find( + (i) => i.geolocation.id === product_location_sel, + )?.id || undefined, + ); + + const product_geolocation_id: string | undefined = $derived( + product_location_sel === `*map-location` + ? undefined + : list_farm_geolocations.find((i) => i.id === product_location_sel) + ?.id || undefined, + ); + + const product_geolocation: ResolveGeolocationInfo | undefined = $derived( + list_farm_geolocations.find((i) => i.id === product_location_sel) || + undefined, + ); + + const product_geolocation_point: GeolocationPoint | undefined = $derived( + product_location_sel === `*map-location` + ? tradepr_lgc_map_point + : parse_geolocation_point(product_geolocation?.point) || undefined, + ); + + const product_geolocation_address: GeolocationAddress | undefined = + $derived( + product_location_sel === `*map-location` + ? parse_geocode_address(tradepr_lgc_map_geoc) + : parse_geolocation_address(product_geolocation?.address) || + undefined, + ); + + const entries_farm_location: ISelectOption<string>[] = $derived( + basis.data?.geolocation + ? [ + { + value: basis.data.geolocation.id, + label: + fmt_geolocation_address( + basis.data.geolocation.address, + ) || + lib_fmt_geometry_point_coords( + basis.data.geolocation.point, + ) || + ``, + }, + ] + : [], + ); + const entries_farm_lots_locations: ISelectOption<string>[] = $derived( + basis.data?.farm_lots?.length + ? basis.data.farm_lots + .filter( + (i) => + i.geolocation.address && + !entries_farm_location + .map((i) => i.value) + .includes(i.geolocation.id), + ) + .map(({ geolocation }) => ({ + value: geolocation.id, + label: `${geolocation.address.primary}, ${geolocation.address.admin}, ${geolocation.address.country}`, + })) + : [], + ); + + onMount(async () => { + try { + casl_init(0, obj_keys_maxnum(casl_param.default.index_map)); + } catch (e) { + handle_err(e, `on_mount`); + } + }); + + const handle_sel_key = async (value: string): Promise<void> => { + el_id(fmt_id(`key_wrap`))?.classList.remove(`layer-1-ring-apply`); + if (value === `*other`) { + await handle_tradepr_key_toggle(true); + } + }; + + const handle_tradepr_key_toggle = async ( + input_visible: boolean, + ): Promise<void> => { + product_key_sel_toggle = input_visible; + if (input_visible) { + product_key_sel_input = ``; + } else { + product_key_sel = ``; + product_key_sel_input = ``; + } + }; + + const handle_sel_process = async (value: string): Promise<void> => { + el_id(fmt_id(`process_wrap`))?.classList.remove(`layer-1-ring-apply`); + if (value === `*choose-product`) { + product_process_sel = ``; + el_id(fmt_id(`key_wrap`))?.classList.add(`layer-1-ring-apply`); + await sleep(1000); + el_id(fmt_id(`key_wrap`))?.classList.remove(`layer-1-ring-apply`); + } + }; + + const handle_product_process_toggle = async ( + input_visible: boolean, + ): Promise<void> => { + product_process_sel_toggle = input_visible; + if (input_visible) { + product_process_sel = ``; + product_process_sel_input = ``; + } + }; + + const handle_product_location_sel_map = async ( + value: string, + ): Promise<void> => { + el_id(fmt_id(`location_wrap`))?.classList.remove(`layer-1-ring-apply`); + if (value === `*map`) { + if (tradepr_lgc_map_point) tradepr_lgc_map_point = undefined; + if (tradepr_lgc_map_geoc) tradepr_lgc_map_geoc = undefined; + const geop = await basis.lc_geop_current(); + if (!geop) { + return void (await basis.lc_gui_alert( + `${$lls(`icu.failure_*`, { value: `${$lls(`icu.reading_*`, { value: `${$lls(`common.geocode`)}`.toLowerCase() })}` })}`, + )); + } + const geoc = await basis.lc_geocode(geop); + if (geoc) tradepr_lgc_map_geoc = geoc; + tradepr_lgc_map_point = parse_geop_point(geop); + product_location_map_toggle = true; + await sleep(1000); + product_location_sel = ``; + } + }; + + const continue_0 = async (): Promise<void> => { + for (const kv_id of [`key`, `process`, `description`]) { + const val = await idb.get(fmt_id(kv_id)); + if (!val) + return void basis.lc_gui_alert( + `${$lls(`farm.product.validation.${kv_id}.required`)}`, + ); + } + await casl_inc(); + }; + + const continue_1 = async (): Promise<void> => { + if (!photo_paths.length) + return void basis.lc_gui_alert( + `Upload a product photo`, //@todo + ); + await casl_inc(); + }; + + const continue_2 = async (): Promise<void> => { + await casl_inc(); + }; + + const continue_3 = async (): Promise<void> => { + if (!product_geolocation_point || !product_geolocation_address) + return void (await basis.lc_gui_alert( + `Select the product location`, //@todo + )); + const vp_data_s = vs_view_farms_products_add_submission.safeParse({ + product: product_key_sel_input || product_key_sel, + process: product_process_sel_input || product_process_sel, + description: product_description_input, + price_amount: parse_float(product_price_input, 1.0), + price_currency: product_price_cur_sel, + price_quantity_unit: product_price_qty_unit_sel, + quantity_amount: parse_int(product_quantity_input, 1), + quantity_unit: product_quantity_unit_sel, + quantity_label: product_quantity_label_sel, + photos: photo_paths, + geolocation_point: product_geolocation_point, + geolocation_address: product_geolocation_address, + } satisfies IViewFarmsProductsAddSubmission); + if (!vp_data_s.success) { + return void basis.lc_gui_alert( + `Please correct the following errors: ${vp_data_s.error}`, + ); + } + data_s = vp_data_s.data; + await casl_inc(); + }; + + const continue_4 = async (): Promise<void> => { + if (!data_s) return; + loading_submit = true; + await basis.lc_submit({ + data_s, + farm_id: basis.data.id, + farm_lot_id: product_farm_lot_id, + geolocation_id: product_geolocation_id, + }); + loading_submit = false; + }; + + const handle_continue = async (num_i?: number): Promise<void> => { + try { + const num = num_i ?? $casl_i; + switch (num) { + case 0: + return await continue_0(); + case 1: + return await continue_1(); + case 2: + return await continue_2(); + case 3: + return await continue_3(); + case 4: + return await continue_4(); + } + } catch (e) { + await handle_err(e, `handle_continue`); + } + }; +</script> + +<LayoutView basis={{ classes: `pb-h_lo_bottom_button_${$app_lo}` }}> + <PageToolbar + basis={{ + header: { + label: `${`${$lls(`common.farm`)}`} / ${`${$lls(`common.product`)}`}`, + callback_route: basis.callback_route || { route: `/farms` }, + }, + }} + /> + <div class={`flex flex-col w-full pt-4 px-4 justify-center items-center`}> + <div class={`flex flex-row w-full gap-2 justify-center items-center`}> + <ButtonHorizontalPairSlide + bind:toggle={toggle_opt_addexisting} + basis={{ + label_l: `${$lls(`common.create_product`)}`, + label_r: `${$lls(`common.add_from_existing`)}`, + }} + /> + </div> + </div> + {#if !toggle_opt_addexisting} + <LayoutBottomButton> + <ButtonRound + basis={{ + classes: `text-layer-0-glyph bg-layer-1-surface active:bg-layer-1-surface_a`, + loading: loading_submit, + label: + $casl_i === $casl_imax + ? `${$lls(`common.post`)}` + : `${$lls(`common.continue`)}`, + callback: async () => { + handle_continue(); + }, + }} + /> + </LayoutBottomButton> + <div + class={`flex flex-col w-full p-4 gap-4 justify-center items-center`} + > + <div class={`flex flex-row h-2 w-full justify-start items-center`}> + <p + class={`font-sans font-[500] text-sm text-layer-0-glyph/60 capitalize`} + > + {casl_param.default.index_map[$casl_i]?.label_desc} + </p> + </div> + <MarkerIndexedView + basis={{ + index_max: $casl_imax + 1, + index_curr: $casl_i, + callback_index: async (index) => { + casl_i.set(index); + }, + }} + /> + </div> + + <Carousel> + <CarouselItem basis={{ classes: `gap-4` }}> + <FormEntrySelectInput + bind:val_sel={product_key_sel} + bind:val_sel_input={product_key_sel_input} + basis={{ + id: `key`, + entry_label: `${$lls(`common.product`)}`, + visible_input: product_key_sel_toggle, + input_placeholder: `${$lls(`icu.enter_the_*`, { value: `${$lls(`icu.*_name`, { value: `${$lls(`common.product`)}` })}`.toLowerCase() })}`, + input_field: form_fields.product_key, + callback_visible: handle_tradepr_key_toggle, + callback_select: async ({ value }) => + await handle_sel_key(value), + select_entries: [ + { + value: ``, + label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.product`)}`.toLowerCase() })}`, + disabled: true, + }, + ...trade_keys.map((i) => ({ + value: i, + label: `${$lls(`trade.product.key.${i}.name`)}`, + })), + { + value: `*other`, + label: `${$lls(`common.other`)}`, + }, + ], + }} + /> + <FormEntrySelectInput + bind:val_sel={product_process_sel} + bind:val_sel_input={product_process_sel_input} + basis={{ + id: `process`, + entry_label: `${$lls(`common.process`)}`, + visible_input: product_process_sel_toggle, + input_placeholder: `${$lls(`icu.enter_the_*`, { value: `${$lls(`common.process`)}`.toLowerCase() })}`, + input_field: form_fields.product_process, + callback_visible: handle_product_process_toggle, + callback_select: async ({ value }) => + await handle_sel_process(value), + select_entries: product_process_list.length + ? [ + { + value: ``, + label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.process`)}`.toLowerCase() })}`, + disabled: true, + }, + ...product_process_list.map((i) => ({ + value: i, + label: `${$lls(`trade.product.key.${tradepr_key_parsed}.process.${i}`)}`, + })), + { + value: `*other`, + label: `${$lls(`common.other`)}`, + }, + ] + : [ + { + value: ``, + label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.process`)}`.toLowerCase() })}`, + disabled: true, + }, + { + value: `*choose-product`, + label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.product`)}`.toLowerCase() })}`, + }, + ].concat( + product_key_sel === `*other` + ? [ + ...trade.default.process.map( + (i) => ({ + value: i, + label: `${$lls(`trade.product.default.process.${i}`)}`, + }), + ), + { + value: `*other`, + label: `${$lls(`common.other`)}`, + }, + ] + : [], + ), + }} + /> + <FormEntryTextarea + bind:val={product_description_input} + basis={{ + id: `description`, + entry_label: `${$lls(`common.description`)}`, + field: form_fields.product_description, + placeholder: `${$lls(`common.describe_your_product`)}`, + }} + /> + </CarouselItem> + <CarouselItem basis={{ classes: `px-4` }}> + <ImageUploadSimple + bind:photo_paths + basis={{ + lc_handle_photo_add: basis.lc_handle_photo_add, + }} + /> + </CarouselItem> + <CarouselItem basis={{ classes: `gap-4` }}> + <FormEntryPrice + bind:val_input_price={product_price_input} + bind:val_sel_currency={product_price_cur_sel} + bind:val_sel_quantity_unit={product_price_qty_unit_sel} + basis={{ + id: `price`, + entry_label: `price`, + input_placeholder: `Enter price`, + }} + /> + <FormEntryQuantity + bind:val_input_quantity={product_quantity_input} + bind:val_sel_quantity_unit={product_quantity_unit_sel} + bind:val_sel_quantity_label={product_quantity_label_sel} + basis={{ + id: `quantity`, + entry_label: `quantity`, + input_placeholder: `Enter quantity`, + }} + /> + </CarouselItem> + <CarouselItem> + <FormEntrySelect + bind:val={product_location_sel} + basis={{ + id: `location`, + entry_label: `${$lls(`common.location`)}`, + callback: async ({ value }) => + await handle_product_location_sel_map(value), + entries: tradepr_lgc_map_geoc + ? [ + { + value: ``, + label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.location`)}`.toLowerCase() })}`, + disabled: true, + }, + { + value: `*map`, + label: `${$lls(`icu.choose_on_*`, { value: `${$lls(`common.map`)}`.toLowerCase() })}`, + }, + { + value: `*map-location`, + label: `${str_trunc(fmt_geocode_address(tradepr_lgc_map_geoc))} (map)`, + }, + ...entries_farm_location, + ...entries_farm_lots_locations, + ] + : [ + { + value: ``, + label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.location`)}`.toLowerCase() })}`, + disabled: true, + }, + ...entries_farm_location, + ...entries_farm_lots_locations, + { + value: `*map`, + label: `${$lls(`icu.choose_on_*`, { value: `${$lls(`common.map`)}`.toLowerCase() })}`, + }, + ], + }} + /> + </CarouselItem> + <CarouselItem> + <div class={`flex flex-col w-full justify-center items-center`}> + <FarmsProductsReviewCard + basis={{ + data: data_s, + }} + /> + </div> + </CarouselItem> + </Carousel> + {:else} + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {`Coming soon!`} + </p> + {/if} +</LayoutView> +<MapLocationSelectEnvelope + bind:map_geop={tradepr_lgc_map_point} + bind:map_geoc={tradepr_lgc_map_geoc} + basis={{ + visible: product_location_map_toggle, + farm_info: basis.data, + lc_geocode: basis.lc_geocode, + lc_submit: async ({ map_geoc, map_geop }) => { + try { + if (map_geoc && map_geop) + product_location_sel = `*map-location`; + else product_location_sel = ``; + product_location_map_toggle = false; + } catch (e) { + await handle_err(e, `lc_submit`); + } + }, + }} +/> diff --git a/apps-lib/src/lib/view/farms-products-view.svelte b/apps-lib/src/lib/view/farms-products-view.svelte @@ -0,0 +1,54 @@ +<script lang="ts"> + import { + ButtonLabelDashed, + LayoutPage, + LayoutView, + PageToolbar, + lls, + } from "$root"; + import { + type CallbackPromise, + type CallbackRoute, + type IViewBasis, + type ResolveFarmProductInfo, + } from "@radroots/util"; + + let { + basis, + }: { + basis: IViewBasis<{ + data: ResolveFarmProductInfo[]; + callback_route?: CallbackRoute<string>; + lc_handle_farm_product_add: CallbackPromise; + }>; + } = $props(); +</script> + +<LayoutView> + <PageToolbar + basis={{ + header: { + label: `${$lls(`common.products`)}`, + callback_route: basis.callback_route || { route: `/farms` }, + }, + }} + /> + <LayoutPage> + {#if basis.data?.length} + {#each basis.data as li} + <div class={`flex flex-row justify-start items-center`}> + {JSON.stringify(li)} + </div> + {/each} + {:else} + <ButtonLabelDashed + basis={{ + label: `Add product`, + callback: async () => { + await basis.lc_handle_farm_product_add(); + }, + }} + /> + {/if} + </LayoutPage> +</LayoutView> diff --git a/apps-lib/src/lib/view/farms-view.svelte b/apps-lib/src/lib/view/farms-view.svelte @@ -0,0 +1,264 @@ +<script lang="ts"> + import Empty from "$lib/components/lib/empty.svelte"; + import { + ButtonSimple, + Glyph, + handle_err, + LayoutPage, + LayoutView, + liblocale, + lls, + Map, + MapMarkerArea, + PageToolbar, + } from "$root"; + import { + geol_lat_fmt, + geol_lng_fmt, + lib_address_fmt, + parse_geom_point_tup, + parse_tup_geop_point, + type CallbackPromiseGeneric, + type CallbackRoute, + type GeolocationPointTuple, + type IViewBasis, + type LcGeocodeCallback, + type ResolveFarmInfo, + } from "@radroots/util"; + import { onMount } from "svelte"; + + let { + basis, + }: { + basis: IViewBasis<{ + data: ResolveFarmInfo; + callback_route?: CallbackRoute<string>; + lc_geocode: LcGeocodeCallback; + lc_handle_farm_lot_add: CallbackPromiseGeneric<string>; + lc_handle_farm_products_view: CallbackPromiseGeneric<string>; + lc_handle_farm_orders_view: CallbackPromiseGeneric<string>; + }>; + } = $props(); + + let map: maplibregl.Map | undefined = $state(undefined); + let map_center: GeolocationPointTuple = $state([0, 0]); + + onMount(async () => { + try { + if (map && basis.data?.geolocation?.point) { + map_center = parse_geom_point_tup(basis.data.geolocation.point); + map.setCenter(map_center); + map.setZoom(12); + } + } catch (e) { + handle_err(e, `on_mount`); + } + }); + + const map_geop = $derived(parse_tup_geop_point(map_center)); + + const info_addr_fmt = $derived( + basis.data?.geolocation?.address + ? lib_address_fmt(basis.data.geolocation.address) + : ``, + ); + + const info_geop_lat = $derived( + basis.data?.geolocation?.point?.coordinates + ? geol_lat_fmt( + basis.data.geolocation.point.coordinates[1], + `dms`, + $liblocale, + 3, + ) + : ``, + ); + + const info_geop_lng = $derived( + basis.data?.geolocation?.point?.coordinates + ? geol_lng_fmt( + basis.data.geolocation.point.coordinates[0], + `dms`, + $liblocale, + 3, + ) + : ``, + ); +</script> + +<LayoutView> + <PageToolbar + basis={{ + header: { + label: `${$lls(`common.farms`)} / ${basis.data.name || ``}`, + callback_route: basis.callback_route, + }, + }} + /> + <LayoutPage> + <div + class={`flex flex-row h-[12rem] w-full justify-start items-center`} + > + <div + class={`flex flex-col basis-1/2 h-full p-4 gap-2 justify-start items-center`} + > + <div class={`flex flex-col w-full justify-start items-center`}> + <div + class={`flex flex-row w-full justify-start items-center`} + > + <p + class={`font-sans font-[500] text-lg text-layer-0-glyph`} + > + {info_addr_fmt} + </p> + </div> + <div + class={`flex flex-row w-full justify-start items-center`} + > + <p + class={`font-sans font-[500] text-lg text-layer-0-glyph tracking-tight`} + > + {info_geop_lat && info_geop_lng + ? `${info_geop_lat}, ${info_geop_lng}` + : ``} + </p> + </div> + </div> + </div> + <div + class={`flex flex-col basis-1/2 h-full justify-start items-center`} + > + <div + class={`flex flex-col h-full w-full p-4 gap-4 justify-start items-center bg-layer-1-surface rounded-2xl`} + > + <p + class={`font-sans font-[500] text-sm text-layer-0-glyph`} + > + {`Farm Info`} + </p> + <div + class={`flex flex-col w-full gap-1 justify-start items-center`} + > + <div + class={`flex flex-row w-full gap-4 justify-between items-center`} + > + <p + class={`font-sans font-[400] text-layer-0-glyph`} + > + {`Farm Size:`} + </p> + + {#if basis.data.area && basis.data.area_unit} + <p + class={`font-sans font-[400] text-layer-0-glyph`} + > + {`${basis.data.area} ${basis.data.area_unit}`} + </p> + {:else} + <div + class={`flex flex-row gap-line justify-start items-center`} + > + <p + class={`font-sans font-[400] text-layer-0-glyph_pl`} + > + {`Add`} + </p> + <Glyph + basis={{ + classes: `text-layer-0-glyph_pl`, + dim: `xs`, + key: `caret-right`, + }} + /> + </div> + {/if} + </div> + <div + class={`flex flex-row w-full gap-4 justify-between items-center`} + > + <p + class={`font-sans font-[400] text-layer-0-glyph`} + > + {`Farm Lots:`} + </p> + <p + class={`font-sans font-[400] text-layer-0-glyph`} + > + {`${basis.data.farm_lots?.length || 0}`} + </p> + </div> + <div + class={`flex flex-row w-full gap-4 justify-between items-center`} + > + <p + class={`font-sans font-[400] text-layer-0-glyph`} + > + {`Products:`} + </p> + <p + class={`font-sans font-[400] text-layer-0-glyph`} + > + {`${0}`} + </p> + </div> + <div + class={`flex flex-row w-full gap-4 justify-between items-center`} + > + <p + class={`font-sans font-[400] text-layer-0-glyph`} + > + {`Orders:`} + </p> + <p + class={`font-sans font-[400] text-layer-0-glyph`} + > + {`${0}`} + </p> + </div> + </div> + </div> + </div> + </div> + <div class={`flex flex-col w-full gap-3 justify-center items-center`}> + <ButtonSimple + basis={{ + label: `View Products`, + callback: async () => { + await basis.lc_handle_farm_products_view(basis.data.id); + }, + }} + /> + <ButtonSimple + basis={{ + label: `View Orders`, + callback: async () => { + await basis.lc_handle_farm_orders_view(basis.data.id); + }, + }} + /> + </div> + <div + class={`flex flex-col flex-shrink-0 h-[16rem] w-full justify-center items-center rounded-2xl overflow-hidden`} + > + <Map + bind:map + basis={{ + interactive: false, + }} + > + <MapMarkerArea + {map_geop} + basis={{ + no_drag: true, + lc_geocode: basis.lc_geocode, + }} + /> + </Map> + </div> + <div + class={`flex flex-col h-[12rem] w-full justify-center items-center`} + > + <Empty /> + </div> + </LayoutPage> +</LayoutView> diff --git a/apps-lib/src/lib/view/farms.svelte b/apps-lib/src/lib/view/farms.svelte @@ -0,0 +1,89 @@ +<script lang="ts"> + import ButtonLabelDashed from "$lib/components/button/button-label-dashed.svelte"; + import { + Fade, + FarmsDisplayLiEl, + GlyphButtonSimple, + handle_err, + kv_init_page, + LayoutPage, + LayoutView, + lls, + PageToolbar, + } from "$root"; + import { + type CallbackPromise, + type CallbackPromiseGeneric, + type CallbackRoute, + type IViewBasis, + type LcGeocodeCallback, + type ResolveFarmInfo, + } from "@radroots/util"; + import { onMount } from "svelte"; + + let { + basis, + }: { + basis: IViewBasis<{ + data?: ResolveFarmInfo[]; + callback_route?: CallbackRoute<string>; + lc_geocode: LcGeocodeCallback; + lc_handle_farm_add: CallbackPromise; + lc_handle_farm_view: CallbackPromiseGeneric<string>; + }>; + } = $props(); + + onMount(async () => { + try { + if (!basis.kv_init_prevent) await kv_init_page(); + } catch (e) { + handle_err(e, `on_mount`); + } + }); +</script> + +<LayoutView> + <PageToolbar + basis={{ + header: { + label: `${$lls(`common.farms`)}`, + callback_route: basis.callback_route, + }, + }} + > + {#snippet header_option()} + {#if basis.data?.length} + <Fade> + <GlyphButtonSimple + basis={{ + label: `${$lls(`icu.add_*`, { value: `${$lls(`common.farm`)}` })}`, + callback: async () => { + await basis.lc_handle_farm_add(); + }, + }} + ></GlyphButtonSimple> + </Fade> + {/if} + {/snippet} + </PageToolbar> + <LayoutPage> + {#if basis.data?.length} + {#each basis.data as li} + <FarmsDisplayLiEl + basis={li} + lc_geocode={basis.lc_geocode} + lc_handle_farm_view={basis.lc_handle_farm_view} + /> + {/each} + {:else} + <ButtonLabelDashed + basis={{ + label: `Add farm`, + callback: async () => { + await basis.lc_handle_farm_add(); + }, + }} + /> + {/if} + </LayoutPage> +</LayoutView> diff --git a/apps-lib/src/lib/view/home.svelte b/apps-lib/src/lib/view/home.svelte @@ -1,26 +1,37 @@ <script lang="ts"> import { - FloatTabs, + ButtonSimple, + handle_err, kv_init_page, + LayoutPage, LayoutView, lls, + NavigationTabs, PageToolbar, + } from "$root"; + import { type CallbackPromise, - type ViewBasis, - } from "$lib"; - import { onDestroy, onMount } from "svelte"; + type IViewBasis, + type ResolveAccountInfo, + } from "@radroots/util"; + import { onMount } from "svelte"; - export let basis: ViewBasis<{ - lc_handle_farm: CallbackPromise; - }>; + let { + basis, + }: { + basis: IViewBasis<{ + data?: ResolveAccountInfo; + lc_handle_farms: CallbackPromise; + lc_handle_products: CallbackPromise; + }>; + } = $props(); onMount(async () => { - if (!basis.kv_init_prevent) await kv_init_page(); - if (basis.lc_on_mount) await basis.lc_on_mount(); - }); - - onDestroy(async () => { - if (basis.lc_on_destroy) await basis.lc_on_destroy(); + try { + if (!basis.kv_init_prevent) await kv_init_page(); + } catch (e) { + handle_err(e, `on_mount`); + } }); </script> @@ -32,19 +43,25 @@ }, }} /> - <div class={`flex flex-col w-full px-4 gap-4 justify-center items-center`}> - <div class={`flex flex-col w-full gap-5 justify-center items-center`}> - <button - class={`group flex flex-row h-[3.5rem] w-full justify-center items-center rounded-touch bg-layer-1-surface layer-1-active-surface layer-1-active-ring`} - on:click={basis.lc_handle_farm} - > - <p - class={`font-sans font-[700] text-xl text-layer-0-glyph capitalize tracking-wider opacity-active`} - > - {`${$lls(`common.farm_land`)}`} - </p> - </button> - </div> - </div> + <LayoutPage> + <ButtonSimple + basis={{ + label: `${$lls(`common.farms`)}`, + callback: async () => { + await basis.lc_handle_farms(); + }, + }} + /> + {#if basis.data?.farms?.length} + <ButtonSimple + basis={{ + label: `${$lls(`common.products`)}`, + callback: async () => { + await basis.lc_handle_products(); + }, + }} + /> + {/if} + </LayoutPage> </LayoutView> -<FloatTabs /> +<NavigationTabs /> diff --git a/apps-lib/src/lib/view/notifications.svelte b/apps-lib/src/lib/view/notifications.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import { FloatTabs, LayoutView, lls, PageToolbar } from "$lib"; + import { LayoutView, lls, NavigationTabs, PageToolbar } from "$root"; let notifications: any[] = []; </script> @@ -28,4 +28,4 @@ {/if} </div> </LayoutView> -<FloatTabs /> +<NavigationTabs /> diff --git a/apps-lib/src/lib/view/profile.svelte b/apps-lib/src/lib/view/profile.svelte @@ -0,0 +1,249 @@ +<script lang="ts"> + import { lls } from "$lib/locale/i18n"; + import { + FloatPageButton, + Glyph, + handle_err, + ImagePath, + ImageUploadAddPhoto, + kv_init_page, + NavigationTabs, + } from "$root"; + import { + ascii, + type CallbackPromise, + type CallbackPromiseFull, + type CallbackPromiseGeneric, + type CallbackPromiseReturn, + type IViewBasis, + type LcPhotoAddCallback, + type ResolveProfileInfo, + } from "@radroots/util"; + import { onMount } from "svelte"; + + let { + basis, + photo_path_opt = $bindable(``), + loading_photo_upload = $bindable(false), + }: { + basis: IViewBasis<{ + data?: ResolveProfileInfo; + lc_handle_back: CallbackPromiseGeneric<string>; + lc_handle_photo_add: LcPhotoAddCallback; + lc_handle_photo_options: CallbackPromise; + lc_fs_read_bin: CallbackPromiseFull<string, Uint8Array | undefined>; + lc_handle_edit_profile_name: CallbackPromise; + lc_handle_edit_profile_name_confirm: CallbackPromiseReturn<boolean>; + lc_handle_edit_profile_display_name: CallbackPromise; + lc_handle_edit_profile_about: CallbackPromise; + }>; + photo_path_opt: string; + loading_photo_upload: boolean; + } = $props(); + + type ViewDisplay = `photos` | `following` | `followers`; + let view_display: ViewDisplay = $state(`photos`); + + onMount(async () => { + try { + if (!basis.kv_init_prevent) await kv_init_page(); + if (basis.lc_on_mount) await basis.lc_on_mount(); + } catch (e) { + handle_err(e, `on_mount`); + } + }); + + const profile_photo = $derived( + basis.data?.profile_photos?.find((i) => i.primary), + ); + + const photo_overlay_visible = $derived(!!(profile_photo || photo_path_opt)); + + const classes_photo_overlay_glyph = $derived( + photo_overlay_visible ? `text-white` : `text-layer-0-glyph`, + ); + + const classes_photo_overlay_glyph_opt = $derived( + photo_overlay_visible ? `text-gray-300` : `text-layer-0-glyph`, + ); + + const classes_photo_overlay_glyph_opt_selected = $derived( + photo_overlay_visible ? `text-white` : `text-layer-1-glyph_d`, + ); +</script> + +<div + class={`relative flex flex-col min-h-[525px] h-[525px] w-full justify-center items-center bg-layer-2-surface fade-in`} +> + <FloatPageButton + basis={{ + posx: `left`, + glyph: `arrow-left`, + loading: loading_photo_upload, + callback: async () => { + if (basis.data) await basis.lc_handle_back(basis.data.id); + }, + }} + /> + <FloatPageButton + basis={{ + posx: `right`, + glyph: `images-square`, + loading: loading_photo_upload, + callback: basis.lc_handle_photo_options, + }} + /> + {#if profile_photo} + <ImagePath + basis={{ + path: profile_photo.media_image.url, + }} + /> + {:else if photo_path_opt} + <ImagePath basis={{ path: photo_path_opt }} /> + {:else} + <div class={`flex flex-row justify-start items-center -translate-y-8`}> + <ImageUploadAddPhoto + bind:photo_path={photo_path_opt} + basis={{ + lc_handle_photo_add: basis.lc_handle_photo_add, + }} + /> + </div> + {/if} + <div + class={`absolute bottom-0 left-0 flex flex-col h-[calc(100%-100%/1.618)] w-full px-6 gap-2 justify-end items-center`} + > + <div + class={`flex flex-col w-full gap-[2px] justify-center items-center`} + > + <div class={`flex flex-row h-10 w-full justify-start items-center`}> + <button + class={`group flex flex-row justify-center items-center`} + onclick={basis.lc_handle_edit_profile_display_name} + > + <p + class={`font-sansd font-[600] text-[2rem] ${classes_photo_overlay_glyph} ${basis.data?.name ? `` : `capitalize opacity-active`} el-re`} + > + {basis.data?.name + ? basis.data.name + : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.profile_name`)}` })}`}`} + </p> + </button> + </div> + <div + class={`flex flex-row w-full gap-[6px] justify-start items-center`} + > + <button + class={`group flex flex-row justify-center items-center`} + onclick={async () => { + if (basis.data?.name) { + const confirm = + basis.lc_handle_edit_profile_name_confirm(); + if (!confirm) return; + } + await basis.lc_handle_edit_profile_name(); + }} + > + <p + class={`font-sansd font-[600] text-[1.1rem] ${classes_photo_overlay_glyph} ${basis.data?.name ? `` : `capitalize opacity-active`} el-re`} + > + {basis.data?.name + ? `@${basis.data.name}` + : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.username`)}` })}`}`} + </p> + </button> + <p + class={`font-sans font-[400] ${classes_photo_overlay_glyph}`} + > + {ascii.bullet} + </p> + <button + class={`flex flex-row justify-center items-center`} + onclick={async () => { + alert(`@todo!`); + }} + > + <Glyph + basis={{ + classes: `${classes_photo_overlay_glyph}`, + dim: `xs`, + + key: `link-simple`, + }} + /> + </button> + </div> + <div class={`flex flex-row w-full justify-start items-center`}> + <button + class={`group flex flex-row justify-center items-center`} + onclick={basis.lc_handle_edit_profile_about} + > + <p + class={`font-sansd font-[400] text-[1.1rem] ${classes_photo_overlay_glyph} ${basis.data?.about ? `` : `capitalize opacity-active`}`} + > + {basis.data?.about + ? `@${basis.data.about}` + : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.bio`)}` })}`}`} + </p> + </button> + </div> + </div> + <div + class={`flex flex-row w-full pt-2 pb-6 gap-2 justify-start items-center`} + > + <button + class={`flex flex-row justify-center items-center`} + onclick={async () => { + view_display = `photos`; + }} + > + <p + class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `photos` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`} + > + {`photos`} + </p> + </button> + <button + class={`flex flex-row justify-center items-center`} + onclick={async () => { + view_display = `following`; + }} + > + <p + class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `following` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`} + > + {`following`} + </p> + </button> + <button + class={`flex flex-row justify-center items-center`} + onclick={async () => { + view_display = `followers`; + }} + > + <p + class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `followers` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`} + > + {`followers`} + </p> + </button> + </div> + </div> +</div> +<div class={`flex flex-col w-full min-h-[500px] justify-start items-center`}> + {#if view_display === `photos`} + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {view_display} + </p> + {:else if view_display === `following`} + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {view_display} + </p> + {:else if view_display === `followers`} + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {view_display} + </p> + {/if} +</div> +<NavigationTabs /> diff --git a/apps-lib/src/lib/view/search.svelte b/apps-lib/src/lib/view/search.svelte @@ -1,51 +1,76 @@ <script lang="ts"> import { - cfg_app, - debounce_input, - FloatTabs, fmt_id, Glyph, + handle_err, Input, kv_init_page, + LayoutPage, LayoutView, lls, + NavigationTabs, PageToolbar, SearchResultDisplay, + } from "$root"; + import { + cfg_app, + debounce_callback, SearchService, - type ISearchLoadData, - type ISearchResultDisplayCallbacks, + type CallbackPromise, + type CallbackPromiseGeneric, + type IViewBasis, + type IViewSearchData, + type ResolveGeolocationInfo, + type ResolveProfileInfo, type SearchServiceResult, - type ViewBasisLoadData, - } from "$lib"; + } from "@radroots/util"; import { onDestroy, onMount } from "svelte"; - type LoadData = ISearchLoadData; + let { + basis, + }: { + basis: IViewBasis<{ + data: IViewSearchData; + lc_handle_back: CallbackPromise; + lc_handle_search_geolocation: CallbackPromiseGeneric<ResolveGeolocationInfo>; + lc_handle_search_profile: CallbackPromiseGeneric<ResolveProfileInfo>; + lc_handle_search_nostr_relay: CallbackPromiseGeneric<{ + id: string; + }>; + }>; + } = $props(); - export let basis: ViewBasisLoadData< - ISearchResultDisplayCallbacks & {}, - LoadData - >; - let load_data: LoadData = undefined; + $effect(() => { + console.log(JSON.stringify(basis.data, null, 4), `basis.data`); + }); - let client_search: SearchService | undefined = undefined; - let search_val = ``; - let search_results: SearchServiceResult[] = []; + let search_service: SearchService | undefined = undefined; + let search_val = $state(``); + let search_results: SearchServiceResult[] = $state([]); onMount(async () => { - search_val = ``; - if (!basis.kv_init_prevent) await kv_init_page(); - if (basis.lc_on_mount) await basis.lc_on_mount(); - load_data = await basis.lc_load_data(); - if (load_data) client_search = new SearchService(load_data); + try { + search_val = ``; + if (!basis.kv_init_prevent) await kv_init_page(); + if (basis.lc_on_mount) await basis.lc_on_mount(); + search_service = new SearchService(basis.data); + } catch (e) { + handle_err(e, `on_mount`); + } }); + const handle_search_input = debounce_callback((val: string) => { + console.log(`val `, val); + if (search_service) search_results = search_service.search(val); + }, cfg_app.debounce.search); + onDestroy(async () => { - if (basis.lc_on_destroy) await basis.lc_on_destroy(); + try { + if (basis.lc_on_destroy) await basis.lc_on_destroy(); + } catch (e) { + handle_err(e, `on_destroy`); + } }); - - const handle_search_input = debounce_input((val: string) => { - if (client_search) search_results = client_search.search(val); - }, cfg_app.debounce.search); </script> <LayoutView> @@ -55,7 +80,7 @@ callback: basis.lc_handle_back, }} /> - <div class={`flex flex-col w-full px-4 gap-6 justify-center items-center`}> + <LayoutPage> <div class={`relative flex flex-row w-full justify-center items-center bg-layer-1-surface rounded-2xl`} > @@ -63,10 +88,10 @@ basis={{ classes: `absolute left-4 text-layer-0-glyph-shade`, dim: `sm`, - weight: `bold`, + key: `magnifying-glass`, }} - ></Glyph> + /> <Input bind:value={search_val} basis={{ @@ -76,23 +101,23 @@ placeholder: `Enter search query`, callback: async ({ value }) => handle_search_input(value), }} - ></Input> + /> </div> <div class={`flex flex-col w-full gap-4 justify-center items-center`}> {#each search_results as li (li.id)} <SearchResultDisplay basis={{ result: li, - lc_handle_result_location_gcs: - basis.lc_handle_result_location_gcs, - lc_handle_result_nostr_profile: - basis.lc_handle_result_nostr_profile, - lc_handle_result_nostr_relay: - basis.lc_handle_result_nostr_relay, + lc_handle_search_geolocation: + basis.lc_handle_search_geolocation, + lc_handle_search_profile: + basis.lc_handle_search_profile, + lc_handle_search_nostr_relay: + basis.lc_handle_search_nostr_relay, }} /> {/each} </div> - </div> + </LayoutPage> </LayoutView> -<FloatTabs /> +<NavigationTabs /> diff --git a/apps-lib/src/lib/view/settings-nostr.svelte b/apps-lib/src/lib/view/settings-nostr.svelte @@ -1,134 +0,0 @@ -<script lang="ts"> - import { - LayoutTrellis, - LayoutView, - PageToolbar, - Trellis, - ascii, - handle_err, - lls, - type ISelectOption, - } from "$lib"; - import { onMount } from "svelte"; - - const page_param: { - select_options: ISelectOption<string>[]; - } = { - select_options: [ - { - value: ascii.bullet, - label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.photo_hosting`)}`.toLowerCase() })}`, - disabled: true, - }, - { - value: `^radroots`, - label: `https://radroots.market`, - }, - { - value: `*add`, - label: `${$lls(`icu.add_*`, { value: `${$lls(`common.upload_url`)}`.toLowerCase() })}`, - }, - { - value: `*disable`, - label: `${$lls(`common.disable_uploads`)}`, - }, - ], - }; - - let nostr_photohosting_sel_val = ``; - let nostr_photohosting_sel_label = ``; - - onMount(async () => { - try { - await init_page(); - } catch (e) { - } finally { - } - }); - - const init_page = async (): Promise<void> => { - try { - nostr_photohosting_sel_val = `^radroots`; //@todo - } catch (e) { - await handle_err(e, `init_page`); - } - }; - - $: nostr_photohosting_sel_label = nostr_photohosting_sel_val - ? page_param.select_options.filter( - (i) => i.value === nostr_photohosting_sel_val, - )?.[0].label - : ``; - - const handle_select_option = async ( - option_value: string, - ): Promise<void> => { - try { - if (!option_value.startsWith(`*`)) { - nostr_photohosting_sel_val = option_value; - return; - } - nostr_photohosting_sel_val = ``; - alert(`@todo!`); - nostr_photohosting_sel_val = `^radroots`; - } catch (e) { - await handle_err(e, `handle_select_option`); - } - }; -</script> - -<LayoutView> - <PageToolbar - basis={{ - header: { - label: `${$lls(`common.nostr`)}`, - }, - }} - /> - <LayoutTrellis> - <Trellis - basis={{ - args: { - layer: 1, - list: [ - { - hide_active: true, - select: { - label: { - left: [ - { - value: `Photo Hosting`, - classes: `capitalize`, - }, - ], - }, - display: { - loading: !nostr_photohosting_sel_val, - label: { - value: nostr_photohosting_sel_label, - }, - }, - el: { - value: nostr_photohosting_sel_val, - options: [ - { - entries: page_param.select_options, - }, - ], - callback: async ({ value }) => { - await handle_select_option(value); - }, - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - }, - }, - ], - }, - }} - /> - </LayoutTrellis> -</LayoutView> diff --git a/apps-lib/src/lib/view/settings-profile-edit.svelte b/apps-lib/src/lib/view/settings-profile-edit.svelte @@ -1,138 +0,0 @@ -<script lang="ts"> - import { - app_notify, - fmt_id, - Input, - kv_init_page, - LayoutView, - lls, - Nav, - qp_nostr_pk, - qp_rkey, - TextArea, - type CallbackPromise, - type ISettingsNostrProfileEditLoadData, - type ViewBasisLoadData, - } from "$lib"; - import { onDestroy, onMount } from "svelte"; - - type LoadData = ISettingsNostrProfileEditLoadData; - - export let basis: ViewBasisLoadData< - { - lc_submit: CallbackPromise; - }, - LoadData - >; - let load_data: LoadData = undefined; - - let val_input_init = ``; - let val_input = ``; - - onMount(async () => { - if (!basis.kv_init_prevent) await kv_init_page(); - if (basis.lc_on_mount) await basis.lc_on_mount(); - if (!$qp_rkey || !$qp_nostr_pk) - return void app_notify.set( - `${$lls(`error.page.load.query_param`)}`, - ); - load_data = await basis.lc_load_data(); - if (load_data?.field_val) { - val_input = load_data.field_val; - val_input_init = load_data.field_val; - } - }); - - onDestroy(async () => { - if (basis.lc_on_destroy) await basis.lc_on_destroy(); - }); - - $: translated_field_key = load_data?.field_key - ? `${$lls(`models.nostr_profile.fields.${load_data.field_key}.label`)}` - : ``; - $: val_input_delta = val_input_init !== val_input; -</script> - -<LayoutView> - {#if load_data} - <div - class={`flex flex-col w-full pt-4 px-4 gap-1 justify-start items-center fade-in`} - > - <div class={`flex flex-row w-full pl-2 justify-start items-center`}> - <p - class={`font-sans text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {translated_field_key.replace(/profile /i, ``)} - </p> - </div> - {#if load_data.field_key === `about`} - <TextArea - bind:value={val_input} - basis={{ - id: fmt_id(load_data.field_key), - classes: `min-h-[12rem] pl-4`, - sync: true, - layer: 1, - placeholder: `${$lls(`icu.enter_*`, { value: `${translated_field_key}`.toLowerCase() })}`, - /*field: { - charset: - nostr_profile_form_fields[load_data.field_key].charset, - validate: - nostr_profile_form_fields[load_data.field_key] - .validation, - validate_keypress: true, - },*/ - callback_keydown: async ({ key_s }) => { - if (key_s && val_input_delta) - await basis.lc_submit(); - }, - }} - ></TextArea> - {:else} - <Input - bind:value={val_input} - basis={{ - id: fmt_id(load_data.field_key), - classes: `rounded-touch pl-4`, - sync: true, - layer: 1, - placeholder: `${$lls(`icu.enter_*`, { value: `${translated_field_key}`.toLowerCase() })}`, - /*field: { - charset: - nostr_profile_form_fields[load_data.field_key].charset, - validate: - nostr_profile_form_fields[load_data.field_key] - .validation, - validate_keypress: true, - },*/ - callback_keydown: async ({ key_s }) => { - if (key_s && val_input_delta) - await basis.lc_submit(); - }, - }} - ></Input> - {/if} - </div> - {/if} -</LayoutView> -<Nav - basis={{ - prev: { - label: `${$lls(`common.profile`)}`, - route: `/settings/profile`, - prevent_route: val_input_delta - ? { - callback: async () => { - if (val_input_delta) await basis.lc_submit(); - }, - } - : undefined, - }, - title: { - label: { - classes: `capitalize`, - value: `${$lls(`icu.edit_*`, { value: `${$lls(`common.profile`)}` })}`, - }, - }, - }} -/> diff --git a/apps-lib/src/lib/view/settings-profile.svelte b/apps-lib/src/lib/view/settings-profile.svelte @@ -1,243 +0,0 @@ -<script lang="ts"> - import { - ascii, - FloatPageButton, - FloatTabs, - Glyph, - ImageBlob, - ImagePath, - ImageUploadAddPhoto, - kv_init_page, - lls, - type CallbackPromise, - type CallbackPromiseFull, - type CallbackPromiseReturn, - type ISettingsNostrProfileLoadData, - type ViewBasisLoadData, - } from "$lib"; - import { onDestroy, onMount } from "svelte"; - - export let bv_photo_path_opt = ``; - - type LoadData = ISettingsNostrProfileLoadData; - - export let basis: ViewBasisLoadData< - { - lc_handle_photo_add: CallbackPromiseReturn<string | undefined>; - lc_handle_photo_options: CallbackPromise; - lc_fs_read_bin: CallbackPromiseFull<string, Uint8Array | undefined>; - lc_handle_edit_profile_name: CallbackPromise; - lc_handle_edit_profile_name_confirm: CallbackPromiseReturn<boolean>; - lc_handle_edit_profile_display_name: CallbackPromise; - lc_handle_edit_profile_about: CallbackPromise; - }, - LoadData - >; - let load_data: LoadData = undefined; - - let loading_photo_upload = false; - type ViewDisplay = `photos` | `following` | `followers`; - let view_display: ViewDisplay = `photos`; - - onMount(async () => { - if (!basis.kv_init_prevent) await kv_init_page(); - if (basis.lc_on_mount) await basis.lc_on_mount(); - load_data = await basis.lc_load_data(); - }); - - onDestroy(async () => { - if (basis.lc_on_destroy) await basis.lc_on_destroy(); - }); - - $: photo_overlay_visible = - load_data?.nostr_profile?.picture || bv_photo_path_opt; - $: classes_photo_overlay_glyph = photo_overlay_visible - ? `text-white` - : `text-layer-0-glyph`; - $: classes_photo_overlay_glyph_opt = photo_overlay_visible - ? `text-gray-300` - : `text-layer-0-glyph`; - $: classes_photo_overlay_glyph_opt_selected = photo_overlay_visible - ? `text-white` - : `text-layer-1-glyph`; -</script> - -<div - class={`relative flex flex-col min-h-[525px] h-[525px] w-full justify-center items-center bg-layer-2-surface fade-in`} -> - <FloatPageButton - basis={{ - posx: `left`, - glyph: `arrow-left`, - loading: loading_photo_upload, - callback: basis.lc_handle_back, - }} - /> - <FloatPageButton - basis={{ - posx: `right`, - glyph: `images-square`, - loading: loading_photo_upload, - callback: basis.lc_handle_photo_options, - }} - /> - {#if load_data?.nostr_profile?.picture} - <ImagePath - basis={{ - path: load_data.nostr_profile.picture, - }} - /> - {:else if bv_photo_path_opt} - {#await basis.lc_fs_read_bin(bv_photo_path_opt) then file_data} - <ImageBlob - basis={{ - data: file_data, - }} - /> - {/await} - {:else} - <div class={`flex flex-row justify-start items-center -translate-y-8`}> - <ImageUploadAddPhoto - bind:bv_photo_path={bv_photo_path_opt} - basis={{ - lc_handle_photo_add: basis.lc_handle_photo_add, - }} - /> - </div> - {/if} - <div - class={`absolute bottom-0 left-0 flex flex-col h-[calc(100%-100%/1.618)] w-full px-6 gap-2 justify-end items-center`} - > - <div - class={`flex flex-col w-full gap-[2px] justify-center items-center`} - > - <div class={`flex flex-row h-10 w-full justify-start items-center`}> - <button - class={`group flex flex-row justify-center items-center`} - on:click={basis.lc_handle_edit_profile_display_name} - > - <p - class={`font-sansd font-[600] text-[2rem] ${classes_photo_overlay_glyph} ${load_data?.nostr_profile.display_name ? `` : `capitalize opacity-active`} el-re`} - > - {load_data?.nostr_profile.display_name - ? load_data.nostr_profile.display_name - : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.profile_name`)}` })}`}`} - </p> - </button> - </div> - <div - class={`flex flex-row w-full gap-[6px] justify-start items-center`} - > - <button - class={`group flex flex-row justify-center items-center`} - on:click={async () => { - if (load_data?.nostr_profile.name) { - const confirm = - basis.lc_handle_edit_profile_name_confirm(); - if (!confirm) return; - } - await basis.lc_handle_edit_profile_name(); - }} - > - <p - class={`font-sansd font-[600] text-[1.1rem] ${classes_photo_overlay_glyph} ${load_data?.nostr_profile.name ? `` : `capitalize opacity-active`} el-re`} - > - {load_data?.nostr_profile.name - ? `@${load_data.nostr_profile.name}` - : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.username`)}` })}`}`} - </p> - </button> - <p - class={`font-sans font-[400] ${classes_photo_overlay_glyph}`} - > - {ascii.bullet} - </p> - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - alert(`@todo!`); - }} - > - <Glyph - basis={{ - classes: `${classes_photo_overlay_glyph}`, - dim: `xs`, - weight: `bold`, - key: `link-simple`, - }} - /> - </button> - </div> - <div class={`flex flex-row w-full justify-start items-center`}> - <button - class={`group flex flex-row justify-center items-center`} - on:click={basis.lc_handle_edit_profile_about} - > - <p - class={`font-sansd font-[400] text-[1.1rem] ${classes_photo_overlay_glyph} ${load_data?.nostr_profile.about ? `` : `capitalize opacity-active`}`} - > - {load_data?.nostr_profile.about - ? `@${load_data.nostr_profile.about}` - : `+ ${`${$lls(`icu.add_*`, { value: `${$lls(`common.bio`)}` })}`}`} - </p> - </button> - </div> - </div> - <div - class={`flex flex-row w-full pt-2 pb-6 gap-2 justify-start items-center`} - > - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - view_display = `photos`; - }} - > - <p - class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `photos` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`} - > - {`photos`} - </p> - </button> - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - view_display = `following`; - }} - > - <p - class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `following` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`} - > - {`following`} - </p> - </button> - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - view_display = `followers`; - }} - > - <p - class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `followers` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`} - > - {`followers`} - </p> - </button> - </div> - </div> -</div> -<div class={`flex flex-col w-full justify-start items-center`}> - {#if view_display === `photos`} - <p class={`font-sans font-[400] text-layer-0-glyph`}> - {view_display} - </p> - {:else if view_display === `following`} - <p class={`font-sans font-[400] text-layer-0-glyph`}> - {view_display} - </p> - {:else if view_display === `followers`} - <p class={`font-sans font-[400] text-layer-0-glyph`}> - {view_display} - </p> - {/if} -</div> -<FloatTabs /> diff --git a/apps-lib/src/lib/view/settings.svelte b/apps-lib/src/lib/view/settings.svelte @@ -1,32 +1,39 @@ <script lang="ts"> import { app_thc, - ascii, + handle_err, kv_init_page, LayoutTrellis, LayoutView, lls, PageToolbar, Trellis, + } from "$root"; + import { + ascii, type CallbackPromise, type CallbackPromiseGeneric, type ISelectOption, - type ViewBasis, - } from "$lib"; - import { onDestroy, onMount } from "svelte"; + type IViewBasis, + } from "@radroots/util"; + import { onMount } from "svelte"; - export let basis: ViewBasis<{ - lc_color_mode: CallbackPromiseGeneric<ISelectOption<string>>; - lc_settings_nostr: CallbackPromise; - }>; + let { + basis, + }: { + basis: IViewBasis<{ + lc_color_mode: CallbackPromiseGeneric<ISelectOption<string>>; + lc_settings_nostr: CallbackPromise; + lc_logout: CallbackPromise; + }>; + } = $props(); onMount(async () => { - if (!basis.kv_init_prevent) await kv_init_page(); - if (basis.lc_on_mount) await basis.lc_on_mount(); - }); - - onDestroy(async () => { - if (basis.lc_on_destroy) await basis.lc_on_destroy(); + try { + if (!basis.kv_init_prevent) await kv_init_page(); + } catch (e) { + handle_err(e, `on_mount`); + } }); </script> @@ -41,323 +48,87 @@ <LayoutTrellis> <Trellis basis={{ - args: { - layer: 1, - title: { - value: `Appearance`, - }, - list: [ - { - hide_active: true, - select: { - label: { - left: [ - { - value: `${$lls(`common.color_mode`)}`, - classes: `capitalize`, - }, - ], - }, - display: { - label: { - value: `${$app_thc}`, + layer: 1, + title: { + value: `Appearance`, + }, + list: [ + { + hide_active: true, + select: { + label: { + left: [ + { + value: `${$lls(`common.color_mode`)}`, classes: `capitalize`, }, - }, - el: { - value: $app_thc, - options: [ - { - entries: [ - { - value: ascii.bullet, - label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.color_mode`)}`.toLowerCase() })}`, - disabled: true, - }, - { - value: `light`, - label: `${$lls(`common.light`)}`, - }, - { - value: `dark`, - label: `${$lls(`common.dark`)}`, - }, - ], - }, - ], - callback: basis.lc_color_mode, - }, - end: { - glyph: { - key: `caret-right`, - }, - }, + ], }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Nostr Keys`, - }, - list: [ - { - touch: { + display: { label: { - left: [ - { - value: `Nostr Key (public)`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - callback: async () => { - alert(`@todo!`); - /*const public_key = await keystore.get( - ks.keys.nostr_publickey, - ); - if (`err` in public_key) return; - await dialog.alert( - `Hi! This is your nostr public key ${public_key.result}`, - );*/ + value: `${$app_thc}`, + classes: `capitalize`, }, }, - }, - { - touch: { - label: { - left: [ - { - value: `Nostr Key (secret)`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, + el: { + value: $app_thc, + options: [ + { + entries: [ + { + value: ascii.bullet, + label: `${$lls(`icu.choose_*`, { value: `${$lls(`common.color_mode`)}`.toLowerCase() })}`, + disabled: true, + }, + { + value: `light`, + label: `${$lls(`common.light`)}`, + }, + { + value: `dark`, + label: `${$lls(`common.dark`)}`, + }, + ], }, - }, - callback: async () => { - alert(`@todo!`); - /*const public_key = await keystore.get( - ks.keys.nostr_publickey, - ); - if (`err` in public_key) return; - const secret_key = await keystore.get( - ks.keys.nostr_secretkey( - public_key.result, - ), - ); - if (`err` in secret_key) return; - await dialog.alert( - `Hi! This is your nostr secret key ${secret_key.result}`, - );*/ - }, + ], + callback: basis.lc_color_mode, }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Configuration`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Nostr Settings`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, - }, + end: { + glyph: { + key: `caret-right`, }, - callback: basis.lc_settings_nostr, }, }, - { - touch: { - label: { - left: [ - { - value: `Reset Device`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - callback: async () => { - alert(`@todo!`); - /*const confirm = await dialog.confirm( - `Hi! This will delete your saved keys.`, - ); - if (confirm === true) await reset_device();*/ - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Location`, }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Geolocation Current`, - classes: `capitalize`, - }, - ], - }, - callback: async () => { - alert(`@todo!`); - /*const pos = await geol.current(); - await dialog.alert(JSON.stringify(pos));*/ - }, - }, - }, - ], - }, + ], }} /> <Trellis basis={{ - args: { - layer: 1, - title: { - value: `Web`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Radroots.Org (Open Homepage)`, - }, - ], - }, - callback: async () => { - //const url = `https://radroots.org`; - //await browser.open(url); - }, + layer: 1, + list: [ + { + hide_active: true, + touch: { + label: { + left: [ + { + value: `${$lls(`common.logout`)}`, + classes: `capitalize`, + }, + ], }, - }, - { - touch: { - label: { - left: [ - { - value: `Radroots.Org (Share Homepage)`, - }, - ], - }, - callback: async () => { - //await share.open({ - // title: `Radroots Home Page`, - // text: `Find farmers around the world.`, - // url: `https://radroots.org`, - // dialog_title: `This is the dialog title`, - //}); + end: { + glyph: { + key: `caret-right`, }, }, - }, - { - touch: { - label: { - left: [ - { - value: `Primal.Net (Open Profile)`, - }, - ], - }, - callback: async () => { - //const public_key = await keystore.get( - // ks.keys.nostr_publickey, - //); - //const npub = nostr.lib.npub(public_key); - //const url = `https://primal.net/p/${npub}`; - //await browser.open(url); - }, + callback: async () => { + await basis.lc_logout(); }, }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Device`, }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Device (Info)`, - }, - ], - }, - callback: async () => { - //const data = await device.info(); - //await dialog.alert(JSON.stringify(data)); - }, - }, - }, - { - touch: { - label: { - left: [ - { - value: `Device (Battery)`, - }, - ], - }, - callback: async () => { - //const data = await device.battery(); - //await dialog.alert(JSON.stringify(data)); - }, - }, - }, - ], - }, + ], }} /> </LayoutTrellis> diff --git a/apps-lib/svelte.config.js b/apps-lib/svelte.config.js @@ -1,8 +1,15 @@ -import preprocess from 'svelte-preprocess'; +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ const config = { - preprocess: preprocess(), + preprocess: vitePreprocess(), + kit: { + adapter: adapter(), + alias: { + $root: './src/lib/index.js', + } + }, }; export default config; diff --git a/apps-lib/tsconfig.json b/apps-lib/tsconfig.json @@ -1,17 +1,13 @@ { - "$schema": "https://json.schemastore.org/tsconfig", - "display": "svelte-lib", "extends": "./.svelte-kit/tsconfig.json", - "include": [ - "src/**/*.d.ts", - "src/**/*.js", - "src/**/*.ts", - "src/**/*.svelte", - "src/**/*.css" - ], - "exclude": [ - "dist", - "build", - "node_modules" - ] -} -\ No newline at end of file + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + } +} diff --git a/apps-lib/vite.config.ts b/apps-lib/vite.config.ts @@ -0,0 +1,11 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + resolve: { + alias: { + '$root': '/src/lib/index.js', + } + }, + plugins: [sveltekit()] +});