web


git clone https://radroots.dev/git/web.git
Log | Files | Refs | Submodules | README | LICENSE

commit 75a240c9d901413a272fd652efbe9dbcec2c0555
parent d948d2816aed7a34d86f1b49864204dcc958dcfb
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Sat, 26 Oct 2024 19:17:08 +0000

Edit `models/trade-product/add` update entry components, add rendering logic,  add styles. Add superellipse css/js. Add styles. Add client file system module.

Diffstat:
MCargo.lock | 1+
Mcrates/tauri/Cargo.toml | 1+
Mcrates/tauri/Info.ios.plist | 11+++++++++++
Mcrates/tauri/src/lib.rs | 1+
Mpackage.json | 1+
Msrc/app.html | 4+++-
Msrc/lib/client.ts | 3++-
Msrc/lib/conf.ts | 3+++
Msrc/routes/(app)/+page.svelte | 25++++++++++++++++++++-----
Msrc/routes/(app)/models/nostr-profile/+page.svelte | 76++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/routes/(app)/models/trade-product/+page.svelte | 9---------
Msrc/routes/(app)/models/trade-product/add/+page.svelte | 376+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/routes/+layout.svelte | 6+++---
Astatic/squircle.min.js | 2++
Mtailwind.config.ts | 7++++++-
15 files changed, 306 insertions(+), 220 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -3433,6 +3433,7 @@ dependencies = [ "tauri", "tauri-build", "tauri-plugin-dialog", + "tauri-plugin-fs", "tauri-plugin-geolocation", "tauri-plugin-http", "tauri-plugin-map-display", diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml @@ -23,6 +23,7 @@ serde_json = "1.0" sqlx = { version = "0.8.2", features = ["sqlite", "runtime-tokio"] } tauri = { version = "2.0.0", features = ["protocol-asset"] } tauri-plugin-dialog = "2.0.0" +tauri-plugin-fs = "2.0.0" tauri-plugin-geolocation = "2.0.0" tauri-plugin-http = "2.0.0" tauri-plugin-map-display = { git = "https://github.com/inkibra/tauri-plugins", tag = "@inkibra/tauri-plugin-map-display@0.2.0", package="tauri-plugin-map-display" } diff --git a/crates/tauri/Info.ios.plist b/crates/tauri/Info.ios.plist @@ -13,5 +13,16 @@ <key>NSAllowsArbitraryLoads</key> <true/> </dict> + <key>NSPrivacyAccessedAPITypes</key> + <array> + <dict> + <key>NSPrivacyAccessedAPIType</key> + <string>NSPrivacyAccessedAPICategoryFileTimestamp</string> + <key>NSPrivacyAccessedAPITypeReasons</key> + <array> + <string>C617.1</string> + </array> + </dict> + </array> </dict> </plist> \ No newline at end of file diff --git a/crates/tauri/src/lib.rs b/crates/tauri/src/lib.rs @@ -44,6 +44,7 @@ pub fn run() { }) }) .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_geolocation::init()) .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_map_display::init()) diff --git a/package.json b/package.json @@ -48,6 +48,7 @@ "@radroots/theme": "workspace:*", "@radroots/utils": "workspace:*", "chart.js": "^4.4.5", + "css-paint-polyfill": "^3.4.0", "sql.js": "^1.11.0" } } \ No newline at end of file diff --git a/src/app.html b/src/app.html @@ -8,9 +8,11 @@ <link rel="stylesheet" type="text/css" href="/phosphor-icons/fill.css" /> <link rel="stylesheet" type="text/css" href="/phosphor-icons/light.css" /> <link rel="stylesheet" type="text/css" href="/phosphor-icons/regular.css" /> - <link rel="stylesheet" type="text/css" href="/stylesheets/maplibre-gl.css" /> + <link rel="stylesheet" type="text/css" href="/stylesheets/styles-maplibre-gl.css" /> + <link rel="stylesheet" type="text/css" href="/stylesheets/styles-superellipse.css" /> <script src="/keyva.min.js"></script> + <script src="/squircle.min.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no viewport-fit=cover" /> diff --git a/src/lib/client.ts b/src/lib/client.ts @@ -1,10 +1,11 @@ -import { ClientNostr, TauriClientDb, TauriClientDevice, TauriClientDialog, TauriClientGeolocation, TauriClientHaptics, TauriClientHttp, TauriClientKeying, TauriClientKeystore, TauriClientLogger, TauriClientMap, TauriClientNotification, TauriClientOs, TauriClientWindow } from "@radroots/client"; +import { ClientNostr, TauriClientDb, TauriClientDevice, TauriClientDialog, TauriClientFs, TauriClientGeolocation, TauriClientHaptics, TauriClientHttp, TauriClientKeying, TauriClientKeystore, TauriClientLogger, TauriClientMap, TauriClientNotification, TauriClientOs, TauriClientWindow } from "@radroots/client"; import { Geocoder } from "@radroots/geocoder"; export const geoc = new Geocoder(`/geonames/geonames.db`); export const db = new TauriClientDb(); export const device = new TauriClientDevice(); export const dialog = new TauriClientDialog(); +export const fs = new TauriClientFs(); export const geol = new TauriClientGeolocation(); export const haptics = new TauriClientHaptics(); export const os = new TauriClientOs(); diff --git a/src/lib/conf.ts b/src/lib/conf.ts @@ -16,6 +16,9 @@ export const ks = { nostr_publickey: `keys:nostr:`, } }; +export const ascii = { + bullet: '•' +} export const cfg = { app: { diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte @@ -21,7 +21,7 @@ import { onMount } from "svelte"; type PageParamButtons = { - route: NavigationRoute; + route?: NavigationRoute; label: string; key: GlyphKey; weight?: GlyphWeight; @@ -50,9 +50,24 @@ key: `note-blank`, }, { - route: `/models/trade-product`, label: `Farm Products`, key: `basket`, + callback: async () => { + const trade_products = await db.trade_product_get({ + list: [`all`], + }); + if (`results` in trade_products) { + if (trade_products.results.length === 0) { + $nav_prev.push({ + route: `/`, + label: `Back`, + }); + await route(`/models/trade-product/add`); + return; + } + } + await route(`/models/trade-product`); + }, }, { route: `/`, @@ -102,7 +117,7 @@ <div class={`flex flex-col pt-2 justify-center items-center`}> {#if tmp_show_no_profile} <button - class={`relative flex flex-row h-24 w-${$app_layout} p-4 gap-4 justify-center items-center bg-layer-2-surface/60 rounded-touch touch-layer-1 touch-layer-1-raise-less el-re`} + class={`relative flex flex-row h-24 w-${$app_layout} p-4 gap-4 justify-center items-center bg-layer-2-surface/60 round-20 touch-layer-1 touch-layer-1-raise-less el-re`} on:click={async () => { await route(`/models/nostr-profile`); }} @@ -153,10 +168,10 @@ <div class={`flex flex-col w-full gap-5 justify-start items-center`}> {#each page_param.buttons[$app_cfg_type] as btn} <button - class={`flex flex-row h-20 w-${$app_layout} py-2 px-6 justify-between items-center rounded-touch bg-layer-1-surface touch-layer-1 touch-layer-1-raise-less el-re`} + class={`flex flex-row h-20 w-${$app_layout} py-2 px-6 justify-between items-center bg-layer-1-surface touch-layer-1 touch-layer-1-raise-less round-20 el-re`} on:click={async () => { if (btn.callback) await btn.callback(); - await route(btn.route); + else if (btn.route) await route(btn.route); }} > <div diff --git a/src/routes/(app)/models/nostr-profile/+page.svelte b/src/routes/(app)/models/nostr-profile/+page.svelte @@ -4,15 +4,15 @@ import { app_nostr_key, app_notify, + Glyph, type ISelectOption, LayoutTrellis, LayoutView, Nav, nav_prev, route, - SelectElement, + SelectEl, t, - Glyph, TrellisTitle, } from "@radroots/svelte-lib"; import { onMount } from "svelte"; @@ -233,45 +233,41 @@ <div class={`z-10 absolute top-2 right-3 flex flex-row h-full justify-end pr-1`} > - <SelectElement + <SelectEl + value={`~`} basis={{ - args: { - layer: 0, - mask: true, - callback: async ({ value }) => { - await handle_key_options_press( - { - option: value, - public_key: - li.public_key, - }, - ); - }, - options: [ - { - entries: - page_param.options_list.filter( - (i) => - !( - !li.name && - i.value === - `edit-profile-name` - ) && - !( - li.name && - i.value === - `add-profile-name` - ) && - !( - li.public_key === - $app_nostr_key && - i.value === - `set-key-active` - ), - ), - }, - ], + layer: 0, + //mask: true, + callback: async ({ value }) => { + await handle_key_options_press({ + option: value, + public_key: li.public_key, + }); }, + options: [ + { + entries: + page_param.options_list.filter( + (i) => + !( + !li.name && + i.value === + `edit-profile-name` + ) && + !( + li.name && + i.value === + `add-profile-name` + ) && + !( + li.public_key === + $app_nostr_key && + i.value === + `set-key-active` + ), + ), + }, + ], }} > <svelte:fragment slot="element"> @@ -284,7 +280,7 @@ }} /> </svelte:fragment> - </SelectElement> + </SelectEl> </div> </div> {/each} diff --git a/src/routes/(app)/models/trade-product/+page.svelte b/src/routes/(app)/models/trade-product/+page.svelte @@ -6,7 +6,6 @@ LayoutTrellis, LayoutView, Nav, - nav_prev, route, t, time_iso, @@ -39,14 +38,6 @@ ); return; } - if (trade_products.results.length === 0) { - $nav_prev.push({ - route: `/`, - label: `Back`, - }); - await route(`/models/trade-product/add`); - return; - } const data: LoadData = { trade_products: trade_products.results, diff --git a/src/routes/(app)/models/trade-product/add/+page.svelte b/src/routes/(app)/models/trade-product/add/+page.svelte @@ -24,7 +24,8 @@ el_id, EntryLine, EntryMultiline, - EntryOption, + EntrySelect, + EntryWrap, Fill, fmt_geol_latitude, fmt_geol_longitude, @@ -38,6 +39,7 @@ LayoutView, locale, Nav, + SelectEl, sleep, t, view_effect, @@ -117,12 +119,7 @@ } }); - $: { - console.log(`$carousel_index `, $carousel_index); - console.log(`$carousel_index_max `, $carousel_index_max); - } - - let el_trellis_wrap_price: HTMLElement | null; + // let el_trellis_wrap_price: HTMLElement | null; let loading_submit = false; @@ -433,6 +430,19 @@ map_choose_loc_geoc_point_select = map_choose_loc_geoc_point; }; + const handle_entry_focus = async (id_key: string): Promise<void> => { + try { + const el = el_id(fmt_id(id_key)); + el?.classList.add(`entry-layer-1-highlight`); + el?.focus(); + await handle_back(2); + await sleep(1000); + el?.classList.remove(`entry-layer-1-highlight`); + } catch (e) { + console.log(`(error) handle_entry_focus `, e); + } + }; + const submit = async (): Promise<void> => { try { loading_submit = true; @@ -477,63 +487,82 @@ value: `${$t(`icu.show_*`, { value: `${$t(`common.options`)}` })}`, }, callback: async () => { + const kv_other = await kv.get( + fmt_id(`key`), + ); + if (kv_other) { + const confirm = + await dialog.confirm({ + message: `${$t(`icu.the_current_entry_*_will_be_deleted`, { value: kv_other })}. ${$t(`common.do_you_want_to_continue_q`)}`, + }); + if (confirm === true) + await kv.set( + fmt_id(`key`), + trade_key_default, + ); + } await toggle_show_key_other(false); }, } : undefined, }} > - {#if show_sel_trade_product_key_other} - <div - class={`relative flex flex-row w-full justify-center items-center rounded-2xl`} - > - <EntryLine - basis={{ - el: { - id: fmt_id(`title`), - sync: true, - sync_init: true, - placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.*_title`, { value: `${$t(`common.listing`)}` })}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields - .title.charset, - validate: - trade_product_form_fields - .title.validation, - validate_keypress: true, + {#if !show_sel_trade_product_key_other} + <EntrySelect + bind:value={sel_trade_product_key} + basis={{ + wrap: { + id: fmt_id(`key_wrap`), + }, + el: { + id: fmt_id(`key`), + sync: true, + options: [ + ...trade_keys.map((i) => ({ + value: i, + label: `${$t(`trade.product.key.${i}`, { default: i })}`, + })), + { + value: `other`, + label: `${$t(`common.other`)}`, }, + ], + callback: async (val) => { + const el = el_id( + fmt_id(`key_wrap`), + ); + el?.classList.remove( + `entry-layer-1-highlight`, + ); + if (val === `other`) { + await toggle_show_key_other( + true, + ); + } }, - }} - /> - </div> + }, + }} + /> {:else} - <EntryOption - bind:value={sel_trade_product_key} + <EntryLine basis={{ - id_wrap: fmt_id(`key_wrap`), - id: fmt_id(`key`), - classes: `font-mono-display`, - sync: true, - options: [ - ...trade_keys.map((i) => ({ - value: i, - label: `${$t(`trade.product.key.${i}`, { default: i })}`, - })), - { - value: `other`, - label: `${$t(`common.other`)}`, + wrap: { + id: fmt_id(`key_wrap`), + }, + el: { + id: fmt_id(`key`), + sync: true, + sync_init: true, + placeholder: `${$t(`icu.enter_the_*`, { value: `${$t(`icu.*_name`, { value: `${$t(`common.product`)}` })}`.toLowerCase() })}`, + field: { + charset: + trade_product_form_fields.title + .charset, + validate: + trade_product_form_fields.title + .validation, + validate_keypress: true, }, - ], - callback: async (val) => { - el_id( - fmt_id(`key_wrap`), - )?.classList.remove( - `entry-layer-1-highlight`, - ); - if (val === `other`) { - await toggle_show_key_other(true); - } }, }} /> @@ -546,11 +575,10 @@ }, }} > - <div - bind:this={el_trellis_wrap_price} - id={fmt_id(`price_wrap`)} - tabindex={-1} - class={`relative el-re flex flex-row w-full pl-2 justify-between items-center h-form_line bg-layer-1-surface rounded-2xl`} + <EntryWrap + basis={{ + id: fmt_id(`price_wrap`), + }} > <InputElement basis={{ @@ -579,11 +607,17 @@ period_count < 2) || value.length < 1 ) { - el_trellis_wrap_price?.classList.remove( + const el = el_id( + fmt_id(`price_wrap`), + ); + el?.classList.remove( `entry-layer-1-highlight`, ); } else { - el_trellis_wrap_price?.classList.add( + const el = el_id( + fmt_id(`price_wrap`), + ); + el?.classList.add( `entry-layer-1-highlight`, ); } @@ -591,43 +625,55 @@ }} /> <div - class={`flex flex-row gap-2 pl-3 pr-4 justify-end items-center bg-layer-1-surface rounded-r-2xl text-layer-1-glyph/70`} + class={`flex flex-row gap-2 justify-end items-center text-layer-1-glyph/70`} > - <EntryOption + <SelectEl bind:value={sel_trade_product_price_currency} basis={{ id: fmt_id(`price_currency`), - classes: `w-fit font-mono-display font-[500] text-lg`, + classes: `w-fit font-circ font-[500] text-[1rem]`, layer: false, - hide_arrows: true, - sync: true, - options: fiat_currencies.map((i) => ({ - value: i, - label: `${$t(`currency.${i}.symbol`, { default: i })}`, - })), + //hide_arrows: true, + //sync: true, + options: [ + { + entries: fiat_currencies.map( + (i) => ({ + value: i, + label: `${$t(`currency.${i}.symbol`, { default: i })}`, + }), + ), + }, + ], }} /> <p - class={`font-sans font-[500] text-[1.1rem] -translate-y-[1px] scale-y-[110%]`} + class={`font-sans font-[500] text-[1.1rem] -translate-y-[1px] scale-y-[120%]`} > {`/`} </p> - <EntryOption + <SelectEl bind:value={sel_trade_product_price_qty_unit} basis={{ id: fmt_id(`price_qty_unit`), - classes: `w-fit font-mono-display font-[600] text-[0.95rem] leading-[1rem]`, + classes: `w-fit font-circ font-[500] text-[1rem]`, layer: false, - hide_arrows: true, - sync: true, - options: mass_units.map((i) => ({ - value: i, - label: `${$t(`measurement.mass.unit.${i}_ab`, { default: i })}`, - })), + // hide_arrows: true, + // sync: true, + options: [ + { + entries: mass_units.map( + (i) => ({ + value: i, + label: `${$t(`measurement.mass.unit.${i}_ab`, { default: i })}`, + }), + ), + }, + ], }} /> </div> - </div> + </EntryWrap> </LayoutTrellisLine> <LayoutTrellisLine basis={{ @@ -654,87 +700,94 @@ tabindex={-1} class={`relative el-re flex flex-row w-full gap-3 justify-between items-center h-form_line bg-layer-1-surface text-layer-1-glyph rounded-2xl`} > - {#if show_sel_trade_product_qty_tup_other} + {#if !show_sel_trade_product_qty_tup_other} + <EntrySelect + bind:value={sel_trade_product_qty_tup} + basis={{ + wrap: { + id: `tmp`, + }, + el: { + options: [ + ...ls_trade_product_quantities.map( + (i) => ({ + value: fmt_trade_quantity_val( + i, + ), + label: `${i.mass} ${$t(`measurement.mass.unit.${i.mass_unit}_ab`, { default: i.mass_unit })} ${i.label}`, + }), + ), + { + value: `other`, + label: `${$t(`common.other`)}`, + }, + ], + callback: async (val) => { + el_id( + fmt_id(`qty_wrap`), + )?.classList.remove( + `entry-layer-1-highlight`, + ); + if (val === `other`) { + await toggle_show_qty_amt_other( + true, + ); + } else { + await kv.set( + fmt_id(`qty_avail`), + `1`, + ); + } + }, + }, + }} + /> + {:else} <div class={`relative flex flex-row w-full justify-center items-center`} > - <EntryLine + <InputElement basis={{ - el: { - id: fmt_id(`qty_amt`), - layer: false, - sync: true, - placeholder: `${$t(`icu.enter_number_of_*_per_order`, { value: `${$t(`measurement.mass.unit.${sel_trade_product_price_qty_unit}_ab`, { default: sel_trade_product_price_qty_unit })}` })}`, - field: { - charset: - trade_product_form_fields - .qty_amt.charset, - validate: - trade_product_form_fields - .qty_amt.validation, - validate_keypress: true, - }, + id: fmt_id(`qty_amt`), + layer: false, + sync: true, + placeholder: `${$t(`icu.enter_number_of_*_per_order`, { value: `${$t(`measurement.mass.unit.${sel_trade_product_price_qty_unit}_ab`, { default: sel_trade_product_price_qty_unit })}` })}`, + field: { + charset: + trade_product_form_fields + .qty_amt.charset, + validate: + trade_product_form_fields + .qty_amt.validation, + validate_keypress: true, }, }} /> <div class={`absolute top-0 right-0 flex flex-row h-full gap-2 justify-center items-center`} > - <EntryOption + <EntrySelect bind:value={sel_trade_product_qty_unit} basis={{ - id: fmt_id(`qty_unit`), - classes: `w-[3.5rem] text-layer-1-glyph font-[500]`, - layer: false, - sync: true, - options: mass_units.map( - (i) => ({ - value: i, - label: `${$t(`measurement.mass.unit.${i}_ab`, { default: i })}`, - }), - ), + wrap: { + id: `tmp2`, + }, + el: { + id: fmt_id(`qty_unit`), + classes: `w-[3.5rem] text-layer-1-glyph font-[500]`, + //layer: false, + //sync: true, + options: mass_units.map( + (i) => ({ + value: i, + label: `${$t(`measurement.mass.unit.${i}_ab`, { default: i })}`, + }), + ), + }, }} /> </div> </div> - {:else} - <EntryOption - bind:value={sel_trade_product_qty_tup} - basis={{ - classes: `font-mono-display`, - options: [ - ...ls_trade_product_quantities.map( - (i) => ({ - value: fmt_trade_quantity_val( - i, - ), - label: `${i.mass} ${$t(`measurement.mass.unit.${i.mass_unit}_ab`, { default: i.mass_unit })} ${i.label}`, - }), - ), - { - value: `other`, - label: `${$t(`common.other`)}`, - }, - ], - callback: async (val) => { - el_id( - fmt_id(`qty_wrap`), - )?.classList.remove( - `entry-layer-1-highlight`, - ); - if (val === `other`) { - await toggle_show_qty_amt_other( - true, - ); - } else { - await kv.set( - fmt_id(`qty_avail`), - `1`, - ); - } - }, - }} - /> {/if} </div> </LayoutTrellisLine> @@ -926,17 +979,8 @@ <button class={`flex flex-row justify-start items-center active:opacity-60 transition-all`} on:click={async () => { - const el = el_id( - fmt_id(`key_wrap`), - ); - el?.classList.add( - `entry-layer-1-highlight`, - ); - el?.focus(); - await handle_back(2); - await sleep(1000); - el?.classList.remove( - `entry-layer-1-highlight`, + await handle_entry_focus( + `key_wrap`, ); }} > @@ -1042,20 +1086,27 @@ <button class={`flex flex-row justify-start items-center active:opacity-60 transition-all`} on:click={async () => { - const el = el_id( + await handle_entry_focus( + `price_wrap`, + ); + /*const el = el_id( fmt_id( `price_wrap`, ), ); + console.log(`el `, el); el?.classList.add( `entry-layer-1-highlight`, ); el?.focus(); await handle_back(2); - await sleep(1000); + await sleep( + cfg.delay + .entry_focus, + ); el?.classList.remove( `entry-layer-1-highlight`, - ); + );*/ }} > <p @@ -1116,7 +1167,10 @@ ); el?.focus(); await handle_back(2); - await sleep(1000); + await sleep( + cfg.delay + .entry_focus, + ); el?.classList.remove( `entry-layer-1-highlight`, ); @@ -1318,7 +1372,9 @@ > <EntryLine basis={{ - id_wrap: fmt_id(`title_wrap`), + wrap: { + id: fmt_id(`title_wrap`), + }, el: { id: fmt_id(`title`), sync: true, diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte @@ -23,15 +23,16 @@ type NavigationRoute, } from "@radroots/svelte-lib"; import { parse_color_mode, parse_theme_key } from "@radroots/theme"; + import "css-paint-polyfill"; import { onDestroy, onMount } from "svelte"; import "../app.css"; - //let unlisten_logger: IClientUnlisten; let route_render: NavigationRoute | undefined = undefined; onMount(async () => { try { - //unlisten_logger = await logger.init(); + if (`paintWorklet` in CSS) + (CSS as any).paintWorklet.addModule(`/squircle.min.js`); const metadata: IClientDeviceMetadata = { version: os.version(), platform: os.platform(), @@ -46,7 +47,6 @@ onDestroy(async () => { try { - //unlisten_logger(); route_render = undefined; } catch (e) { } finally { diff --git a/static/squircle.min.js b/static/squircle.min.js @@ -0,0 +1 @@ +const drawSquircle = (ctx, geom, radii, smooth, lineWidth, color) => { const defaultFill = color; const lineWidthOffset = lineWidth / 2; ctx.beginPath(); ctx.lineTo(radii[0], lineWidthOffset); ctx.lineTo(geom.width - radii[1], lineWidthOffset); ctx.bezierCurveTo(geom.width - radii[1] / smooth, lineWidthOffset, geom.width - lineWidthOffset, radii[1] / smooth, geom.width - lineWidthOffset, radii[1]); ctx.lineTo(geom.width - lineWidthOffset, geom.height - radii[2]); ctx.bezierCurveTo(geom.width - lineWidthOffset, geom.height - radii[2] / smooth, geom.width - radii[2] / smooth, geom.height - lineWidthOffset, geom.width - radii[2], geom.height - lineWidthOffset); ctx.lineTo(radii[3], geom.height - lineWidthOffset); ctx.bezierCurveTo(radii[3] / smooth, geom.height - lineWidthOffset, lineWidthOffset, geom.height - radii[3] / smooth, lineWidthOffset, geom.height - radii[3]); ctx.lineTo(lineWidthOffset, radii[0]); ctx.bezierCurveTo(lineWidthOffset, radii[0] / smooth, radii[0] / smooth, lineWidthOffset, radii[0], lineWidthOffset); ctx.closePath(); if (lineWidth) { ctx.strokeStyle = defaultFill; ctx.lineWidth = lineWidth; ctx.stroke() } else { ctx.fillStyle = defaultFill; ctx.fill() } }; class SquircleClass { static get contextOptions() { return { alpha: true } } static get inputProperties() { return ["--squircle-radius", "--squircle-radius-top-left", "--squircle-radius-top-right", "--squircle-radius-bottom-right", "--squircle-radius-bottom-left", "--squircle-smooth", "--squircle-outline", "--squircle-fill"] } paint(ctx, geom, properties) { const smoothRatio = 10; const distanceRatio = 1.8; const squircleSmooth = parseFloat(properties.get("--squircle-smooth") * smoothRatio); const individualRadiiProps = SquircleClass.inputProperties.slice(1, 5); let squircleRadii = individualRadiiProps.map(prop => { const value = properties.get(prop); return value ? parseInt(value, 10) * distanceRatio : NaN }); let shorthand_R; if (squircleRadii.some(isNaN)) { const radiusRegex = /([0-9]+[a-z%]*)/g; const radius_shorthand = properties.get("--squircle-radius").toString(); const matches = radius_shorthand.match(radiusRegex); if (matches) { shorthand_R = matches.map(val => parseInt(val, 10) * distanceRatio); while (shorthand_R.length < 4) { if (shorthand_R.length === 1) { shorthand_R.push(shorthand_R[0]) } else if (shorthand_R.length === 2) { shorthand_R = [shorthand_R[0], shorthand_R[1], shorthand_R[0], shorthand_R[1]] } else if (shorthand_R.length === 3) { shorthand_R = [shorthand_R[0], shorthand_R[1], shorthand_R[2], shorthand_R[1]] } } } else { const defaultRadius = squircleRadii.every(isNaN) ? 8 * distanceRatio : 0; shorthand_R = [defaultRadius, defaultRadius, defaultRadius, defaultRadius] } } squircleRadii = squircleRadii.map((val, i) => isNaN(val) ? shorthand_R[i] : val); const squrcleOutline = parseFloat(properties.get("--squircle-outline"), 10); const squrcleColor = properties.get("--squircle-fill").toString(); const isSmooth = () => { if (typeof properties.get("--squircle-smooth")[0] !== "undefined") { if (squircleSmooth === 0) { return 1 } return squircleSmooth } else { return 10 } }; const isOutline = () => { if (squrcleOutline) { return squrcleOutline } else { return 0 } }; const isColor = () => { if (squrcleColor) { return squrcleColor } else { return "#f45" } }; const maxRadius = Math.max(...squircleRadii); if (maxRadius < geom.width / 2 && maxRadius < geom.height / 2) { drawSquircle(ctx, geom, squircleRadii, isSmooth(), isOutline(), isColor()) } else { const minRadius = Math.min(geom.width / 2, geom.height / 2); drawSquircle(ctx, geom, squircleRadii.map(() => minRadius), isSmooth(), isOutline(), isColor()) } } } if (typeof registerPaint !== "undefined") { registerPaint("squircle", SquircleClass) } +\ No newline at end of file diff --git a/tailwind.config.ts b/tailwind.config.ts @@ -6,7 +6,7 @@ import tailwind_default from "tailwindcss/defaultTheme"; const { fontFamily: tw_font } = tailwind_default; const heights_form = { - line: "44px", + line: "46px", }; const heights_responsive = { @@ -101,6 +101,10 @@ const config: Config = { envelopeButtonCancel: ["1.1rem", { lineHeight: "1.75rem", fontWeight: 600 }], envelopeButtonLabel: ["1.1rem", { lineHeight: "1.75rem", fontWeight: 500 }], }, + gridTemplateColumns: { + '16': 'repeat(16, minmax(0, 1fr))', + '24': 'repeat(24, minmax(0, 1fr))', + }, height: { ...heights, ...dimensions, @@ -139,6 +143,7 @@ const config: Config = { }, borderRadius: { input_form: "8px", + entry: "1.05rem", touch: "1.25rem" }, }