From 35418e5f484afd8580496ebba8ad566070f2c46b Mon Sep 17 00:00:00 2001 From: Bastiaan Date: Sat, 6 Jun 2026 22:40:47 +0200 Subject: [PATCH] chore: lots of small improvements to swiper --- deno.lock | 195 +++--------------- package.json | 4 +- .../client/migrations/2026-05-04.initial.ts | 3 +- pkgs/@scope.db/client/mod.ts | 3 + src/components/app.add-item.tsx | 13 ++ src/components/app.item.tsx | 57 +++++ src/components/app.missed-items.tsx | 17 +- src/components/common.button.tsx | 2 +- src/components/common.swiper.tsx | 110 ++++++++-- src/functions/item.queries.ts | 36 +++- src/routes/index.tsx | 35 ++-- 11 files changed, 254 insertions(+), 221 deletions(-) create mode 100644 src/components/app.add-item.tsx create mode 100644 src/components/app.item.tsx diff --git a/deno.lock b/deno.lock index f233ca1..6e89d03 100644 --- a/deno.lock +++ b/deno.lock @@ -5,13 +5,13 @@ "npm:@deno/vite-plugin@^1.0.6": "1.0.6_vite@7.3.5__lightningcss@1.32.0", "npm:@iconify-json/solar@^1.2.5": "1.2.5", "npm:@plausible-analytics/tracker@~0.4.5": "0.4.5", - "npm:@sentry/solid@^10.56.0": "10.56.0_@tanstack+solid-router@1.170.11__solid-js@1.9.13_solid-js@1.9.13", + "npm:@sentry/solid@^10.56.0": "10.56.0_@tanstack+solid-router@1.170.15__solid-js@1.9.13_solid-js@1.9.13", "npm:@sentry/vite-plugin@^5.3.0": "5.3.0", "npm:@solid-primitives/i18n@^2.2.1": "2.2.1_solid-js@1.9.13", "npm:@solid-primitives/refs@^1.1.3": "1.1.3_solid-js@1.9.13", "npm:@solid-primitives/storage@^4.3.4": "4.3.4_solid-js@1.9.13", - "npm:@tanstack/solid-router@^1.170.11": "1.170.11_solid-js@1.9.13", - "npm:@tanstack/solid-start@^1.168.19": "1.168.19_solid-js@1.9.13_vite@7.3.5__lightningcss@1.32.0_vite-plugin-solid@2.11.12__solid-js@1.9.13__vite@7.3.5___lightningcss@1.32.0__lightningcss@1.32.0", + "npm:@tanstack/solid-router@^1.170.15": "1.170.15_solid-js@1.9.13", + "npm:@tanstack/solid-start@^1.168.24": "1.168.24_solid-js@1.9.13_vite@7.3.5__lightningcss@1.32.0_vite-plugin-solid@2.11.12__solid-js@1.9.13__vite@7.3.5___lightningcss@1.32.0__lightningcss@1.32.0", "npm:@vite-pwa/assets-generator@^1.0.2": "1.0.2", "npm:class-variance-authority@~0.7.1": "0.7.1", "npm:kysely@~0.29.2": "0.29.2", @@ -320,13 +320,6 @@ "@babel/helper-plugin-utils" ] }, - "@babel/plugin-syntax-typescript@7.29.7_@babel+core@7.29.7": { - "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", - "dependencies": [ - "@babel/core", - "@babel/helper-plugin-utils" - ] - }, "@babel/plugin-syntax-unicode-sets-regex@7.18.6_@babel+core@7.29.7": { "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dependencies": [ @@ -1189,9 +1182,6 @@ "@tybys/wasm-util" ] }, - "@nothing-but/utils@0.17.0": { - "integrity": "sha512-TuCHcHLOqDL0SnaAxACfuRHBNRgNJcNn9X0GiH5H3YSDBVquCr3qEIG3FOQAuMyZCbu9w8nk2CHhOsn7IvhIwQ==" - }, "@oozcitak/dom@2.0.2": { "integrity": "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==", "dependencies": [ @@ -1728,7 +1718,7 @@ "magic-string" ] }, - "@sentry/solid@10.56.0_@tanstack+solid-router@1.170.11__solid-js@1.9.13_solid-js@1.9.13": { + "@sentry/solid@10.56.0_@tanstack+solid-router@1.170.15__solid-js@1.9.13_solid-js@1.9.13": { "integrity": "sha512-bbZ3FlHslaq2J7C0z1sLQxzwDznPYnbZ6H93oQ+XL3UgX99aKIkbfd7zCyhOvscaK79jOXw5C+gS0uTWIAJL+Q==", "dependencies": [ "@sentry/browser", @@ -1747,88 +1737,12 @@ "@sentry/rollup-plugin" ] }, - "@solid-devtools/debugger@0.28.1_solid-js@1.9.13": { - "integrity": "sha512-6qIUI6VYkXoRnL8oF5bvh2KgH71qlJ18hNw/mwSyY6v48eb80ZR48/5PDXufUa3q+MBSuYa1uqTMwLewpay9eg==", - "dependencies": [ - "@nothing-but/utils", - "@solid-devtools/shared", - "@solid-primitives/bounds", - "@solid-primitives/event-listener", - "@solid-primitives/keyboard", - "@solid-primitives/rootless", - "@solid-primitives/scheduled", - "@solid-primitives/static-store", - "@solid-primitives/utils", - "solid-js" - ] - }, - "@solid-devtools/logger@0.9.11_solid-js@1.9.13": { - "integrity": "sha512-THbiY1iQlieL6vdgJc4FIsLe7V8a57hod/Thm8zdKrTkWL88UPZjkBBfM+mVNGusd4OCnAN20tIFBhNnuT1Dew==", - "dependencies": [ - "@nothing-but/utils", - "@solid-devtools/debugger", - "@solid-devtools/shared", - "@solid-primitives/utils", - "solid-js" - ] - }, - "@solid-devtools/shared@0.20.0_solid-js@1.9.13": { - "integrity": "sha512-o5TACmUOQsxpzpOKCjbQqGk8wL8PMi+frXG9WNu4Lh3PQVUB6hs95Kl/S8xc++zwcMguUKZJn8h5URUiMOca6Q==", - "dependencies": [ - "@nothing-but/utils", - "@solid-primitives/event-listener", - "@solid-primitives/media", - "@solid-primitives/refs", - "@solid-primitives/rootless", - "@solid-primitives/scheduled", - "@solid-primitives/static-store", - "@solid-primitives/styles", - "@solid-primitives/utils", - "solid-js" - ] - }, - "@solid-primitives/bounds@0.1.5_solid-js@1.9.13": { - "integrity": "sha512-JFym8zijMfWp1FaAmJlH3xMfenCuhjaUsoBn3kt9FtoWwLj+yt+EGYt+p3SkOKwF7h4gaGtZ5PIdSbSNVWkRmg==", - "dependencies": [ - "@solid-primitives/event-listener", - "@solid-primitives/resize-observer", - "@solid-primitives/static-store", - "@solid-primitives/utils", - "solid-js" - ] - }, - "@solid-primitives/event-listener@2.4.5_solid-js@1.9.13": { - "integrity": "sha512-nwRV558mIabl4yVAhZKY8cb6G+O1F0M6Z75ttTu5hk+SxdOnKSGj+eetDIu7Oax1P138ZdUU01qnBPR8rnxaEA==", - "dependencies": [ - "@solid-primitives/utils", - "solid-js" - ] - }, "@solid-primitives/i18n@2.2.1_solid-js@1.9.13": { "integrity": "sha512-TnTnE2Ku11MGYZ1JzhJ8pYscwg1fr9MteoYxPwsfxWfh9Jp5K7RRJncJn9BhOHvNLwROjqOHZ46PT7sPHqbcXw==", "dependencies": [ "solid-js" ] }, - "@solid-primitives/keyboard@1.3.5_solid-js@1.9.13": { - "integrity": "sha512-sav+l+PL+74z3yaftVs7qd8c2SXkqzuxPOVibUe5wYMt+U5Hxp3V3XCPgBPN2I6cANjvoFtz0NiU8uHVLdi9FQ==", - "dependencies": [ - "@solid-primitives/event-listener", - "@solid-primitives/rootless", - "@solid-primitives/utils", - "solid-js" - ] - }, - "@solid-primitives/media@2.3.5_solid-js@1.9.13": { - "integrity": "sha512-LX9fB5WDaK87FMDtUB1qokBOfT2et9Uobv/zZaKLH9caFSz4+P70MBKEIBHcZQy+9MV5M2XvGYLTbLskjkzMjA==", - "dependencies": [ - "@solid-primitives/event-listener", - "@solid-primitives/rootless", - "@solid-primitives/static-store", - "@solid-primitives/utils", - "solid-js" - ] - }, "@solid-primitives/refs@1.1.3_solid-js@1.9.13": { "integrity": "sha512-aam02fjNKpBteewF/UliPSQCVJsIIGOLEWQOh+ll6R/QePzBOOBMcC4G+5jTaO75JuUS1d/14Q1YXT3X0Ow6iA==", "dependencies": [ @@ -1836,36 +1750,6 @@ "solid-js" ] }, - "@solid-primitives/resize-observer@2.1.5_solid-js@1.9.13": { - "integrity": "sha512-AiyTknKcNBaKHbcSMuxtSNM8FjIuiSuFyFghdD0TcCMU9hKi9EmsC5pjfjDwxE+5EueB1a+T/34PLRI5vbBbKw==", - "dependencies": [ - "@solid-primitives/event-listener", - "@solid-primitives/rootless", - "@solid-primitives/static-store", - "@solid-primitives/utils", - "solid-js" - ] - }, - "@solid-primitives/rootless@1.5.3_solid-js@1.9.13": { - "integrity": "sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA==", - "dependencies": [ - "@solid-primitives/utils", - "solid-js" - ] - }, - "@solid-primitives/scheduled@1.5.3_solid-js@1.9.13": { - "integrity": "sha512-oNwLE6E6lxJAWrc8QXuwM0k2oU1BnANnkChwMw82aK1j3+mWGJkG1IFe5gCwbV+afYmjI76t9JJV3md/8tLw+g==", - "dependencies": [ - "solid-js" - ] - }, - "@solid-primitives/static-store@0.1.3_solid-js@1.9.13": { - "integrity": "sha512-uxez7SXnr5GiRnzqO2IEDjOJRIXaG+0LZLBizmUA1FwSi+hrpuMzVBwyk70m4prcl8X6FDDXUl9O8hSq8wHbBQ==", - "dependencies": [ - "@solid-primitives/utils", - "solid-js" - ] - }, "@solid-primitives/storage@4.3.4_solid-js@1.9.13": { "integrity": "sha512-GSxPAIuyxhJWOcv7n10iv3aid5oHN3KUgyA9IV0GYWlPpgyGs43aS9E85b0VXDLoH+D4ThNK8+2WEJ8B/S6Ccg==", "dependencies": [ @@ -1873,14 +1757,6 @@ "solid-js" ] }, - "@solid-primitives/styles@0.1.3_solid-js@1.9.13": { - "integrity": "sha512-7YdA21prMeCX+oOF/1RAn02+cGz/pG4dyPWtHBC2H8aZvnC7IfThBt80mP+TioejrdfE7Lc54Uh18f7Pig+gRQ==", - "dependencies": [ - "@solid-primitives/rootless", - "@solid-primitives/utils", - "solid-js" - ] - }, "@solid-primitives/utils@6.4.0_solid-js@1.9.13": { "integrity": "sha512-AeGTBg8Wtkh/0s+evyLtP8piQoS4wyqqQaAFs2HJcFMMjYAtUgo+ZPduRXLjPlqKVc2ejeR544oeqpbn8Egn8A==", "dependencies": [ @@ -1899,8 +1775,8 @@ "@tanstack/history@1.162.0": { "integrity": "sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA==" }, - "@tanstack/router-core@1.171.9": { - "integrity": "sha512-QM5ZwLT9c5ZcTJW0QQZRRIBC4qjImUyUCXCVyuYVOF9xr76XLsJSX4F2dOxr9VptAv+W+TkWNOYdX8VaO9kdgA==", + "@tanstack/router-core@1.171.13": { + "integrity": "sha512-+NOwEj1kO/6IGmpHRIZHasYxYWpyBQGNIZAST9aNrk9Q3YlU9SgqVnl1pbLa9qAKfeNdXQIRve0RQb/0kyDeDA==", "dependencies": [ "@tanstack/history", "cookie-es", @@ -1908,8 +1784,8 @@ "seroval-plugins" ] }, - "@tanstack/router-generator@1.167.13": { - "integrity": "sha512-DldbCjA8S/CXQBuoyQqr76xqZe9k+H1ymV+ugj2IBHFi4yRzx4z4f2nSsPYlLdpXD2Cf/MEjLncaG7ceY5H5ig==", + "@tanstack/router-generator@1.167.17": { + "integrity": "sha512-xtB9tB2Ws0tWR6Pi7nc3Qk9IYgoh1mQCKWjHqIl9tf6BNUpKoqniJoPAQ4+LGrK8FeZYU0o0p/qlZEyj9FAulA==", "dependencies": [ "@babel/types", "@tanstack/router-core", @@ -1921,19 +1797,15 @@ "zod" ] }, - "@tanstack/router-plugin@1.168.14_vite@7.3.5__lightningcss@1.32.0_vite-plugin-solid@2.11.12__solid-js@1.9.13__vite@7.3.5___lightningcss@1.32.0__lightningcss@1.32.0": { - "integrity": "sha512-z+3vYJ7ouNnMzBIC1hsNWsxaQFu9Gf0WSdE3jBHWa326ipnONqDD5KeCqWGczq0HMdZY4UsDjyfvjucxXhrb0A==", + "@tanstack/router-plugin@1.168.18_vite@7.3.5__lightningcss@1.32.0_vite-plugin-solid@2.11.12__solid-js@1.9.13__vite@7.3.5___lightningcss@1.32.0__lightningcss@1.32.0": { + "integrity": "sha512-MofS28/axfnfnhOD2RSgJEaU882aX5RsAzhGz5Vc4XhAmvCjy919u9JrNs4QsTWFbTD1P7IJ8WFlFVsrg0pStg==", "dependencies": [ "@babel/core", - "@babel/plugin-syntax-jsx", - "@babel/plugin-syntax-typescript", "@babel/template", - "@babel/traverse", "@babel/types", "@tanstack/router-core", "@tanstack/router-generator", "@tanstack/router-utils", - "@tanstack/virtual-file-routes", "chokidar", "unplugin@3.0.0", "vite", @@ -1945,10 +1817,9 @@ "vite-plugin-solid" ] }, - "@tanstack/router-utils@1.162.1": { - "integrity": "sha512-62layyTGmclHDQS/eidwKRfN1hhCKwViG7iEBcVmL0MXgcAB3OOucWCEcDDGd9Cu11H6b4QQ5oOo47MWIqwz0A==", + "@tanstack/router-utils@1.162.2": { + "integrity": "sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==", "dependencies": [ - "@babel/core", "@babel/generator", "@babel/parser", "@babel/types", @@ -1959,10 +1830,9 @@ "tinyglobby" ] }, - "@tanstack/solid-router@1.170.11_solid-js@1.9.13": { - "integrity": "sha512-YTHU4OB74KUj30dBco0rBiw8GGiUHK4849TfLWrPFmcVDUpWcKMz49O1hqEPYP7rLqp/poICvZdTZVDRMG5bUQ==", + "@tanstack/solid-router@1.170.15_solid-js@1.9.13": { + "integrity": "sha512-PJKQR3r47EZNzbwYKhEjsYiux0dEyxJdefz7gOlpOmxRuhnqPThMnNMojg6U99i1K73XSTkwlPgBuLG85guuTA==", "dependencies": [ - "@solid-devtools/logger", "@solid-primitives/refs", "@solidjs/meta", "@tanstack/history", @@ -1971,8 +1841,8 @@ "solid-js" ] }, - "@tanstack/solid-start-client@1.168.8_solid-js@1.9.13": { - "integrity": "sha512-MGCygKT+XMQPbxQ3tb50YQkgYJ7wkLzVsjx0NWF+zG5BbH1qzq2cIUJewMCQI6ZDu/dXl18KsHE36mNrhh5Drg==", + "@tanstack/solid-start-client@1.168.12_solid-js@1.9.13": { + "integrity": "sha512-QBqA3BiZ2fDssw5yqRDmsrV2rG5HIAlf36OaUaJM0dyeEsSHsgcByMrvEJUQ+hE+1zCIxyMf8oEeqwjVoVDdqQ==", "dependencies": [ "@tanstack/router-core", "@tanstack/solid-router", @@ -1980,20 +1850,17 @@ "solid-js" ] }, - "@tanstack/solid-start-server@1.167.14_solid-js@1.9.13": { - "integrity": "sha512-aIg3+fHzhj/8oj15RIM4UUcxrdSiSQRccpV3sr44lRkaJvMPBw/JR214pwyM5D1KUGk3Koi76v/oPJCD1I4RqQ==", + "@tanstack/solid-start-server@1.167.18_solid-js@1.9.13": { + "integrity": "sha512-JzSxfq6PND0oAGK9tWdCjiJ4Np+pMLP+88LAFOiUVrF1+2EKTHNIJPgtpkg3XjnTFPkERhxDzoyaSFaEAmkc+Q==", "dependencies": [ - "@solidjs/meta", - "@tanstack/history", "@tanstack/router-core", "@tanstack/solid-router", - "@tanstack/start-client-core", "@tanstack/start-server-core", "solid-js" ] }, - "@tanstack/solid-start@1.168.19_solid-js@1.9.13_vite@7.3.5__lightningcss@1.32.0_vite-plugin-solid@2.11.12__solid-js@1.9.13__vite@7.3.5___lightningcss@1.32.0__lightningcss@1.32.0": { - "integrity": "sha512-v7JdCNZoHVZ1ASZ7DgPubvqXJ+27hiQVrhE4i/n7+clSX5fnl/o+vWFSQPWHzPj5RU39A/eTE9bVyU4Bhp2Obg==", + "@tanstack/solid-start@1.168.24_solid-js@1.9.13_vite@7.3.5__lightningcss@1.32.0_vite-plugin-solid@2.11.12__solid-js@1.9.13__vite@7.3.5___lightningcss@1.32.0__lightningcss@1.32.0": { + "integrity": "sha512-rGt201ILaV6UGA2KJxaUF4XzSVsBQvEx+ebEmu41pGP9i2EwrpJ1qML00ULAdohyIluvbP2TCNL8a5yXaxBOOQ==", "dependencies": [ "@tanstack/solid-router", "@tanstack/solid-start-client", @@ -2009,8 +1876,8 @@ "vite" ] }, - "@tanstack/start-client-core@1.170.7": { - "integrity": "sha512-LKNHeK3n8DZ2ub1KpidWCISvJNq7wGuErrd2oSyoUDHSo90ldl7JJcG4OpbDS7GQjqIZ79M47eklajwgKXBxrQ==", + "@tanstack/start-client-core@1.170.11": { + "integrity": "sha512-0rInC7S5CwixMoUEWqWJAGRF+T7Kq5tbdbP5uUUmV2CW4sgv58KLoj++5TetC4a02wjQxfjMNx1vQyRVhUZmyg==", "dependencies": [ "@tanstack/router-core", "@tanstack/start-fn-stubs", @@ -2021,18 +1888,16 @@ "@tanstack/start-fn-stubs@1.162.0": { "integrity": "sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ==" }, - "@tanstack/start-plugin-core@1.171.11_vite@7.3.5__lightningcss@1.32.0_vite-plugin-solid@2.11.12__solid-js@1.9.13__vite@7.3.5___lightningcss@1.32.0__lightningcss@1.32.0": { - "integrity": "sha512-f6z9W8lYveloSLFocMGfUrS4UL2sc0qrJiB0cuhs885W/bRE1iG0Vm9cNhM/khWxrLmWNeN5eelcnfB77QjLJg==", + "@tanstack/start-plugin-core@1.171.16_vite@7.3.5__lightningcss@1.32.0_vite-plugin-solid@2.11.12__solid-js@1.9.13__vite@7.3.5___lightningcss@1.32.0__lightningcss@1.32.0": { + "integrity": "sha512-+j209gE9vzkAyjCrgpRpQUP2Zra/KXV+HSmNBhqja+ShdYtJtu7pX5yeE+TYH4FSsEBCG6Un+gHhBVI4O4OWsw==", "dependencies": [ "@babel/code-frame@7.27.1", "@babel/core", "@babel/types", - "@rolldown/pluginutils", "@tanstack/router-core", "@tanstack/router-generator", "@tanstack/router-plugin", "@tanstack/router-utils", - "@tanstack/start-client-core", "@tanstack/start-server-core", "exsolve", "lightningcss", @@ -2052,8 +1917,8 @@ "vite" ] }, - "@tanstack/start-server-core@1.169.9": { - "integrity": "sha512-i2OXl+svinZI+7tE2FTQSc9vLIMp0/3nQAI47zg7cZ/0btmC2g2wVrEUa7pF4bmS2TrKEfmOancbURWfB2YrkA==", + "@tanstack/start-server-core@1.169.13": { + "integrity": "sha512-GlXpcenAEyQsckzX0AmVIv0u5Ufm6uHss39Xd2gpuHpBDM/+Bc9JOX7ygqxqqcNpvmTBB1vy7R/EfTffsoP/xA==", "dependencies": [ "@tanstack/history", "@tanstack/router-core", @@ -2064,8 +1929,8 @@ "seroval" ] }, - "@tanstack/start-storage-context@1.167.11": { - "integrity": "sha512-19wywJH3jiamctg4BxXme0G9iH+P5qHSILxBbyksTK727shDEZPb6V/NzO2dz4cKFAoB6TdBcKBj/guADClOfQ==", + "@tanstack/start-storage-context@1.167.15": { + "integrity": "sha512-Jy0q4vdG6pv76N92+X+ag3fuOV2zINQagYyMN1/es7tPI1vzpKECIU8AqHqzI6ahkwaph7XDvmfUkiLJ3i4LOA==", "dependencies": [ "@tanstack/router-core" ] @@ -4704,8 +4569,8 @@ "npm:@solid-primitives/i18n@^2.2.1", "npm:@solid-primitives/refs@^1.1.3", "npm:@solid-primitives/storage@^4.3.4", - "npm:@tanstack/solid-router@^1.170.11", - "npm:@tanstack/solid-start@^1.168.19", + "npm:@tanstack/solid-router@^1.170.15", + "npm:@tanstack/solid-start@^1.168.24", "npm:@vite-pwa/assets-generator@^1.0.2", "npm:class-variance-authority@~0.7.1", "npm:kysely@~0.29.2", diff --git a/package.json b/package.json index 359b85b..2f1b237 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "@solid-primitives/i18n": "^2.2.1", "@solid-primitives/refs": "^1.1.3", "@solid-primitives/storage": "^4.3.4", - "@tanstack/solid-router": "^1.170.11", - "@tanstack/solid-start": "^1.168.19", + "@tanstack/solid-router": "^1.170.15", + "@tanstack/solid-start": "^1.168.24", "@vite-pwa/assets-generator": "^1.0.2", "class-variance-authority": "^0.7.1", "kysely": "^0.29.2", diff --git a/pkgs/@scope.db/client/migrations/2026-05-04.initial.ts b/pkgs/@scope.db/client/migrations/2026-05-04.initial.ts index 8f7f710..d3b5d1e 100644 --- a/pkgs/@scope.db/client/migrations/2026-05-04.initial.ts +++ b/pkgs/@scope.db/client/migrations/2026-05-04.initial.ts @@ -1,4 +1,4 @@ -import type { Migration } from "kysely"; +import type { Migration } from "kysely/migration"; export default { up: async ({ schema }) => { @@ -6,6 +6,7 @@ export default { .addColumn("id", "text", (col) => col.primaryKey()) .addColumn("dueAt", "text", (col) => col.notNull()) .addColumn("title", "text", (col) => col.notNull()) + .addColumn("completedAt", "text") .execute(); }, } satisfies Migration; diff --git a/pkgs/@scope.db/client/mod.ts b/pkgs/@scope.db/client/mod.ts index 6b4707c..0ea2495 100644 --- a/pkgs/@scope.db/client/mod.ts +++ b/pkgs/@scope.db/client/mod.ts @@ -9,9 +9,12 @@ type Database = { id: string; dueAt: string; title: string; + completedAt?: string; }; }; +export type Item = Database["item"]; + let sqlocal: SQLocalKysely; const useSqlocal = createClientOnlyFn(() => { return sqlocal ??= new SQLocalKysely({ diff --git a/src/components/app.add-item.tsx b/src/components/app.add-item.tsx new file mode 100644 index 0000000..4188af7 --- /dev/null +++ b/src/components/app.add-item.tsx @@ -0,0 +1,13 @@ +import * as itemFns from "../functions/item.queries.ts"; + +type AddItemProps = { + selectedDate: () => Temporal.PlainDate; +}; +export const AddItem = (props: AddItemProps) => { + const addItem = async () => { + console.log("pressed the button.."); + return await itemFns.insert(props.selectedDate().toString()); + }; + + return ; +}; diff --git a/src/components/app.item.tsx b/src/components/app.item.tsx new file mode 100644 index 0000000..7965455 --- /dev/null +++ b/src/components/app.item.tsx @@ -0,0 +1,57 @@ +import type { Item as DbItem } from "@scope/db/client"; +import { cva } from "class-variance-authority"; +import * as itemFns from "../functions/item.queries.ts"; +import { tw } from "../utils/unocss.ts"; +import { Swiper } from "./common.swiper.tsx"; + +const item = cva(tw("b-1 mx-1 flex flex-col gap-1 rounded-xl bg-white p-2"), { + variants: { + completed: { + false: "b-stone-500", + true: "b-emerald-200", + }, + }, +}); + +const add3Days = (date: string) => { + return Temporal.PlainDate.from(date).add(Temporal.Duration.from({ days: 3 })).toString(); +}; + +type ItemProps = { + item: DbItem; + selectedDate: Temporal.PlainDate; +}; +export const Item = (props: ItemProps) => { + return ( + , + onClick: () => itemFns.postpone(props.item.id, add3Days(props.item.dueAt)), + }, + { + class: + "b-1 b-stone-500 flex aspect-square h-full flex-col items-center justify-center gap-1 rounded-xl bg-white", + children: , + onClick: () => itemFns.remove(props.item.id), + }, + ], + main: [ + { + class: + "b-1 b-stone-500 flex aspect-square h-full flex-col items-center justify-center gap-1 rounded-xl bg-white", + children: , + onClick: () => + itemFns.complete(props.item.id, !props.item.completedAt ? props.selectedDate.toString() : undefined), + }, + ], + }} + > + {props.item.title} + + ); +}; diff --git a/src/components/app.missed-items.tsx b/src/components/app.missed-items.tsx index 684fa66..3354dea 100644 --- a/src/components/app.missed-items.tsx +++ b/src/components/app.missed-items.tsx @@ -1,6 +1,6 @@ import * as clientDb from "@scope/db/client"; -import { type Accessor, Index, Show } from "solid-js"; -import { Swiper } from "./common.swiper.tsx"; +import { type Accessor, For, Show } from "solid-js"; +import { Item } from "./app.item.tsx"; import { useI18n } from "./hook.i18n.ts"; type MissedItemsProps = { @@ -12,6 +12,7 @@ export const MissedItems = (props: MissedItemsProps) => { const items = clientDb.useLiveQuery(({ db }) => { return db.selectFrom("item") .where("dueAt", "<", props.today().toString()) + .where("completedAt", "is", null) .selectAll(); }); @@ -23,15 +24,9 @@ export const MissedItems = (props: MissedItemsProps) => {
- - {(item) => ( -
- - {item().title} - -
- )} -
+ + {(item) => } +
diff --git a/src/components/common.button.tsx b/src/components/common.button.tsx index 600a0b0..9360ef8 100644 --- a/src/components/common.button.tsx +++ b/src/components/common.button.tsx @@ -7,7 +7,7 @@ const defaultButtonProps: ButtonProps = { type: "button", }; -type ButtonProps = ComponentProps<"button">; +export type ButtonProps = ComponentProps<"button">; export const Button = (props: ButtonProps) => { const merged = mergeProps(defaultButtonProps, props); const [local, attribs] = splitProps(merged, ["class", "ref"]); diff --git a/src/components/common.swiper.tsx b/src/components/common.swiper.tsx index cd60eaa..479b21c 100644 --- a/src/components/common.swiper.tsx +++ b/src/components/common.swiper.tsx @@ -1,14 +1,29 @@ import { cx } from "class-variance-authority"; -import { type JSX, type ParentProps } from "solid-js"; +import { Index, type JSX, mergeProps, type ParentProps, Show } from "solid-js"; import { createStore, produce } from "solid-js/store"; -import {} from "sqlocal"; +import { Button, type ButtonProps } from "./common.button.tsx"; type PointerEventHandler = JSX.EventHandler; -type SwiperProps = ParentProps & { +type SwiperAction = { + title: string | JSX.Element; + action?: () => Promise; +}; + +const defaultProps = { + panThreshold: 8, +}; + +type SwiperProps = ParentProps & Partial & { + actions?: { + main?: Array; + alt?: Array; + }; class?: string; + swapMainToAlt?: boolean; }; export const Swiper = (props: SwiperProps) => { + const merged = mergeProps(defaultProps, props); const handleContextMenu: PointerEventHandler = (evt) => { evt.preventDefault(); }; @@ -29,13 +44,34 @@ export const Swiper = (props: SwiperProps) => { const handlePointerDown: PointerEventHandler = (evt) => { setState({ isDragging: true, - startX: evt.clientX, - finalX: evt.clientX, - startY: evt.clientY, - finalY: evt.clientY, + startX: state.startX ?? evt.clientX, + finalX: state.finalX ?? evt.clientX, + startY: state.startY ?? evt.clientY, + finalY: state.finalY ?? evt.clientY, }); }; + let ref: Element; + + const widthAction = () => ref.clientHeight + 4; + const widthMainActionBar = () => (props.actions?.main?.length ?? 0) * widthAction(); + const widthAltActionBar = () => (props.actions?.alt?.length ?? 0) * widthAction(); + + const widthActionBar = (deltaX: number) => { + return deltaX > 0 + ? (props.swapMainToAlt ? widthAltActionBar() : widthMainActionBar()) + : (props.swapMainToAlt ? widthMainActionBar() : widthAltActionBar()); + }; + const inverseCorrection = (deltaX: number) => { + return deltaX > 0 ? (props.swapMainToAlt ? -1 : 1) : (props.swapMainToAlt ? 1 : -1); + }; + const freeMove = (deltaX: number) => { + return Math.max(0, Math.min(Math.abs(deltaX), widthActionBar(deltaX))) * inverseCorrection(deltaX); + }; + const restMove = (deltaX: number) => { + return Math.sqrt(Math.abs(deltaX - freeMove(deltaX)) / 10) * inverseCorrection(deltaX); + }; + const handlePointerMove: PointerEventHandler = (evt) => { setState(produce((draft) => { draft.finalX = evt.clientX; @@ -45,21 +81,38 @@ export const Swiper = (props: SwiperProps) => { const deltaY = draft.finalY! - draft.startY!; const delta = Math.hypot(deltaX, deltaY); - if (!draft.action && delta > 16) { + if (!draft.action && delta > merged.panThreshold) { const diffXoverY = Math.abs(deltaX) > Math.abs(deltaY); draft.action = diffXoverY ? "panX" : "panY"; evt.target.setPointerCapture(evt.pointerId); } if (draft.action === "panX") { - draft.deltaX = deltaX; + draft.deltaX = freeMove(deltaX) + restMove(deltaX); } else if (draft.action === "panY") { globalThis.scrollBy(0, -evt.movementY); } })); }; - const handlerPointerCancel: PointerEventHandler = (evt) => { + const handlePointerUp: PointerEventHandler = (evt) => { + setState({ + isDragging: undefined, + action: undefined, + + startX: undefined, + finalX: undefined, + deltaX: (Math.abs(state.deltaX!) > (widthActionBar(state.deltaX!) / 5 * 4)) + ? widthActionBar(state.deltaX!) * inverseCorrection(state.deltaX!) + : undefined, + + startY: undefined, + finalY: undefined, + deltaY: undefined, + }); + }; + + const handlerPointerCancel: PointerEventHandler = () => { setState({ isDragging: undefined, action: undefined, @@ -75,16 +128,43 @@ export const Swiper = (props: SwiperProps) => { }; return ( -
+
ref = el} class="relative"> + + {(actions) => ( +
+ {(action) =>
+ )} +
+ + + {(actions) => ( +
+ {(action) =>
+ )} +
+
{ export const insert = createClientOnlyFn(async (dueAt: string) => { const db = useClientDb(); - return await db.insertInto("item").values({ - id: nanoid(), - title: nanoid(), - dueAt, - }).execute(); + return await db.insertInto("item") + .values({ + id: nanoid(), + title: nanoid(), + dueAt, + }) + .execute(); +}); + +export const remove = createClientOnlyFn(async (id: string) => { + const db = useClientDb(); + + return await db.deleteFrom("item") + .where("id", "=", id) + .executeTakeFirst(); +}); + +export const postpone = createClientOnlyFn(async (id: string, dueAt: string) => { + const db = useClientDb(); + + return await db.updateTable("item") + .set("dueAt", dueAt).where("id", "=", id) + .executeTakeFirst(); +}); + +export const complete = createClientOnlyFn(async (id: string, completedAt?: string) => { + const db = useClientDb(); + + return await db.updateTable("item") + .set("completedAt", completedAt).where("id", "=", id) + .executeTakeFirst(); }); diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 3b63f64..6e0771d 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,10 +1,10 @@ import * as clientDb from "@scope/db/client"; import { createFileRoute } from "@tanstack/solid-router"; -import { createSignal, Index, Show, Suspense } from "solid-js"; +import { createSignal, For, Show, Suspense } from "solid-js"; +import { AddItem } from "../components/app.add-item.tsx"; import { AppHeader } from "../components/app.header.tsx"; +import { Item } from "../components/app.item.tsx"; import { MissedItems } from "../components/app.missed-items.tsx"; -import { Swiper } from "../components/common.swiper.tsx"; -import * as itemFns from "../functions/item.queries.ts"; export const Route = createFileRoute("/")({ component: () => { @@ -18,34 +18,27 @@ export const Route = createFileRoute("/")({ .selectAll(); }); - const addItem = async () => { - console.log("pressed the button.."); - return await itemFns.insert(selectedDate().toString()); - }; - return ( -
+
- - = 0}> + + - - + +
- - {(item) => ( - - {item().title} - - )} - + + {(item) => } +
- + = -1}> + +
); }, -- 2.51.2