mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 14:10:52 +01:00
back to flyonui
This commit is contained in:
@ -80,3 +80,7 @@ cd haex-vault
|
||||
pnpm i
|
||||
pnpm tauri dev
|
||||
```
|
||||
|
||||
```
|
||||
shadcn components: pnpm dlx shadcn-vue@latest add alert-dialog button card dialog dropdown-menu input label menubar navigation-menu popover resizable sheet sonner switch tabs tooltip
|
||||
```
|
||||
|
||||
@ -1,7 +1,14 @@
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
extends: ['github:haexhub/haex-base-ui', { install: true }],
|
||||
modules: [
|
||||
'nuxt-zod-i18n',
|
||||
'@nuxtjs/i18n',
|
||||
'@pinia/nuxt',
|
||||
'@vueuse/nuxt',
|
||||
'@nuxt/icon',
|
||||
'nuxt-snackbar',
|
||||
],
|
||||
|
||||
compatibilityDate: '2024-11-01',
|
||||
|
||||
@ -37,26 +44,33 @@ export default defineNuxtConfig({
|
||||
],
|
||||
},
|
||||
|
||||
/* i18n: {
|
||||
strategy: "prefix_and_default",
|
||||
defaultLocale: "de",
|
||||
vueI18n: "~/i18n/i18n.config.ts",
|
||||
i18n: {
|
||||
strategy: 'prefix_and_default',
|
||||
defaultLocale: 'de',
|
||||
vueI18n: '~/i18n/i18n.config.ts',
|
||||
|
||||
locales: [
|
||||
{ code: "de", language: "de-DE", isCatchallLocale: true },
|
||||
{ code: "en", language: "en-EN" },
|
||||
{ code: 'de', language: 'de-DE', isCatchallLocale: true },
|
||||
{ code: 'en', language: 'en-EN' },
|
||||
],
|
||||
|
||||
detectBrowserLanguage: {
|
||||
useCookie: true,
|
||||
cookieKey: "i18n_redirected",
|
||||
redirectOn: "root", // recommended
|
||||
cookieKey: 'i18n_redirected',
|
||||
redirectOn: 'root', // recommended
|
||||
},
|
||||
types: "composition",
|
||||
types: 'composition',
|
||||
bundle: {
|
||||
optimizeTranslationDirective: false,
|
||||
},
|
||||
}, */
|
||||
},
|
||||
|
||||
zodI18n: {
|
||||
localeCodesMapping: {
|
||||
'en-GB': 'en',
|
||||
'de-DE': 'de',
|
||||
},
|
||||
},
|
||||
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
@ -88,4 +102,4 @@ export default defineNuxtConfig({
|
||||
strictPort: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
11
package.json
11
package.json
@ -30,26 +30,19 @@
|
||||
"@tauri-apps/plugin-store": "^2.2.0",
|
||||
"@vueuse/core": "^13.2.0",
|
||||
"@vueuse/nuxt": "^13.2.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"drizzle-orm": "^0.43.1",
|
||||
"lucide-vue-next": "^0.511.0",
|
||||
"flyonui": "^2.2.0",
|
||||
"nuxt": "^3.17.3",
|
||||
"nuxt-snackbar": "1.3.0",
|
||||
"nuxt-zod-i18n": "^1.11.5",
|
||||
"reka-ui": "^2.2.1",
|
||||
"shadcn-nuxt": "^2.1.0",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"tw-animate-css": "^1.3.0",
|
||||
"vue": "^3.5.14",
|
||||
"vue-router": "^4.5.1",
|
||||
"vue-sonner": "^2.0.0",
|
||||
"zod": "^3.25.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.340",
|
||||
"@iconify/tailwind4": "^1.0.6",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@tauri-apps/cli": "^2.5.0",
|
||||
"drizzle-kit": "^0.31.1"
|
||||
},
|
||||
|
||||
243
pnpm-lock.yaml
generated
243
pnpm-lock.yaml
generated
@ -53,45 +53,30 @@ importers:
|
||||
'@vueuse/nuxt':
|
||||
specifier: ^13.2.0
|
||||
version: 13.2.0(magicast@0.3.5)(nuxt@3.17.4(@libsql/client@0.15.7)(@parcel/watcher@2.5.1)(@types/node@22.15.21)(db0@0.3.2(@libsql/client@0.15.7)(drizzle-orm@0.43.1(@libsql/client@0.15.7)))(drizzle-orm@0.43.1(@libsql/client@0.15.7))(eslint@9.27.0(jiti@2.4.2))(ioredis@5.6.1)(lightningcss@1.30.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.0)(terser@5.39.2)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.2)(yaml@2.8.0))(vue-tsc@2.2.10(typescript@5.8.3))(yaml@2.8.0))(vue@3.5.14(typescript@5.8.3))
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
drizzle-orm:
|
||||
specifier: ^0.43.1
|
||||
version: 0.43.1(@libsql/client@0.15.7)
|
||||
lucide-vue-next:
|
||||
specifier: ^0.511.0
|
||||
version: 0.511.0(vue@3.5.14(typescript@5.8.3))
|
||||
flyonui:
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
nuxt:
|
||||
specifier: ^3.17.3
|
||||
version: 3.17.4(@libsql/client@0.15.7)(@parcel/watcher@2.5.1)(@types/node@22.15.21)(db0@0.3.2(@libsql/client@0.15.7)(drizzle-orm@0.43.1(@libsql/client@0.15.7)))(drizzle-orm@0.43.1(@libsql/client@0.15.7))(eslint@9.27.0(jiti@2.4.2))(ioredis@5.6.1)(lightningcss@1.30.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.0)(terser@5.39.2)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.2)(yaml@2.8.0))(vue-tsc@2.2.10(typescript@5.8.3))(yaml@2.8.0)
|
||||
nuxt-snackbar:
|
||||
specifier: 1.3.0
|
||||
version: 1.3.0(magicast@0.3.5)(vue@3.5.14(typescript@5.8.3))
|
||||
nuxt-zod-i18n:
|
||||
specifier: ^1.11.5
|
||||
version: 1.11.5(magicast@0.3.5)(typescript@5.8.3)
|
||||
reka-ui:
|
||||
specifier: ^2.2.1
|
||||
version: 2.2.1(typescript@5.8.3)(vue@3.5.14(typescript@5.8.3))
|
||||
tailwind-merge:
|
||||
specifier: ^3.3.0
|
||||
version: 3.3.0
|
||||
tailwindcss:
|
||||
specifier: ^4.1.7
|
||||
version: 4.1.7
|
||||
tw-animate-css:
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0
|
||||
vue:
|
||||
specifier: ^3.5.14
|
||||
version: 3.5.14(typescript@5.8.3)
|
||||
vue-router:
|
||||
specifier: ^4.5.1
|
||||
version: 4.5.1(vue@3.5.14(typescript@5.8.3))
|
||||
vue-sonner:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
zod:
|
||||
specifier: ^3.25.20
|
||||
version: 3.25.20
|
||||
@ -102,9 +87,6 @@ importers:
|
||||
'@iconify/tailwind4':
|
||||
specifier: ^1.0.6
|
||||
version: 1.0.6(tailwindcss@4.1.7)
|
||||
'@nuxtjs/color-mode':
|
||||
specifier: ^3.5.2
|
||||
version: 3.5.2(magicast@0.3.5)
|
||||
'@tauri-apps/cli':
|
||||
specifier: ^2.5.0
|
||||
version: 2.5.0
|
||||
@ -604,9 +586,6 @@ packages:
|
||||
'@floating-ui/utils@0.2.9':
|
||||
resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
|
||||
|
||||
'@floating-ui/vue@1.1.6':
|
||||
resolution: {integrity: sha512-XFlUzGHGv12zbgHNk5FN2mUB7ROul3oG2ENdTpWdE+qMFxyNxWSRmsoyhiEnpmabNm6WnUvR1OvJfUfN4ojC1A==}
|
||||
|
||||
'@humanfs/core@0.19.1':
|
||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
@ -649,12 +628,6 @@ packages:
|
||||
peerDependencies:
|
||||
vue: '>=3'
|
||||
|
||||
'@internationalized/date@3.8.1':
|
||||
resolution: {integrity: sha512-PgVE6B6eIZtzf9Gu5HvJxRK3ufUFz9DhspELuhW/N0GuMGMTLvPQNRkHP2hTuP9lblOk+f+1xi96sPiPXANXAA==}
|
||||
|
||||
'@internationalized/number@3.6.2':
|
||||
resolution: {integrity: sha512-E5QTOlMg9wo5OrKdHD6edo1JJlIoOsylh0+mbf0evi1tHJwMZfJSaBpGtnJV9N7w3jeiioox9EG/EWRWPh82vg==}
|
||||
|
||||
'@intlify/bundle-utils@10.0.1':
|
||||
resolution: {integrity: sha512-WkaXfSevtpgtUR4t8K2M6lbR7g03mtOxFeh+vXp5KExvPqS12ppaRj1QxzwRuRI5VUto54A22BjKoBMLyHILWQ==}
|
||||
engines: {node: '>= 18'}
|
||||
@ -938,9 +911,6 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.3.4
|
||||
|
||||
'@nuxtjs/color-mode@3.5.2':
|
||||
resolution: {integrity: sha512-cC6RfgZh3guHBMLLjrBB2Uti5eUoGM9KyauOaYS9ETmxNWBMTvpgjvSiSJp1OFljIXPIqVTJ3xtJpSNZiO3ZaA==}
|
||||
|
||||
'@nuxtjs/i18n@9.5.4':
|
||||
resolution: {integrity: sha512-HSCC6bLvkI74AOJ/Hsa8+52uy92Bzpu/lVOKYJZIR/HV4TtV48fgKLPRlL8RmCXx/AmKBtrPsLfhAAIj9RBAKQ==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
@ -1406,9 +1376,6 @@ packages:
|
||||
'@speed-highlight/core@1.2.7':
|
||||
resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==}
|
||||
|
||||
'@swc/helpers@0.5.17':
|
||||
resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==}
|
||||
|
||||
'@tailwindcss/node@4.1.7':
|
||||
resolution: {integrity: sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==}
|
||||
|
||||
@ -1499,14 +1466,6 @@ packages:
|
||||
peerDependencies:
|
||||
vite: ^5.2.0 || ^6
|
||||
|
||||
'@tanstack/virtual-core@3.13.9':
|
||||
resolution: {integrity: sha512-3jztt0jpaoJO5TARe2WIHC1UQC3VMLAFUW5mmMo0yrkwtDB2AQP0+sh10BVUpWrnvHjSLvzFizydtEGLCJKFoQ==}
|
||||
|
||||
'@tanstack/vue-virtual@3.13.9':
|
||||
resolution: {integrity: sha512-HsvHaOo+o52cVcPhomKDZ3CMpTF/B2qg+BhPHIQJwzn4VIqDyt/rRVqtIomG6jE83IFsE2vlr6cmx7h3dHA0SA==}
|
||||
peerDependencies:
|
||||
vue: ^2.7.0 || ^3.0.0
|
||||
|
||||
'@tauri-apps/api@2.5.0':
|
||||
resolution: {integrity: sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA==}
|
||||
|
||||
@ -1795,17 +1754,11 @@ packages:
|
||||
'@vue/shared@3.5.14':
|
||||
resolution: {integrity: sha512-oXTwNxVfc9EtP1zzXAlSlgARLXNC84frFYkS0HHz0h3E4WZSP9sywqjqzGCP9Y34M8ipNmd380pVgmMuwELDyQ==}
|
||||
|
||||
'@vueuse/core@12.8.2':
|
||||
resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==}
|
||||
|
||||
'@vueuse/core@13.2.0':
|
||||
resolution: {integrity: sha512-n5TZoIAxbWAQ3PqdVPDzLgIRQOujFfMlatdI+f7ditSmoEeNpPBvp7h2zamzikCmrhFIePAwdEQB6ENccHr7Rg==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/metadata@12.8.2':
|
||||
resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==}
|
||||
|
||||
'@vueuse/metadata@13.2.0':
|
||||
resolution: {integrity: sha512-kPpzuQCU0+D8DZCzK0iPpIcXI+6ufWSgwnjJ6//GNpEn+SHViaCtR+XurzORChSgvpHO9YC8gGM97Y1kB+UabA==}
|
||||
|
||||
@ -1815,9 +1768,6 @@ packages:
|
||||
nuxt: ^3.0.0 || ^4.0.0-0
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/shared@12.8.2':
|
||||
resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==}
|
||||
|
||||
'@vueuse/shared@13.2.0':
|
||||
resolution: {integrity: sha512-vx9ZPDF5HcU9up3Jgt3G62dMUfZEdk6tLyBAHYAG4F4n73vpaA7J5hdncDI/lS9Vm7GA/FPlbOmh9TrDZROTpg==}
|
||||
peerDependencies:
|
||||
@ -1938,10 +1888,6 @@ packages:
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
aria-hidden@1.2.6:
|
||||
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
array-union@2.1.0:
|
||||
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
|
||||
engines: {node: '>=8'}
|
||||
@ -2086,9 +2032,6 @@ packages:
|
||||
citty@0.1.6:
|
||||
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
|
||||
|
||||
class-variance-authority@0.7.1:
|
||||
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
||||
|
||||
clipboardy@4.0.0:
|
||||
resolution: {integrity: sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==}
|
||||
engines: {node: '>=18'}
|
||||
@ -2097,10 +2040,6 @@ packages:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
clsx@2.1.1:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cluster-key-slot@1.1.2:
|
||||
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -2804,6 +2743,9 @@ packages:
|
||||
flatted@3.3.3:
|
||||
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
|
||||
|
||||
flyonui@2.2.0:
|
||||
resolution: {integrity: sha512-Gncal89zwklAYpqV8IjSgN/1edsXnbTlxhfKRcYa2WgeY8jBSuoNBQWqdL1DSLuXarxYluimwIv34YaRKNuSzg==}
|
||||
|
||||
fn.name@1.1.0:
|
||||
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
|
||||
|
||||
@ -3262,7 +3204,6 @@ packages:
|
||||
|
||||
libsql@0.5.11:
|
||||
resolution: {integrity: sha512-P2xY1nL2Jl7oM75LcguAEYqouVcevWhLWT8RU/p9ldaqQx5s/chF9t5ZFXPWP0x9myQQ4SguRqPO+FqdnCzKQg==}
|
||||
cpu: [x64, arm64, wasm32, arm]
|
||||
os: [darwin, linux, win32]
|
||||
|
||||
lightningcss-darwin-arm64@1.30.1:
|
||||
@ -3395,11 +3336,6 @@ packages:
|
||||
lru-cache@5.1.1:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
|
||||
lucide-vue-next@0.511.0:
|
||||
resolution: {integrity: sha512-VSv0F3pHniGN7JMMzDcLFNMQbl8381+shNnHwV8hi+El7xl2ZL8qdNuzPoiBViKk8mTKK5K3ZDfmE/wEcTZVIQ==}
|
||||
peerDependencies:
|
||||
vue: '>=3.0.1'
|
||||
|
||||
luxon@3.6.1:
|
||||
resolution: {integrity: sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==}
|
||||
engines: {node: '>=12'}
|
||||
@ -3652,6 +3588,9 @@ packages:
|
||||
nth-check@2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
|
||||
nuxt-snackbar@1.3.0:
|
||||
resolution: {integrity: sha512-wfUR0BdvRUj6C4qdDmBPHtkCxH3wwfPI5dOkCZM4g6lXB7mssHji56We99Pj1ue8wXWBMYgcrF7pg/jJ6GGVIQ==}
|
||||
|
||||
nuxt-zod-i18n@1.11.5:
|
||||
resolution: {integrity: sha512-0XR7DLLR7JGSKxhxdOOFduLvjfPIeNfXpaWxQHBnwPg/zQjL2VuhlBuq1ltZioRw4f1Wfpy5+MwPyjK3PrmY0g==}
|
||||
|
||||
@ -4142,11 +4081,6 @@ packages:
|
||||
resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
reka-ui@2.2.1:
|
||||
resolution: {integrity: sha512-oLHiyBn6gTIQGnTnv8G5LQuFp9j8HuUNl0qdnW3XPhFb/07hrxzFpjo2kt/jxOZive+n/XWDbOjSj2h9Hih3qA==}
|
||||
peerDependencies:
|
||||
vue: '>= 3.2.0'
|
||||
|
||||
remove-trailing-separator@1.1.0:
|
||||
resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==}
|
||||
|
||||
@ -4429,9 +4363,6 @@ packages:
|
||||
resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tailwind-merge@3.3.0:
|
||||
resolution: {integrity: sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==}
|
||||
|
||||
tailwindcss@4.1.7:
|
||||
resolution: {integrity: sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==}
|
||||
|
||||
@ -4465,6 +4396,9 @@ packages:
|
||||
text-hex@1.0.0:
|
||||
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
|
||||
|
||||
tiny-emitter@2.1.0:
|
||||
resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
|
||||
|
||||
tiny-invariant@1.3.3:
|
||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||
|
||||
@ -4529,9 +4463,6 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
|
||||
|
||||
tw-animate-css@1.3.0:
|
||||
resolution: {integrity: sha512-jrJ0XenzS9KVuDThJDvnhalbl4IYiMQ/XvpA0a2FL8KmlK+6CSMviO7ROY/I7z1NnUs5NnDhlM6fXmF40xPxzw==}
|
||||
|
||||
type-check@0.4.0:
|
||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@ -4811,17 +4742,6 @@ packages:
|
||||
vue-bundle-renderer@2.1.1:
|
||||
resolution: {integrity: sha512-+qALLI5cQncuetYOXp4yScwYvqh8c6SMXee3B+M7oTZxOgtESP0l4j/fXdEJoZ+EdMxkGWIj+aSEyjXkOdmd7g==}
|
||||
|
||||
vue-demi@0.14.10:
|
||||
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.0.0-rc.1
|
||||
vue: ^3.0.0-0 || ^2.6.0
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
vue-devtools-stub@0.1.0:
|
||||
resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==}
|
||||
|
||||
@ -4836,15 +4756,22 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
|
||||
vue-sonner@2.0.0:
|
||||
resolution: {integrity: sha512-nvlqGGWvxEv9UnKcZxsGdKpHrODEdv3CXAJF3er+1pLC03caJt2+v9HuWtRqlBQwUr1SFttsYuwVbpbEl05n4A==}
|
||||
|
||||
vue-tsc@2.2.10:
|
||||
resolution: {integrity: sha512-jWZ1xSaNbabEV3whpIDMbjVSVawjAyW+x1n3JeGQo7S0uv2n9F/JMgWW90tGWNFRKya4YwKMZgCtr0vRAM7DeQ==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.0'
|
||||
|
||||
vue3-icon@3.0.3:
|
||||
resolution: {integrity: sha512-B9/f/nwbVBUsBQVSNp7nFIsD/uM/M9MbK38FQFXs8IMSClrEG0gXWtyViQN28na25zpsoKH8Blrzcn/LbUV76Q==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
|
||||
vue3-snackbar@2.4.0:
|
||||
resolution: {integrity: sha512-XDdaWf+9cjLICJyUNThqh4IRHWPzIqNvaEWS0zW5bxWHXDUCkXRhP7OjZky7EV7AfZa/6UNE82By4rpBaKohWg==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
|
||||
vue@3.5.14:
|
||||
resolution: {integrity: sha512-LbOm50/vZFG6Mhy6KscQYXZMQ0LMCC/y40HDJPPvGFQ+i/lUH+PJHR6C3assgOQiXdl6tAfsXHbXYVBZZu65ew==}
|
||||
peerDependencies:
|
||||
@ -5401,15 +5328,6 @@ snapshots:
|
||||
|
||||
'@floating-ui/utils@0.2.9': {}
|
||||
|
||||
'@floating-ui/vue@1.1.6(vue@3.5.14(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.7.0
|
||||
'@floating-ui/utils': 0.2.9
|
||||
vue-demi: 0.14.10(vue@3.5.14(typescript@5.8.3))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@humanfs/core@0.19.1': {}
|
||||
|
||||
'@humanfs/node@0.16.6':
|
||||
@ -5460,14 +5378,6 @@ snapshots:
|
||||
'@iconify/types': 2.0.0
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
|
||||
'@internationalized/date@3.8.1':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.17
|
||||
|
||||
'@internationalized/number@3.6.2':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.17
|
||||
|
||||
'@intlify/bundle-utils@10.0.1(vue-i18n@10.0.7(vue@3.5.14(typescript@5.8.3)))':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 11.1.3
|
||||
@ -6025,15 +5935,6 @@ snapshots:
|
||||
- vue-tsc
|
||||
- yaml
|
||||
|
||||
'@nuxtjs/color-mode@3.5.2(magicast@0.3.5)':
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.17.4(magicast@0.3.5)
|
||||
pathe: 1.1.2
|
||||
pkg-types: 1.3.1
|
||||
semver: 7.7.2
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@nuxtjs/i18n@9.5.4(@vue/compiler-dom@3.5.14)(eslint@9.27.0(jiti@2.4.2))(magicast@0.3.5)(rollup@4.41.0)(vue@3.5.14(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@intlify/h3': 0.6.1
|
||||
@ -6384,10 +6285,6 @@ snapshots:
|
||||
|
||||
'@speed-highlight/core@1.2.7': {}
|
||||
|
||||
'@swc/helpers@0.5.17':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@tailwindcss/node@4.1.7':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
@ -6459,13 +6356,6 @@ snapshots:
|
||||
tailwindcss: 4.1.7
|
||||
vite: 6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.2)(yaml@2.8.0)
|
||||
|
||||
'@tanstack/virtual-core@3.13.9': {}
|
||||
|
||||
'@tanstack/vue-virtual@3.13.9(vue@3.5.14(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@tanstack/virtual-core': 3.13.9
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
|
||||
'@tauri-apps/api@2.5.0': {}
|
||||
|
||||
'@tauri-apps/cli-darwin-arm64@2.5.0':
|
||||
@ -6842,15 +6732,6 @@ snapshots:
|
||||
|
||||
'@vue/shared@3.5.14': {}
|
||||
|
||||
'@vueuse/core@12.8.2(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.21
|
||||
'@vueuse/metadata': 12.8.2
|
||||
'@vueuse/shared': 12.8.2(typescript@5.8.3)
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@vueuse/core@13.2.0(vue@3.5.14(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.21
|
||||
@ -6858,8 +6739,6 @@ snapshots:
|
||||
'@vueuse/shared': 13.2.0(vue@3.5.14(typescript@5.8.3))
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
|
||||
'@vueuse/metadata@12.8.2': {}
|
||||
|
||||
'@vueuse/metadata@13.2.0': {}
|
||||
|
||||
'@vueuse/nuxt@13.2.0(magicast@0.3.5)(nuxt@3.17.4(@libsql/client@0.15.7)(@parcel/watcher@2.5.1)(@types/node@22.15.21)(db0@0.3.2(@libsql/client@0.15.7)(drizzle-orm@0.43.1(@libsql/client@0.15.7)))(drizzle-orm@0.43.1(@libsql/client@0.15.7))(eslint@9.27.0(jiti@2.4.2))(ioredis@5.6.1)(lightningcss@1.30.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.0)(terser@5.39.2)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.2)(yaml@2.8.0))(vue-tsc@2.2.10(typescript@5.8.3))(yaml@2.8.0))(vue@3.5.14(typescript@5.8.3))':
|
||||
@ -6873,12 +6752,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@vueuse/shared@12.8.2(typescript@5.8.3)':
|
||||
dependencies:
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@vueuse/shared@13.2.0(vue@3.5.14(typescript@5.8.3))':
|
||||
dependencies:
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
@ -7028,10 +6901,6 @@ snapshots:
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
aria-hidden@1.2.6:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
array-union@2.1.0: {}
|
||||
|
||||
ast-kit@1.4.3:
|
||||
@ -7184,10 +7053,6 @@ snapshots:
|
||||
dependencies:
|
||||
consola: 3.4.2
|
||||
|
||||
class-variance-authority@0.7.1:
|
||||
dependencies:
|
||||
clsx: 2.1.1
|
||||
|
||||
clipboardy@4.0.0:
|
||||
dependencies:
|
||||
execa: 8.0.1
|
||||
@ -7200,8 +7065,6 @@ snapshots:
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
cluster-key-slot@1.1.2: {}
|
||||
|
||||
color-convert@1.9.3:
|
||||
@ -7865,6 +7728,10 @@ snapshots:
|
||||
|
||||
flatted@3.3.3: {}
|
||||
|
||||
flyonui@2.2.0:
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.7.0
|
||||
|
||||
fn.name@1.1.0: {}
|
||||
|
||||
foreground-child@3.3.1:
|
||||
@ -8438,10 +8305,6 @@ snapshots:
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
|
||||
lucide-vue-next@0.511.0(vue@3.5.14(typescript@5.8.3)):
|
||||
dependencies:
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
|
||||
luxon@3.6.1: {}
|
||||
|
||||
magic-string-ast@0.7.1:
|
||||
@ -8741,6 +8604,15 @@ snapshots:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
||||
nuxt-snackbar@1.3.0(magicast@0.3.5)(vue@3.5.14(typescript@5.8.3)):
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.17.4(magicast@0.3.5)
|
||||
defu: 6.1.4
|
||||
vue3-snackbar: 2.4.0(vue@3.5.14(typescript@5.8.3))
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
- vue
|
||||
|
||||
nuxt-zod-i18n@1.11.5(magicast@0.3.5)(typescript@5.8.3):
|
||||
dependencies:
|
||||
'@intlify/shared': 11.1.3
|
||||
@ -9363,23 +9235,6 @@ snapshots:
|
||||
dependencies:
|
||||
redis-errors: 1.2.0
|
||||
|
||||
reka-ui@2.2.1(typescript@5.8.3)(vue@3.5.14(typescript@5.8.3)):
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.7.0
|
||||
'@floating-ui/vue': 1.1.6(vue@3.5.14(typescript@5.8.3))
|
||||
'@internationalized/date': 3.8.1
|
||||
'@internationalized/number': 3.6.2
|
||||
'@tanstack/vue-virtual': 3.13.9(vue@3.5.14(typescript@5.8.3))
|
||||
'@vueuse/core': 12.8.2(typescript@5.8.3)
|
||||
'@vueuse/shared': 12.8.2(typescript@5.8.3)
|
||||
aria-hidden: 1.2.6
|
||||
defu: 6.1.4
|
||||
ohash: 2.0.11
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- typescript
|
||||
|
||||
remove-trailing-separator@1.1.0: {}
|
||||
|
||||
require-directory@2.1.1: {}
|
||||
@ -9678,8 +9533,6 @@ snapshots:
|
||||
|
||||
system-architecture@0.1.0: {}
|
||||
|
||||
tailwind-merge@3.3.0: {}
|
||||
|
||||
tailwindcss@4.1.7: {}
|
||||
|
||||
tapable@2.2.2: {}
|
||||
@ -9729,6 +9582,8 @@ snapshots:
|
||||
|
||||
text-hex@1.0.0: {}
|
||||
|
||||
tiny-emitter@2.1.0: {}
|
||||
|
||||
tiny-invariant@1.3.3: {}
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
@ -9775,8 +9630,6 @@ snapshots:
|
||||
tslib: 1.14.1
|
||||
typescript: 5.8.3
|
||||
|
||||
tw-animate-css@1.3.0: {}
|
||||
|
||||
type-check@0.4.0:
|
||||
dependencies:
|
||||
prelude-ls: 1.2.1
|
||||
@ -10035,10 +9888,6 @@ snapshots:
|
||||
dependencies:
|
||||
ufo: 1.6.1
|
||||
|
||||
vue-demi@0.14.10(vue@3.5.14(typescript@5.8.3)):
|
||||
dependencies:
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
|
||||
vue-devtools-stub@0.1.0: {}
|
||||
|
||||
vue-i18n@10.0.7(vue@3.5.14(typescript@5.8.3)):
|
||||
@ -10053,14 +9902,22 @@ snapshots:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
|
||||
vue-sonner@2.0.0: {}
|
||||
|
||||
vue-tsc@2.2.10(typescript@5.8.3):
|
||||
dependencies:
|
||||
'@volar/typescript': 2.4.14
|
||||
'@vue/language-core': 2.2.10(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
|
||||
vue3-icon@3.0.3(vue@3.5.14(typescript@5.8.3)):
|
||||
dependencies:
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
|
||||
vue3-snackbar@2.4.0(vue@3.5.14(typescript@5.8.3)):
|
||||
dependencies:
|
||||
tiny-emitter: 2.1.0
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
vue3-icon: 3.0.3(vue@3.5.14(typescript@5.8.3))
|
||||
|
||||
vue@3.5.14(typescript@5.8.3):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.14
|
||||
|
||||
13
src/app.vue
13
src/app.vue
@ -1,16 +1,15 @@
|
||||
<template>
|
||||
<div class=" ">
|
||||
<BaseDialog />
|
||||
<!-- <NuxtLayout>
|
||||
<div>
|
||||
<NuxtLayout :data-theme="currentTheme.value">
|
||||
<NuxtPage />
|
||||
<Toaster />
|
||||
</NuxtLayout> -->
|
||||
<NuxtSnackbar />
|
||||
</NuxtLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
//import 'vue-sonner/style.css'
|
||||
//const { currentTheme } = storeToRefs(useUiStore())
|
||||
|
||||
const { currentTheme } = storeToRefs(useUiStore())
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@ -1,228 +1,9 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
@import 'tailwindcss';
|
||||
@import 'flyonui/variants.css';
|
||||
|
||||
@plugin "@iconify/tailwind4";
|
||||
|
||||
@theme inline {
|
||||
|
||||
--color-background: var(--background);
|
||||
|
||||
--color-foreground: var(--foreground);
|
||||
|
||||
--color-card: var(--card);
|
||||
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
|
||||
--color-popover: var(--popover);
|
||||
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
|
||||
--color-primary: var(--primary);
|
||||
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
|
||||
--color-secondary: var(--secondary);
|
||||
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
|
||||
--color-muted: var(--muted);
|
||||
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
|
||||
--color-accent: var(--accent);
|
||||
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
|
||||
--color-destructive: var(--destructive);
|
||||
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
|
||||
--color-border: var(--border);
|
||||
|
||||
--color-input: var(--input);
|
||||
|
||||
--color-ring: var(--ring);
|
||||
|
||||
--color-chart-1: var(--chart-1);
|
||||
|
||||
--color-chart-2: var(--chart-2);
|
||||
|
||||
--color-chart-3: var(--chart-3);
|
||||
|
||||
--color-chart-4: var(--chart-4);
|
||||
|
||||
--color-chart-5: var(--chart-5);
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
|
||||
--radius-lg: var(--radius);
|
||||
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
|
||||
--color-sidebar: var(--sidebar);
|
||||
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
}
|
||||
|
||||
:root {
|
||||
|
||||
--background: oklch(1 0 0);
|
||||
|
||||
--foreground: oklch(0.129 0.042 264.695);
|
||||
|
||||
--card: oklch(1 0 0);
|
||||
|
||||
--card-foreground: oklch(0.129 0.042 264.695);
|
||||
|
||||
--popover: oklch(1 0 0);
|
||||
|
||||
--popover-foreground: oklch(0.129 0.042 264.695);
|
||||
|
||||
--primary: oklch(0.208 0.042 265.755);
|
||||
|
||||
--primary-foreground: oklch(0.984 0.003 247.858);
|
||||
|
||||
--secondary: oklch(0.968 0.007 247.896);
|
||||
|
||||
--secondary-foreground: oklch(0.208 0.042 265.755);
|
||||
|
||||
--muted: oklch(0.968 0.007 247.896);
|
||||
|
||||
--muted-foreground: oklch(0.554 0.046 257.417);
|
||||
|
||||
--accent: oklch(0.968 0.007 247.896);
|
||||
|
||||
--accent-foreground: oklch(0.208 0.042 265.755);
|
||||
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
|
||||
--destructive-foreground: oklch(0.577 0.245 27.325);
|
||||
|
||||
--border: oklch(0.929 0.013 255.508);
|
||||
|
||||
--input: oklch(0.929 0.013 255.508);
|
||||
|
||||
--ring: oklch(0.704 0.04 256.788);
|
||||
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
|
||||
--radius: 0.625rem;
|
||||
|
||||
--sidebar: oklch(0.984 0.003 247.858);
|
||||
|
||||
--sidebar-foreground: oklch(0.129 0.042 264.695);
|
||||
|
||||
--sidebar-primary: oklch(0.208 0.042 265.755);
|
||||
|
||||
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
|
||||
|
||||
--sidebar-accent: oklch(0.968 0.007 247.896);
|
||||
|
||||
--sidebar-accent-foreground: oklch(0.208 0.042 265.755);
|
||||
|
||||
--sidebar-border: oklch(0.929 0.013 255.508);
|
||||
|
||||
--sidebar-ring: oklch(0.704 0.04 256.788);
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
--background: oklch(0.129 0.042 264.695);
|
||||
|
||||
--foreground: oklch(0.984 0.003 247.858);
|
||||
|
||||
--card: oklch(0.129 0.042 264.695);
|
||||
|
||||
--card-foreground: oklch(0.984 0.003 247.858);
|
||||
|
||||
--popover: oklch(0.129 0.042 264.695);
|
||||
|
||||
--popover-foreground: oklch(0.984 0.003 247.858);
|
||||
|
||||
--primary: oklch(0.984 0.003 247.858);
|
||||
|
||||
--primary-foreground: oklch(0.208 0.042 265.755);
|
||||
|
||||
--secondary: oklch(0.279 0.041 260.031);
|
||||
|
||||
--secondary-foreground: oklch(0.984 0.003 247.858);
|
||||
|
||||
--muted: oklch(0.279 0.041 260.031);
|
||||
|
||||
--muted-foreground: oklch(0.704 0.04 256.788);
|
||||
|
||||
--accent: oklch(0.279 0.041 260.031);
|
||||
|
||||
--accent-foreground: oklch(0.984 0.003 247.858);
|
||||
|
||||
--destructive: oklch(0.396 0.141 25.723);
|
||||
|
||||
--destructive-foreground: oklch(0.637 0.237 25.331);
|
||||
|
||||
--border: oklch(0.279 0.041 260.031);
|
||||
|
||||
--input: oklch(0.279 0.041 260.031);
|
||||
|
||||
--ring: oklch(0.446 0.043 257.281);
|
||||
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
|
||||
--sidebar: oklch(0.208 0.042 265.755);
|
||||
|
||||
--sidebar-foreground: oklch(0.984 0.003 247.858);
|
||||
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
|
||||
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
|
||||
|
||||
--sidebar-accent: oklch(0.279 0.041 260.031);
|
||||
|
||||
--sidebar-accent-foreground: oklch(0.984 0.003 247.858);
|
||||
|
||||
--sidebar-border: oklch(0.279 0.041 260.031);
|
||||
|
||||
--sidebar-ring: oklch(0.446 0.043 257.281);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
@plugin "flyonui" {
|
||||
themes: all;
|
||||
}
|
||||
|
||||
@source "../../node_modules/flyonui/flyonui.js";
|
||||
|
||||
14
src/components/haex/button/index.vue
Normal file
14
src/components/haex/button/index.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<button class="btn join-item" :type>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
type: {
|
||||
type: String as PropType<"reset" | "submit" | "button">,
|
||||
default: "button",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
15
src/components/haex/button/outline.vue
Normal file
15
src/components/haex/button/outline.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<button :class="cn(
|
||||
`relative flex items-center justify-center min-w-28 min-h-10 overflow-hidden outline-2 outline-offset-2 rounded cursor-pointer`, className
|
||||
)
|
||||
">
|
||||
<span class="btn-content inline-flex size-full items-center justify-center px-4 py-2 gap-2">
|
||||
<slot />
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils";
|
||||
const { className = "primary" } = defineProps<{ className?: string }>()
|
||||
</script>
|
||||
102
src/components/haex/input/index.vue
Normal file
102
src/components/haex/input/index.vue
Normal file
@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div>
|
||||
<fieldset class="join w-full">
|
||||
<slot name="prepend" />
|
||||
|
||||
|
||||
<!-- <div class="">
|
||||
-->
|
||||
<HaexButton v-if="withCopyButton" class="btn-outline btn-accent btn-square join-item h-auto"
|
||||
@click="copy(`${input}`)">
|
||||
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
||||
</Haexbutton>
|
||||
|
||||
<!-- <div class="">
|
||||
<input :id :name="name ?? id" :placeholder="placeholder || label" :type :autofocus class="" v-bind="$attrs"
|
||||
v-model="input" ref="inputRef" :readonly="read_only" />
|
||||
<label class="floating-label" :for="id">{{ label }}</label>
|
||||
</div> -->
|
||||
<label class="floating-label input join-item">
|
||||
<Icon v-if="iconPrepend" :name="iconPrepend" class="my-auto size-6" />
|
||||
<span>Your Email</span>
|
||||
<input type="text" placeholder="mail@site.com" class=" join-item " />
|
||||
<Icon v-if="iconAppend" :name="iconAppend" class="my-auto shrink-0" />
|
||||
</label>
|
||||
|
||||
<!-- <Icon v-if="iconAppend" :name="iconAppend" class="my-auto shrink-0" />
|
||||
</div> -->
|
||||
|
||||
<slot name="append" class="h-auto" />
|
||||
|
||||
<HaexButton v-if="withCopyButton" class="btn-outline btn-accent btn-square join-item h-auto"
|
||||
@click="copy(`${input}`)">
|
||||
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
||||
</Haexbutton>
|
||||
</fieldset>
|
||||
|
||||
<span class="flex flex-col px-2 pt-0.5" v-show="errors">
|
||||
<span v-for="error in errors" class="label-text-alt text-error">
|
||||
{{ error }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<!-- <div class="relative w-full max-w-sm items-center">
|
||||
<span class="absolute start-0 inset-y-0 flex items-center justify-center px-2">
|
||||
<Icon v-if="iconPrepend" :name="iconPrepend" class="size-6" />
|
||||
<button>aa</button>
|
||||
</span>
|
||||
|
||||
<Input id="search" type="text" placeholder="Search..." :class="{ 'pl-10': iconPrepend, 'pr-10': iconAppend }" />
|
||||
|
||||
<span class="absolute end-0 inset-y-0 flex items-center justify-center px-2">
|
||||
<Icon v-if="iconAppend" :name="iconAppend" class="size-6" />
|
||||
</span>
|
||||
</div> -->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { HaexButton } from '#components';
|
||||
import type { ZodSchema } from 'zod';
|
||||
|
||||
const id = useId()
|
||||
|
||||
const props = defineProps<{ iconAppend?: string, iconPrepend?: string, placeholder?: string, type?: string, label?: string, withCopyButton?: boolean, rules?: ZodSchema, read_only?: boolean, autofocus?: boolean, checkInput?: boolean, name?: string }>()
|
||||
|
||||
const inputRef = useTemplateRef('inputRef')
|
||||
|
||||
const input = defineModel<string | number | undefined | null>({
|
||||
default: '',
|
||||
required: true,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus && inputRef.value) inputRef.value.focus()
|
||||
})
|
||||
|
||||
watch(input, () => checkInput())
|
||||
|
||||
watch(
|
||||
() => props.checkInput,
|
||||
() => {
|
||||
checkInput()
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits(['error'])
|
||||
|
||||
const errors = defineModel<string[] | undefined>('errors')
|
||||
const checkInput = () => {
|
||||
if (props.rules) {
|
||||
const result = props.rules.safeParse(input.value)
|
||||
//console.log('check result', result.error, props.rules);
|
||||
if (!result.success) {
|
||||
errors.value = result.error.errors.map((error) => error.message)
|
||||
emit('error', errors.value)
|
||||
} else {
|
||||
errors.value = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { copy, copied } = useClipboard()
|
||||
</script>
|
||||
@ -1,41 +1,36 @@
|
||||
<template>
|
||||
<UiDropdown
|
||||
activator-class="btn btn-text btn-circle "
|
||||
dropdown-class="[--offset:20]"
|
||||
>
|
||||
<UiDropdown activator-class="btn btn-text btn-circle " dropdown-class="[--offset:20]">
|
||||
<template #activator>
|
||||
<div
|
||||
class="size-9.5 rounded-full items-center justify-center text-base-content text-base"
|
||||
>
|
||||
<div class="size-9.5 rounded-full items-center justify-center text-base-content text-base">
|
||||
<Icon name="mdi:format-list-bulleted" class="size-full p-2" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #items>
|
||||
<ul
|
||||
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-60"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="dropdown-avatar"
|
||||
>
|
||||
<li>
|
||||
<NuxtLinkLocale class="dropdown-item" :to="{ name: 'haexSettings' }">
|
||||
<span class="icon-[tabler--settings]"></span>
|
||||
{{ t('settings') }}
|
||||
</NuxtLinkLocale>
|
||||
</li>
|
||||
|
||||
<li class="dropdown-footer gap-2">
|
||||
<button
|
||||
class="btn btn-error btn-soft btn-block"
|
||||
@click="onVaultCloseAsync"
|
||||
>
|
||||
<span class="icon-[tabler--logout]"></span>
|
||||
{{ t('vault.close') }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- <ul class="dropdown-menu dropdown-open:opacity-100 hidden min-w-60" role="menu" aria-orientation="vertical"
|
||||
aria-labelledby="dropdown-avatar"> -->
|
||||
|
||||
<template #items>
|
||||
|
||||
<li>
|
||||
<NuxtLinkLocale class="dropdown-item" :to="{ name: 'haexSettings' }">
|
||||
<span class="icon-[tabler--settings]"></span>
|
||||
{{ t('settings') }}
|
||||
</NuxtLinkLocale>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li class="dropdown-footer gap-2">
|
||||
<button class="btn btn-error btn-soft btn-block" @click="onVaultCloseAsync">
|
||||
<span class="icon-[tabler--logout]"></span>
|
||||
{{ t('vault.close') }}
|
||||
</button>
|
||||
</li>
|
||||
</template>
|
||||
<!--
|
||||
</ul> -->
|
||||
|
||||
</UiDropdown>
|
||||
</template>
|
||||
|
||||
|
||||
14
src/components/ui/button/index.vue
Normal file
14
src/components/ui/button/index.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<button class="btn join-item" :type>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
type: {
|
||||
type: String as PropType<"reset" | "submit" | "button">,
|
||||
default: "button",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
97
src/components/ui/dialog/index.vue
Normal file
97
src/components/ui/dialog/index.vue
Normal file
@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<button v-bind="$attrs" type="button" aria-haspopup="dialog" aria-expanded="false" :aria-label="label"
|
||||
class="--prevent-on-load-init " @click="$emit('open')">
|
||||
<slot name="trigger">open</slot>
|
||||
</button>
|
||||
|
||||
<div :id class="overlay modal overlay-open:opacity-100 hidden overlay-open:duration-300" role="dialog" ref="modalRef"
|
||||
tabindex="-1">
|
||||
<div
|
||||
class="overlay-animation-target overlay-open:mt-4 overlay-open:duration-500 mt-12 transition-all ease-out modal-dialog overlay-open:opacity-100">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<slot name="title">
|
||||
<h3 v-if="title" class="modal-title text-base sm:text-lg">
|
||||
{{ title }}
|
||||
</h3>
|
||||
</slot>
|
||||
|
||||
<button type="button" class="btn btn-text btn-circle btn-sm absolute end-3 top-3" :aria-label="t('close')"
|
||||
tabindex="1" @click="open = false">
|
||||
<Icon name="mdi:close" size="18" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-sm sm:text-base py-1">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="modal-footer flex-wrap">
|
||||
<slot name="buttons" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HSOverlay } from "flyonui/flyonui";
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
defineProps<{ title?: string; label?: string }>();
|
||||
|
||||
defineEmits(["open", "close"]);
|
||||
|
||||
const id = useId();
|
||||
|
||||
const open = defineModel<boolean>("open", { default: false });
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const modalRef = useTemplateRef("modalRef");
|
||||
|
||||
defineExpose({ modalRef });
|
||||
|
||||
const modal = ref<HSOverlay>();
|
||||
|
||||
watch(open, async () => {
|
||||
console.log("watch open modal", open.value, modal.value);
|
||||
if (open.value) {
|
||||
await modal.value?.open();
|
||||
} else {
|
||||
await modal.value?.close(true);
|
||||
//HSOverlay.close(`#${id}`);
|
||||
//console.log("close dialog");
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
if (!modalRef.value) return;
|
||||
// flyonui has a problem importing HSOverlay at component level due to ssr
|
||||
// that's the workaround I found
|
||||
//const flyonui = await import("flyonui/flyonui");
|
||||
|
||||
modal.value = new window.HSOverlay(modalRef.value, {
|
||||
isClosePrev: true,
|
||||
});
|
||||
|
||||
modal.value.on("close", () => {
|
||||
console.log("on close", open.value);
|
||||
open.value = false;
|
||||
});
|
||||
/* modal.value.on("open", () => {
|
||||
console.log("on open", open.value);
|
||||
open.value = true;
|
||||
}); */
|
||||
});
|
||||
</script>
|
||||
|
||||
<i18n lang="json">{
|
||||
"de": {
|
||||
"close": "Schließen"
|
||||
},
|
||||
"en": {
|
||||
"close": "Close"
|
||||
}
|
||||
}</i18n>
|
||||
44
src/components/ui/dropdown/index.vue
Normal file
44
src/components/ui/dropdown/index.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="dropdown relative inline-flex">
|
||||
<button :id class="dropdown-toggle " v-bind="$attrs" aria-haspopup="menu" aria-expanded="false" :aria-label="label">
|
||||
<slot name="activator">
|
||||
{{ label }}
|
||||
<span class="icon-[tabler--chevron-down] dropdown-open:rotate-180 size-4">
|
||||
</span>
|
||||
</slot>
|
||||
</button>
|
||||
|
||||
<ul class="dropdown-menu dropdown-open:opacity-100 hidden min-w-28" role="menu" aria-orientation="vertical"
|
||||
:aria-labelledby="id">
|
||||
|
||||
<slot name="items">
|
||||
|
||||
|
||||
<li v-for="item in items" :is="itemIs" class="dropdown-item" @click="$emit('select', item)">
|
||||
<slot name="item" :item>
|
||||
{{ item }}
|
||||
</slot>
|
||||
</li>
|
||||
</slot>
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" generic="T">
|
||||
const { itemIs = "li" } = defineProps<{
|
||||
label?: string;
|
||||
items?: T[];
|
||||
itemIs?: string;
|
||||
activatorClass?: string;
|
||||
}>();
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
defineEmits<{ select: [T] }>();
|
||||
|
||||
const id = useId();
|
||||
</script>
|
||||
33
src/components/ui/dropdown/locale.vue
Normal file
33
src/components/ui/dropdown/locale.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<UiDropdown :items="availableLocales" @select="(locale) => $emit('select', locale)"
|
||||
class="btn btn-primary btn-outline">
|
||||
<template #activator>
|
||||
|
||||
<Icon :name="flags[locale]" />
|
||||
<Icon name="tabler:chevron-down" class="dropdown-open:rotate-180 size-4" />
|
||||
|
||||
</template>
|
||||
|
||||
<template #item="{ item }">
|
||||
<div class="flex gap-2 justify-center">
|
||||
<Icon :name="flags[item]" class="my-auto" />
|
||||
<p>
|
||||
{{ item }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</UiDropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type Locale } from 'vue-i18n'
|
||||
|
||||
const flags = {
|
||||
de: 'emojione:flag-for-germany',
|
||||
en: 'emojione:flag-for-united-kingdom',
|
||||
}
|
||||
|
||||
const { availableLocales, locale } = useI18n()
|
||||
|
||||
defineEmits<{ select: [Locale] }>()
|
||||
</script>
|
||||
22
src/components/ui/dropdown/theme.vue
Normal file
22
src/components/ui/dropdown/theme.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<UiDropdown :items="availableThemes" @select="(theme) => $emit('select', theme)" class="btn btn-primary btn-outline">
|
||||
<template #activator>
|
||||
<Icon :name="currentTheme.icon" />
|
||||
</template>
|
||||
|
||||
<template #item="{ item }">
|
||||
<div class="flex gap-2 justify-center">
|
||||
<Icon :name="item.icon" class="my-auto" />
|
||||
<p>
|
||||
{{ item.name }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</UiDropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { availableThemes, currentTheme } = storeToRefs(useUiStore())
|
||||
|
||||
defineEmits<{ select: [ITheme] }>()
|
||||
</script>
|
||||
133
src/components/ui/input/index.vue
Normal file
133
src/components/ui/input/index.vue
Normal file
@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div>
|
||||
<fieldset class="join w-full pt-0.5">
|
||||
<slot name="prepend" />
|
||||
|
||||
<div class="input join-item">
|
||||
<Icon v-if="prependIcon" :name="prependIcon" class="my-auto shrink-0" />
|
||||
|
||||
<div class="input-floating grow">
|
||||
<input :id :name="name ?? id" :placeholder="placeholder || label" :type :autofocus class="ps-3"
|
||||
v-bind="$attrs" v-model="input" ref="inputRef" :readonly="read_only" />
|
||||
<label class="input-floating-label" :for="id">{{ label }}</label>
|
||||
</div>
|
||||
|
||||
<Icon v-if="appendIcon" :name="appendIcon" class="my-auto shrink-0" />
|
||||
</div>
|
||||
|
||||
<slot name="append" class="h-auto" />
|
||||
|
||||
<UiButton v-if="withCopyButton" class="btn-outline btn-accent btn-square join-item h-auto"
|
||||
@click="copy(`${input}`)">
|
||||
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
||||
</UiButton>
|
||||
</fieldset>
|
||||
|
||||
<span class="flex flex-col px-2 pt-0.5" v-show="errors">
|
||||
<span v-for="error in errors" class="label-text-alt text-error">
|
||||
{{ error }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type ZodSchema } from "zod";
|
||||
|
||||
const inputRef = useTemplateRef("inputRef");
|
||||
defineExpose({ inputRef });
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<
|
||||
| "button"
|
||||
| "checkbox"
|
||||
| "color"
|
||||
| "date"
|
||||
| "datetime-local"
|
||||
| "email"
|
||||
| "file"
|
||||
| "hidden"
|
||||
| "image"
|
||||
| "month"
|
||||
| "number"
|
||||
| "password"
|
||||
| "radio"
|
||||
| "range"
|
||||
| "reset"
|
||||
| "search"
|
||||
| "submit"
|
||||
| "tel"
|
||||
| "text"
|
||||
| "time"
|
||||
| "url"
|
||||
| "week"
|
||||
>,
|
||||
default: "text",
|
||||
},
|
||||
label: String,
|
||||
name: String,
|
||||
prependIcon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
prependLabel: String,
|
||||
appendIcon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
appendLabel: String,
|
||||
rules: Object as PropType<ZodSchema>,
|
||||
checkInput: Boolean,
|
||||
withCopyButton: Boolean,
|
||||
autofocus: Boolean,
|
||||
read_only: Boolean,
|
||||
});
|
||||
|
||||
const input = defineModel<string | number | undefined | null>({
|
||||
default: "",
|
||||
required: true,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus && inputRef.value) inputRef.value.focus();
|
||||
});
|
||||
|
||||
const errors = defineModel<string[] | undefined>("errors");
|
||||
|
||||
const id = useId();
|
||||
|
||||
watch(input, () => checkInput());
|
||||
|
||||
watch(
|
||||
() => props.checkInput,
|
||||
() => {
|
||||
checkInput();
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits(["error"]);
|
||||
|
||||
const checkInput = () => {
|
||||
if (props.rules) {
|
||||
const result = props.rules.safeParse(input.value);
|
||||
//console.log('check result', result.error, props.rules);
|
||||
if (!result.success) {
|
||||
errors.value = result.error.errors.map((error) => error.message);
|
||||
emit("error", errors.value);
|
||||
} else {
|
||||
errors.value = [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const { copy, copied } = useClipboard();
|
||||
</script>
|
||||
42
src/components/ui/input/password.vue
Normal file
42
src/components/ui/input/password.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<UiInput :check-input :label="label || t('password')" :placeholder="placeholder || t('password')" :rules :type="type"
|
||||
:autofocus v-model="value">
|
||||
<template #append>
|
||||
<UiButton class="btn-outline btn-accent btn-square h-auto" @click="tooglePasswordType">
|
||||
<Icon :name="type === 'password' ? 'mdi:eye-off' : 'mdi:eye'" />
|
||||
</UiButton>
|
||||
</template>
|
||||
</UiInput>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ZodSchema } from "zod";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const value = defineModel<string | number | null | undefined>();
|
||||
|
||||
defineProps({
|
||||
label: String,
|
||||
placeholder: String,
|
||||
checkInput: Boolean,
|
||||
rules: Object as PropType<ZodSchema>,
|
||||
autofocus: Boolean,
|
||||
});
|
||||
|
||||
const type = ref<"password" | "text">("password");
|
||||
|
||||
const tooglePasswordType = () => {
|
||||
type.value = type.value === "password" ? "text" : "password";
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<i18n lang="json">{
|
||||
"de": {
|
||||
"password": "Passwort"
|
||||
},
|
||||
"en": {
|
||||
"password": "Password"
|
||||
}
|
||||
}</i18n>
|
||||
53
src/components/ui/logo/itemis.vue
Normal file
53
src/components/ui/logo/itemis.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<svg id="logo" class="fill-current stroke-current w-[160px]" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 286.3 85" xml:space="preserve">
|
||||
<switch>
|
||||
<g>
|
||||
<g class="logo-imagesss">
|
||||
<circle fill="white" cx="42.5" cy="42.5" r="40"></circle>
|
||||
<path d="M42.3,83.4c-22.6,0-40.9-18.4-40.9-40.9c0-22.6,18.4-40.9,40.9-40.9c22.6,0,40.9,18.4,40.9,40.9
|
||||
C83.3,65.1,64.9,83.4,42.3,83.4z M42.3,5.8C22.1,5.8,5.7,22.3,5.7,42.5s16.5,36.7,36.7,36.7S79,62.7,79,42.5S62.6,5.8,42.3,5.8z
|
||||
"></path>
|
||||
<g>
|
||||
<g>
|
||||
<polygon points="38.8,69.8 38.8,31.7 22.3,31.7 22.3,38.5 29.8,38.5 29.8,69.8 "></polygon>
|
||||
<path d="M34.1,13.2c-3.3,0-6,2.6-6,5.9c0,3.3,2.6,6,5.9,6c3.3,0,6-2.6,6-6
|
||||
C39.9,15.9,37.3,13.2,34.1,13.2z"></path>
|
||||
</g>
|
||||
<g>
|
||||
<polygon points="45.9,69.8 45.9,31.7 62.4,31.7 62.4,38.5 54.9,38.5 54.9,69.8 "></polygon>
|
||||
<path d="M50.6,13.2c3.3,0,6,2.6,6,5.9c0,3.3-2.6,6-5.9,6c-3.3,0-6-2.6-6-6
|
||||
C44.8,15.9,47.4,13.2,50.6,13.2z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g class="logo-textsss">
|
||||
<path
|
||||
d="M136.1,63.6c-4,0-5.3-2.6-5.3-6V38.5h10.6v-6.7h-10.6v-6.7h-9c0,7,0,29.1,0,32.7
|
||||
c0,4.2,1.6,7.5,3.8,9.7c2.3,2.2,5.6,3.3,9.8,3.3c5.1,0,8.4-1.8,10.6-4.2l-4.7-6C140.2,62.1,138.5,63.6,136.1,63.6z">
|
||||
</path>
|
||||
<path d="M217.7,30.7c-4.9,0-8.2,1.6-10.4,3.8c-2.2-2.2-5.5-3.8-10.4-3.8c-15,0-14.9,12.1-14.9,15
|
||||
s0,24.1,0,24.1h9V45.7c0-8.5,4.9-8.3,5.9-8.3c1,0,5.9-0.3,5.9,8.3v24.1h0h9h0V45.7c0-8.5,4.9-8.3,5.9-8.3c1,0,5.9-0.3,5.9,8.3
|
||||
v24.1h9c0,0,0-21.2,0-24.1C232.6,42.8,232.7,30.7,217.7,30.7z"></path>
|
||||
<path d="M273.2,46.4c-4.3-1.4-6-2.5-6-5.2c0-2,1.1-3.8,4.3-3.8c3.2,0,4.5,3.3,5.1,4.8
|
||||
c2.7-1.5,5.3-2.9,6.6-3.6c-2.5-6-6.3-7.9-12-7.9c-8,0-11.7,5.5-11.7,10.6c0,6.5,2.9,9.8,11.2,12.2c6,1.8,6.5,4.7,6.2,6.2
|
||||
c-0.3,1.7-1.6,3.6-5.3,3.6c-3.6,0-5.8-3.8-6.8-5.4c-1.8,1.1-3.4,2.1-6.4,3.8c2.1,5,6.8,9.1,13.5,9.1c7.9,0,12.9-5.1,12.9-12.1
|
||||
C284.9,51,279.6,48.5,273.2,46.4z"></path>
|
||||
<g>
|
||||
<polygon points="239.7,69.8 239.7,31.7 256.2,31.7 256.2,38.5 248.7,38.5 248.7,69.8 "></polygon>
|
||||
<path d="M244.4,13.2c3.3,0,6,2.6,6,5.9c0,3.3-2.6,6-5.9,6c-3.3,0-6-2.6-6-6
|
||||
C238.6,15.9,241.2,13.2,244.4,13.2z"></path>
|
||||
</g>
|
||||
<g>
|
||||
<polygon points="114.7,69.8 114.7,31.7 98.1,31.7 98.1,38.5 105.7,38.5 105.7,69.8 "></polygon>
|
||||
<path d="M110,13.2c-3.3,0-6,2.6-6,5.9c0,3.3,2.6,6,5.9,6c3.3,0,6-2.6,6-6C115.8,15.9,113.2,13.2,110,13.2
|
||||
z"></path>
|
||||
</g>
|
||||
<path d="M176.4,52.4v-3.7c0-12.3-4.7-18-14.8-18c-9.3,0-14.7,6.6-14.7,18v4c0,11.5,5.8,18.2,15.8,18.2
|
||||
c6.6,0,10.8-3.7,12.7-7.9c-2.2-1.4-4.6-2.8-6.1-3.8c-1,1.7-2.9,4.4-6.7,4.4c-5.8,0-7-5.9-7-10.9v-0.2H176.4z M155.7,45.7
|
||||
c0.2-7.1,3.3-8.2,6-8.2c2.6,0,5.9,1,6,8.2H155.7z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</switch>
|
||||
</svg>
|
||||
</template>
|
||||
5
src/components/ui/text/gradient.vue
Normal file
5
src/components/ui/text/gradient.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<p class="bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent font-black">
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
52
src/components/ui/tooltip/index.vue
Normal file
52
src/components/ui/tooltip/index.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="tooltip [--prevent-popper:false]">
|
||||
<div class="tooltip-toggle" aria-label="Tooltip">
|
||||
<slot>
|
||||
<button class="btn btn-square">
|
||||
<Icon name="mdi:chevron-up-box-outline" />
|
||||
</button>
|
||||
</slot>
|
||||
|
||||
<span class="tooltip-content tooltip-shown:opacity-100 tooltip-shown:visible z-40" role="tooltip">
|
||||
<span class="tooltip-body" v-bind="$attrs">
|
||||
{{ tooltip }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
direction: {
|
||||
type: String as PropType<
|
||||
| 'top'
|
||||
| 'top-start'
|
||||
| 'top-end'
|
||||
| 'bottom'
|
||||
| 'bottom-start'
|
||||
| 'bottom-end'
|
||||
| 'right'
|
||||
| 'right-start'
|
||||
| 'right-end'
|
||||
| 'left'
|
||||
| 'left-start'
|
||||
| 'left-end'
|
||||
>,
|
||||
default: 'top',
|
||||
},
|
||||
|
||||
tooltip: String,
|
||||
|
||||
trigger: {
|
||||
type: String as PropType<'focus' | 'hover' | 'click'>,
|
||||
default: 'hover',
|
||||
},
|
||||
});
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
</script>
|
||||
@ -1,41 +1,32 @@
|
||||
<template>
|
||||
<Dialog>
|
||||
<!-- class="btn btn-primary btn-outline shadow-md md:btn-lg shrink-0 flex-1 whitespace-nowrap flex-nowrap" -->
|
||||
<DialogTrigger as-child>
|
||||
<Button>
|
||||
<UiDialog :title="t('title')" v-model:open="open"
|
||||
class="btn btn-primary btn-outline shadow-md md:btn-lg shrink-0 flex-1 whitespace-nowrap flex-nowrap">
|
||||
<template #trigger>
|
||||
|
||||
<Icon name="mdi:plus" />
|
||||
{{ t('database.create') }}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit profile</DialogTitle>
|
||||
<DialogDescription>
|
||||
Make changes to your profile here. Click save when you're done.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Icon name="mdi:plus" />
|
||||
{{ t('database.create') }}
|
||||
|
||||
<form class="flex flex-col gap-4" @submit="onCreateAsync">
|
||||
<Input :check-input="check" :label="t('database.label')" :placeholder="t('database.placeholder')"
|
||||
:rules="vaultDatabaseSchema.name" autofocus prepend-icon="mdi:safe" v-model="database.name" />
|
||||
</template>
|
||||
|
||||
<!-- <UiInputPassword :check-input="check" :rules="vaultDatabaseSchema.password" prepend-icon="mdi:key-outline"
|
||||
v-model="database.password" /> -->
|
||||
</form>
|
||||
<form class="flex flex-col gap-4" @submit="onCreateAsync">
|
||||
<UiInput :check-input="check" :label="t('database.label')" :placeholder="t('database.placeholder')"
|
||||
:rules="vaultDatabaseSchema.name" autofocus prepend-icon="mdi:safe" v-model="database.name" />
|
||||
|
||||
<DialogFooter>
|
||||
<Button class="btn-error" @click="onClose">
|
||||
{{ t('abort') }}
|
||||
</Button>
|
||||
<UiInputPassword :check-input="check" :rules="vaultDatabaseSchema.password" prepend-icon="mdi:key-outline"
|
||||
v-model="database.password" />
|
||||
</form>
|
||||
|
||||
<Button class="btn-primary" @click="onCreateAsync">
|
||||
{{ t('create') }}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<template #buttons>
|
||||
<UiButton class="btn-error" @click="onClose">
|
||||
{{ t('abort') }}
|
||||
</UiButton>
|
||||
|
||||
<UiButton class="btn-primary" @click="onCreateAsync">
|
||||
{{ t('create') }}
|
||||
</UiButton>
|
||||
</template>
|
||||
</UiDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -43,7 +34,6 @@ import { save } from '@tauri-apps/plugin-dialog'
|
||||
import { onKeyStroke } from '@vueuse/core'
|
||||
import { useVaultStore } from '~/stores/vault'
|
||||
import { vaultDatabaseSchema } from './schema'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
onKeyStroke('Enter', (e) => {
|
||||
e.preventDefault()
|
||||
@ -76,7 +66,7 @@ const initDatabase = () => {
|
||||
|
||||
initDatabase()
|
||||
|
||||
|
||||
const { add } = useSnackbar()
|
||||
const { createAsync } = useVaultStore()
|
||||
|
||||
const onCreateAsync = async () => {
|
||||
@ -121,7 +111,7 @@ const onCreateAsync = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast({ type: 'error', text: JSON.stringify(error) })
|
||||
add({ type: 'error', text: JSON.stringify(error) })
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,4 +146,4 @@ const onClose = () => {
|
||||
"abort": "Abort",
|
||||
"description": "Haex Vault for your most secret secrets"
|
||||
}
|
||||
}</i18n>
|
||||
}</i18n>
|
||||
@ -1,29 +1,16 @@
|
||||
<template>
|
||||
<UiDialog
|
||||
class="btn btn-primary btn-outline shadow-md md:btn-lg shrink-0 flex-1"
|
||||
v-model:open="isOpen"
|
||||
@click="onLoadDatabase"
|
||||
>
|
||||
<!-- @close="initDatabase" -->
|
||||
<UiDialog v-model:open="isOpen" class="btn btn-primary btn-outline shadow-md md:btn-lg shrink-0 flex-1 "
|
||||
@open="onLoadDatabase">
|
||||
|
||||
<template #trigger>
|
||||
<!-- <button
|
||||
class="btn btn-primary btn-outline shadow-md md:btn-lg shrink-0 flex-1"
|
||||
@click="onLoadDatabase"
|
||||
> -->
|
||||
|
||||
<Icon name="mdi:folder-open-outline" />
|
||||
{{ t('database.open') }}
|
||||
<!-- </button> -->
|
||||
|
||||
</template>
|
||||
|
||||
<UiInputPassword
|
||||
:check-input="check"
|
||||
:rules="vaultDatabaseSchema.password"
|
||||
@keyup.enter="onOpenDatabase"
|
||||
autofocus
|
||||
prepend-icon="mdi:key-outline"
|
||||
v-model="database.password"
|
||||
/>
|
||||
<UiInputPassword :check-input="check" :rules="vaultDatabaseSchema.password" @keyup.enter="onOpenDatabase" autofocus
|
||||
prepend-icon="mdi:key-outline" v-model="database.password" />
|
||||
|
||||
<template #buttons>
|
||||
<UiButton class="btn-error" @click="onClose">
|
||||
@ -95,6 +82,7 @@ const onLoadDatabase = async () => {
|
||||
],
|
||||
})
|
||||
|
||||
console.log("database.path", database.path)
|
||||
if (!database.path) return
|
||||
|
||||
isOpen.value = true
|
||||
@ -162,8 +150,7 @@ const onClose = () => {
|
||||
}
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
{
|
||||
<i18n lang="json">{
|
||||
"de": {
|
||||
"open": "Öffnen",
|
||||
"abort": "Abbrechen",
|
||||
@ -171,7 +158,6 @@ const onClose = () => {
|
||||
"open": "Vault öffnen"
|
||||
}
|
||||
},
|
||||
|
||||
"en": {
|
||||
"open": "Open",
|
||||
"abort": "Abort",
|
||||
@ -179,5 +165,4 @@ const onClose = () => {
|
||||
"open": "Open Vault"
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
}</i18n>
|
||||
16
src/i18n/i18n.config.ts
Normal file
16
src/i18n/i18n.config.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/* import de from '@/stores/sidebar/de.json';
|
||||
import en from '@/stores/sidebar/en.json'; */
|
||||
|
||||
export default defineI18nConfig(() => {
|
||||
return {
|
||||
legacy: false,
|
||||
messages: {
|
||||
de: {
|
||||
//sidebar: de,
|
||||
},
|
||||
en: {
|
||||
//sidebar: en,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<div class="items-center justify-center min-h-full flex w-full relative">
|
||||
<div class="fixed top-2 right-2">
|
||||
<!-- <UiDropdownLocale @select="setLocale" /> -->
|
||||
<ThemeSwitcher />
|
||||
|
||||
<UiDropdownLocale @select="setLocale" />
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-center gap-5 max-w-3xl">
|
||||
<img src="/logo.svg" class="bg-primary p-3 size-16 rounded-full" alt="HaexVault Logo" />
|
||||
@ -12,22 +10,13 @@
|
||||
<p class="whitespace-nowrap">
|
||||
{{ t('welcome') }}
|
||||
</p>
|
||||
<!-- <UiTextGradient>Haex Hub</UiTextGradient> -->
|
||||
<UiTextGradient>Haex Hub</UiTextGradient>
|
||||
</span>
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-4 w-full h-24 md:h-auto">
|
||||
<VaultButtonCreate />
|
||||
<TestDialog />
|
||||
<BaseDialog />
|
||||
<!--
|
||||
|
||||
<VaultButtonOpen
|
||||
v-model:isOpen="passwordPromptOpen"
|
||||
:path="vaultPath"
|
||||
/> -->
|
||||
|
||||
|
||||
|
||||
<VaultButtonOpen v-model:isOpen="passwordPromptOpen" :path="vaultPath" />
|
||||
</div>
|
||||
|
||||
<div v-show="lastVaults.length" class="w-full">
|
||||
@ -60,7 +49,7 @@
|
||||
<h4>{{ t('sponsors') }}</h4>
|
||||
<div>
|
||||
<button @click="openUrl('https://itemis.com')">
|
||||
<!-- <UiLogoItemis class="text-[#00457C]" /> -->
|
||||
<UiLogoItemis class="text-[#00457C]" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -97,4 +86,4 @@ await syncLastVaultsAsync()
|
||||
"lastUsed": "Last used Vaults",
|
||||
"sponsors": "Powered by"
|
||||
}
|
||||
}</i18n>
|
||||
}</i18n>
|
||||
28
src/plugins/flyonui.client.ts
Normal file
28
src/plugins/flyonui.client.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import 'flyonui/flyonui'
|
||||
|
||||
import type { HSOverlay, IStaticMethods } from 'flyonui/flyonui'
|
||||
declare global {
|
||||
interface Window {
|
||||
HSStaticMethods: IStaticMethods
|
||||
HSOverlay: typeof HSOverlay
|
||||
}
|
||||
}
|
||||
|
||||
export default defineNuxtPlugin(() => {
|
||||
const router = useRouter()
|
||||
router.afterEach(async () => {
|
||||
setTimeout(() => {
|
||||
if (window.HSStaticMethods) {
|
||||
window.HSStaticMethods.autoInit()
|
||||
}
|
||||
}, 50)
|
||||
})
|
||||
|
||||
if (import.meta.client) {
|
||||
setTimeout(() => {
|
||||
if (window.HSStaticMethods) {
|
||||
window.HSStaticMethods.autoInit()
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
})
|
||||
6
src/stores/ui/de.json
Normal file
6
src/stores/ui/de.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"light": "Hell",
|
||||
"dark": "Dunkel",
|
||||
"soft": "Soft",
|
||||
"corporate": "Corporate"
|
||||
}
|
||||
6
src/stores/ui/en.json
Normal file
6
src/stores/ui/en.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"soft": "Soft",
|
||||
"corporate": "Corporate"
|
||||
}
|
||||
55
src/stores/ui/index.ts
Normal file
55
src/stores/ui/index.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
|
||||
import de from './de.json'
|
||||
import en from './en.json'
|
||||
|
||||
export interface ITheme {
|
||||
value: string
|
||||
name: string
|
||||
icon: string
|
||||
}
|
||||
|
||||
export const useUiStore = defineStore('uiStore', () => {
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||
|
||||
const currentScreenSize = computed(() =>
|
||||
breakpoints.active().value.length > 0 ? breakpoints.active().value : 'xs'
|
||||
)
|
||||
|
||||
const { t } = useI18n({
|
||||
messages: {
|
||||
de: { ui: de },
|
||||
en: { ui: en },
|
||||
},
|
||||
})
|
||||
|
||||
const availableThemes = ref([
|
||||
{
|
||||
value: 'dark',
|
||||
name: t('ui.dark'),
|
||||
icon: 'line-md:moon-rising-alt-loop',
|
||||
},
|
||||
{
|
||||
value: 'light',
|
||||
name: t('ui.light'),
|
||||
icon: 'line-md:moon-to-sunny-outline-loop-transition',
|
||||
},
|
||||
{ value: 'soft', name: t('ui.soft'), icon: 'line-md:paint-drop' },
|
||||
{
|
||||
value: 'corporate',
|
||||
name: t('ui.corporate'),
|
||||
icon: 'hugeicons:corporate',
|
||||
},
|
||||
])
|
||||
|
||||
const defaultTheme = ref(availableThemes.value[0])
|
||||
|
||||
const currentTheme = ref(defaultTheme)
|
||||
|
||||
return {
|
||||
availableThemes,
|
||||
breakpoints,
|
||||
currentScreenSize,
|
||||
currentTheme,
|
||||
defaultTheme,
|
||||
}
|
||||
})
|
||||
10
src/types/flyonui.d.ts
vendored
Normal file
10
src/types/flyonui.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
import type { IStaticMethods } from 'flyonui/flyonui'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
// FlyonUI
|
||||
HSStaticMethods: IStaticMethods
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
121
src/utils/helper.ts
Normal file
121
src/utils/helper.ts
Normal file
@ -0,0 +1,121 @@
|
||||
export const bytesToBase64DataUrlAsync = async (
|
||||
bytes: Uint8Array,
|
||||
type = 'application/octet-stream'
|
||||
) => {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const reader = Object.assign(new FileReader(), {
|
||||
onload: () => resolve(reader.result),
|
||||
onerror: () => reject(reader.error),
|
||||
})
|
||||
reader.readAsDataURL(new File([new Blob([bytes])], '', { type }))
|
||||
})
|
||||
}
|
||||
|
||||
export const blobToImageAsync = (blob: Blob): Promise<HTMLImageElement> => {
|
||||
return new Promise((resolve) => {
|
||||
console.log('transform blob', blob)
|
||||
const url = URL.createObjectURL(blob)
|
||||
let img = new Image()
|
||||
img.onload = () => {
|
||||
URL.revokeObjectURL(url)
|
||||
resolve(img)
|
||||
}
|
||||
img.src = url
|
||||
})
|
||||
}
|
||||
|
||||
export const deepToRaw = <T extends Record<string, any>>(sourceObj: T): T => {
|
||||
const objectIterator = (input: any): any => {
|
||||
if (Array.isArray(input)) {
|
||||
return input.map((item) => objectIterator(item))
|
||||
}
|
||||
if (isRef(input) || isReactive(input) || isProxy(input)) {
|
||||
return objectIterator(toRaw(input))
|
||||
}
|
||||
if (input && typeof input === 'object') {
|
||||
return Object.keys(input).reduce((acc, key) => {
|
||||
acc[key as keyof typeof acc] = objectIterator(input[key])
|
||||
return acc
|
||||
}, {} as T)
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
return objectIterator(sourceObj)
|
||||
}
|
||||
|
||||
export const readableFileSize = (sizeInByte: number | string = 0) => {
|
||||
if (!sizeInByte) {
|
||||
return '0 KB'
|
||||
}
|
||||
const size =
|
||||
typeof sizeInByte === 'string' ? parseInt(sizeInByte) : sizeInByte
|
||||
const sizeInKb = size / 1024
|
||||
const sizeInMb = sizeInKb / 1024
|
||||
const sizeInGb = sizeInMb / 1024
|
||||
const sizeInTb = sizeInGb / 1024
|
||||
|
||||
if (sizeInTb > 1) return `${sizeInTb.toFixed(2)} TB`
|
||||
if (sizeInGb > 1) return `${sizeInGb.toFixed(2)} GB`
|
||||
if (sizeInMb > 1) return `${sizeInMb.toFixed(2)} MB`
|
||||
|
||||
return `${sizeInKb.toFixed(2)} KB`
|
||||
}
|
||||
|
||||
import type { LocationQueryValue, RouteLocationRawI18n } from 'vue-router'
|
||||
|
||||
export const getSingleRouteParam = (
|
||||
param: string | string[] | LocationQueryValue | LocationQueryValue[]
|
||||
): string => {
|
||||
const _param = Array.isArray(param) ? param.at(0) ?? '' : param ?? ''
|
||||
//console.log('found param', _param, param);
|
||||
return decodeURIComponent(_param)
|
||||
}
|
||||
|
||||
export const isRouteActive = (
|
||||
to: RouteLocationRawI18n,
|
||||
exact: boolean = false
|
||||
) =>
|
||||
computed(() => {
|
||||
const found = useRouter()
|
||||
.getRoutes()
|
||||
.find((route) => route.name === useLocaleRoute()(to)?.name)
|
||||
//console.log('found route', found, useRouter().currentRoute.value, to);
|
||||
return exact
|
||||
? found?.name === useRouter().currentRoute.value.name
|
||||
: found?.name === useRouter().currentRoute.value.name ||
|
||||
found?.children.some(
|
||||
(child) => child.name === useRouter().currentRoute.value.name
|
||||
)
|
||||
})
|
||||
|
||||
export const isKey = <T extends object>(x: T, k: PropertyKey): k is keyof T => {
|
||||
return k in x
|
||||
}
|
||||
|
||||
export const filterAsync = async <T>(
|
||||
arr: T[],
|
||||
predicate: (value: T, index: number, array: T[]) => Promise<boolean>
|
||||
) => {
|
||||
// 1. Mappe jedes Element auf ein Promise, das zu true/false auflöst
|
||||
const results = await Promise.all(arr.map(predicate))
|
||||
|
||||
// 2. Filtere das ursprüngliche Array basierend auf den Ergebnissen
|
||||
return arr.filter((_value, index) => results[index])
|
||||
}
|
||||
|
||||
export const stringToHex = (str: string) =>
|
||||
str
|
||||
.split('')
|
||||
.map((char) => char.charCodeAt(0).toString(16).padStart(2, '0'))
|
||||
.join('') // Join array into a single string
|
||||
|
||||
export const hexToString = (hex: string) => {
|
||||
if (!hex) return ''
|
||||
const parsedValue = hex
|
||||
.match(/.{1,2}/g) // Split hex into pairs
|
||||
?.map((byte) => String.fromCharCode(parseInt(byte, 16))) // Convert hex to char
|
||||
.join('') // Join array into a single string
|
||||
|
||||
return parsedValue ? parsedValue : ''
|
||||
}
|
||||
115
utils/helper.ts
Normal file
115
utils/helper.ts
Normal file
@ -0,0 +1,115 @@
|
||||
export const bytesToBase64DataUrlAsync = async (
|
||||
bytes: Uint8Array,
|
||||
type = "application/octet-stream"
|
||||
) => {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const reader = Object.assign(new FileReader(), {
|
||||
onload: () => resolve(reader.result),
|
||||
onerror: () => reject(reader.error),
|
||||
});
|
||||
reader.readAsDataURL(new File([new Blob([bytes])], "", { type }));
|
||||
});
|
||||
};
|
||||
|
||||
export const blobToImageAsync = (blob: Blob): Promise<HTMLImageElement> => {
|
||||
return new Promise((resolve) => {
|
||||
console.log("transform blob", blob);
|
||||
const url = URL.createObjectURL(blob);
|
||||
let img = new Image();
|
||||
img.onload = () => {
|
||||
URL.revokeObjectURL(url);
|
||||
resolve(img);
|
||||
};
|
||||
img.src = url;
|
||||
});
|
||||
};
|
||||
|
||||
export const deepToRaw = <T extends Record<string, any>>(sourceObj: T): T => {
|
||||
const objectIterator = (input: any): any => {
|
||||
if (Array.isArray(input)) {
|
||||
return input.map((item) => objectIterator(item));
|
||||
}
|
||||
if (isRef(input) || isReactive(input) || isProxy(input)) {
|
||||
return objectIterator(toRaw(input));
|
||||
}
|
||||
if (input && typeof input === "object") {
|
||||
return Object.keys(input).reduce((acc, key) => {
|
||||
acc[key as keyof typeof acc] = objectIterator(input[key]);
|
||||
return acc;
|
||||
}, {} as T);
|
||||
}
|
||||
return input;
|
||||
};
|
||||
|
||||
return objectIterator(sourceObj);
|
||||
};
|
||||
|
||||
export const readableFileSize = (sizeInByte: number | string = 0) => {
|
||||
if (!sizeInByte) {
|
||||
return "0 KB";
|
||||
}
|
||||
const size = typeof sizeInByte === "string" ? parseInt(sizeInByte) : sizeInByte;
|
||||
const sizeInKb = size / 1024;
|
||||
const sizeInMb = sizeInKb / 1024;
|
||||
const sizeInGb = sizeInMb / 1024;
|
||||
const sizeInTb = sizeInGb / 1024;
|
||||
|
||||
if (sizeInTb > 1) return `${sizeInTb.toFixed(2)} TB`;
|
||||
if (sizeInGb > 1) return `${sizeInGb.toFixed(2)} GB`;
|
||||
if (sizeInMb > 1) return `${sizeInMb.toFixed(2)} MB`;
|
||||
|
||||
return `${sizeInKb.toFixed(2)} KB`;
|
||||
};
|
||||
|
||||
import type { LocationQueryValue, RouteLocationRawI18n } from "vue-router";
|
||||
|
||||
export const getSingleRouteParam = (
|
||||
param: string | string[] | LocationQueryValue | LocationQueryValue[]
|
||||
): string => {
|
||||
const _param = Array.isArray(param) ? param.at(0) ?? "" : param ?? "";
|
||||
//console.log('found param', _param, param);
|
||||
return decodeURIComponent(_param);
|
||||
};
|
||||
|
||||
export const isRouteActive = (to: RouteLocationRawI18n, exact: boolean = false) =>
|
||||
computed(() => {
|
||||
const found = useRouter()
|
||||
.getRoutes()
|
||||
.find((route) => route.name === useLocaleRoute()(to)?.name);
|
||||
//console.log('found route', found, useRouter().currentRoute.value, to);
|
||||
return exact
|
||||
? found?.name === useRouter().currentRoute.value.name
|
||||
: found?.name === useRouter().currentRoute.value.name ||
|
||||
found?.children.some((child) => child.name === useRouter().currentRoute.value.name);
|
||||
});
|
||||
|
||||
export const isKey = <T extends object>(x: T, k: PropertyKey): k is keyof T => {
|
||||
return k in x;
|
||||
};
|
||||
|
||||
export const filterAsync = async <T>(
|
||||
arr: T[],
|
||||
predicate: (value: T, index: number, array: T[]) => Promise<boolean>
|
||||
) => {
|
||||
// 1. Mappe jedes Element auf ein Promise, das zu true/false auflöst
|
||||
const results = await Promise.all(arr.map(predicate));
|
||||
|
||||
// 2. Filtere das ursprüngliche Array basierend auf den Ergebnissen
|
||||
return arr.filter((_value, index) => results[index]);
|
||||
};
|
||||
|
||||
export const stringToHex = (str: string) =>
|
||||
str
|
||||
.split("")
|
||||
.map((char) => char.charCodeAt(0).toString(16).padStart(2, "0"))
|
||||
.join(""); // Join array into a single string
|
||||
|
||||
export const hexToString = (hex: string) => {
|
||||
if (!hex) return "";
|
||||
const parsedValue = hex
|
||||
.match(/.{1,2}/g) // Split hex into pairs
|
||||
?.map((byte) => String.fromCharCode(parseInt(byte, 16))) // Convert hex to char
|
||||
.join(""); // Join array into a single string
|
||||
|
||||
return parsedValue ? parsedValue : "";
|
||||
};
|
||||
Reference in New Issue
Block a user