feat: framework done
This commit is contained in:
parent
39076673dd
commit
80c746f14e
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
node_modules
|
||||
target
|
||||
dist
|
||||
dist
|
||||
.env
|
||||
@ -17,17 +17,24 @@
|
||||
"sizecheck": "npx vite-bundle-visualizer"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
"@internationalized/date": "^3.7.0",
|
||||
"@unhead/vue": "^1.11.15",
|
||||
"@unocss/preset-web-fonts": "^65.4.3",
|
||||
"@unocss/reset": "^65.4.0",
|
||||
"@vueuse/core": "^12.4.0",
|
||||
"@vueuse/head": "^2.0.0",
|
||||
"beasties": "^0.2.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-vue-next": "^0.474.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.3.0",
|
||||
"radix-vue": "^1.9.13",
|
||||
"shadcn-vue": "^0.11.3",
|
||||
"tailwind-merge": "^3.0.1",
|
||||
"unplugin-vue-define-options": "1.5.5",
|
||||
"vaul-vue": "^0.2.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-demi": "^0.14.10",
|
||||
"vue-i18n": "^11.0.1",
|
||||
|
||||
105
pnpm-lock.yaml
generated
105
pnpm-lock.yaml
generated
@ -13,6 +13,12 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@amap/amap-jsapi-loader':
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
'@internationalized/date':
|
||||
specifier: ^3.7.0
|
||||
version: 3.7.0
|
||||
'@unhead/vue':
|
||||
specifier: ^1.11.15
|
||||
version: 1.11.15(vue@3.5.13(typescript@5.7.3))
|
||||
@ -31,21 +37,36 @@ importers:
|
||||
beasties:
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
lucide-vue-next:
|
||||
specifier: ^0.474.0
|
||||
version: 0.474.0(vue@3.5.13(typescript@5.7.3))
|
||||
nprogress:
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0
|
||||
pinia:
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
|
||||
radix-vue:
|
||||
specifier: ^1.9.13
|
||||
version: 1.9.13(vue@3.5.13(typescript@5.7.3))
|
||||
shadcn-vue:
|
||||
specifier: ^0.11.3
|
||||
version: 0.11.3(@vitest/ui@2.1.8)(eslint@9.18.0(jiti@2.4.0))(vitest@2.1.8)(vue@3.5.13(typescript@5.7.3))
|
||||
tailwind-merge:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
unplugin-vue-define-options:
|
||||
specifier: 1.5.5
|
||||
version: 1.5.5(vue@3.5.13(typescript@5.7.3))
|
||||
vaul-vue:
|
||||
specifier: ^0.2.1
|
||||
version: 0.2.1(radix-vue@1.9.13(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))
|
||||
vue:
|
||||
specifier: ^3.5.13
|
||||
version: 3.5.13(typescript@5.7.3)
|
||||
@ -195,6 +216,9 @@ packages:
|
||||
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
'@amap/amap-jsapi-loader@1.0.1':
|
||||
resolution: {integrity: sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw==}
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@ -2103,6 +2127,15 @@ packages:
|
||||
vue:
|
||||
optional: true
|
||||
|
||||
'@vue-macros/common@1.16.1':
|
||||
resolution: {integrity: sha512-Pn/AWMTjoMYuquepLZP813BIcq8DTZiNCoaceuNlvaYuOTd8DqBZWc5u0uOMQZMInwME1mdSmmBAcTluiV9Jtg==}
|
||||
engines: {node: '>=16.14.0'}
|
||||
peerDependencies:
|
||||
vue: ^2.7.0 || ^3.2.25
|
||||
peerDependenciesMeta:
|
||||
vue:
|
||||
optional: true
|
||||
|
||||
'@vue-macros/config@0.5.1':
|
||||
resolution: {integrity: sha512-LqrsrrtqTgTLLnducOoG2mfDkrP52Qar4IcQfjXfsr+U3QXc4TqgbGxYfZ5Hk5Sf6lCymwPTLU6QEJo/hVpGTA==}
|
||||
engines: {node: '>=16.14.0'}
|
||||
@ -2458,6 +2491,10 @@ packages:
|
||||
resolution: {integrity: sha512-gdvX700WVC6sHCJQ7bJGfDvtuKAh6Sa6weIZROxfzUZKP7BjvB8y0SMlM/o4omSQ3L60PQSJROBJsb0vEViVnA==}
|
||||
engines: {node: '>=16.14.0'}
|
||||
|
||||
ast-kit@1.4.0:
|
||||
resolution: {integrity: sha512-BlGeOw73FDsX7z0eZE/wuuafxYoek2yzNJ6l6A1nsb4+z/p87TOPbHaWuN53kFKNuUXiCQa2M+xLF71IqQmRSw==}
|
||||
engines: {node: '>=16.14.0'}
|
||||
|
||||
ast-types@0.14.2:
|
||||
resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==}
|
||||
engines: {node: '>=4'}
|
||||
@ -2685,6 +2722,9 @@ packages:
|
||||
citty@0.1.6:
|
||||
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
|
||||
|
||||
class-variance-authority@0.7.1:
|
||||
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
||||
|
||||
clean-css@5.3.2:
|
||||
resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==}
|
||||
engines: {node: '>= 10.0'}
|
||||
@ -4257,10 +4297,19 @@ packages:
|
||||
lru-cache@5.1.1:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
|
||||
lucide-vue-next@0.474.0:
|
||||
resolution: {integrity: sha512-bQaSBjfJ33xiPQCxCf4JD3rcUgZFgWZzxSY8SScNa4Mcq2vWGlbvQx6icTL1UXRqsxzfoT13RXawePSmgg4iWw==}
|
||||
peerDependencies:
|
||||
vue: '>=3.0.1'
|
||||
|
||||
magic-string-ast@0.6.3:
|
||||
resolution: {integrity: sha512-C9sgUzVZtUtzCBoMdYtwrIRQ4IucGRFGgdhkjL7PXsVfPYmTuWtewqzk7dlipaCMWH/gOYehW9rgMoa4Oebtpw==}
|
||||
engines: {node: '>=16.14.0'}
|
||||
|
||||
magic-string-ast@0.7.0:
|
||||
resolution: {integrity: sha512-686fgAHaJY7wLTFEq7nnKqeQrhqmXB19d1HnqT35Ci7BN6hbAYLZUezTQ062uUHM7ggZEQlqJ94Ftls+KDXU8Q==}
|
||||
engines: {node: '>=16.14.0'}
|
||||
|
||||
magic-string@0.25.9:
|
||||
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
|
||||
|
||||
@ -5780,6 +5829,10 @@ packages:
|
||||
resolution: {integrity: sha512-vh9hOHd/VRxZ5QHdLdl0WO8aZWuVx8GN8eExCP665o8anh0LC8Xj4PF5zx0BxRcDi6AZOnTQZPgfQlA0Iw/74Q==}
|
||||
engines: {node: '>=16.14.0'}
|
||||
|
||||
unplugin-vue-define-options@1.5.5:
|
||||
resolution: {integrity: sha512-V50sWbpoADsjyVgovxewoLo2IDW0zfgHJbKiAl2EdZT8OL3g3h1Mz3QKoAAu09i8+LnkDatIEQMgBVeHHxWXNg==}
|
||||
engines: {node: '>=16.14.0'}
|
||||
|
||||
unplugin-vue-macros@2.13.8:
|
||||
resolution: {integrity: sha512-+TR+276cFvaXpU/h/4eSCOeVBF+JPLpiJ0NYDcVC3T0wn4RqeJapMJPZnZi1YfADFptoRhsfy/mTLg3XJZl0jw==}
|
||||
engines: {node: '>=16.14.0'}
|
||||
@ -5838,6 +5891,12 @@ packages:
|
||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
vaul-vue@0.2.1:
|
||||
resolution: {integrity: sha512-iF91R1JQZzxb9mb9uGNHYv8rVFxR5bL5Fj51iqvyXjzMPAzNMciCrXb9OUBu2NdlhcF6rVtEADXnQoTY+pKIeA==}
|
||||
peerDependencies:
|
||||
radix-vue: ^1.4.0
|
||||
vue: ^3.3.0
|
||||
|
||||
verror@1.10.0:
|
||||
resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
|
||||
engines: {'0': node >=0.6.0}
|
||||
@ -6234,6 +6293,8 @@ snapshots:
|
||||
|
||||
'@aashutoshrathi/word-wrap@1.2.6': {}
|
||||
|
||||
'@amap/amap-jsapi-loader@1.0.1': {}
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.5
|
||||
@ -8424,6 +8485,17 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
|
||||
'@vue-macros/common@1.16.1(vue@3.5.13(typescript@5.7.3))':
|
||||
dependencies:
|
||||
'@vue/compiler-sfc': 3.5.13
|
||||
ast-kit: 1.4.0
|
||||
local-pkg: 1.0.0
|
||||
magic-string-ast: 0.7.0
|
||||
pathe: 2.0.2
|
||||
picomatch: 4.0.2
|
||||
optionalDependencies:
|
||||
vue: 3.5.13(typescript@5.7.3)
|
||||
|
||||
'@vue-macros/config@0.5.1(rollup@4.30.1)(vue@3.5.13(typescript@5.7.3))':
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.15.1(rollup@4.30.1)(vue@3.5.13(typescript@5.7.3))
|
||||
@ -8951,6 +9023,11 @@ snapshots:
|
||||
'@babel/parser': 7.26.3
|
||||
pathe: 1.1.2
|
||||
|
||||
ast-kit@1.4.0:
|
||||
dependencies:
|
||||
'@babel/parser': 7.26.7
|
||||
pathe: 2.0.2
|
||||
|
||||
ast-types@0.14.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
@ -9191,6 +9268,10 @@ snapshots:
|
||||
dependencies:
|
||||
consola: 3.3.1
|
||||
|
||||
class-variance-authority@0.7.1:
|
||||
dependencies:
|
||||
clsx: 2.1.1
|
||||
|
||||
clean-css@5.3.2:
|
||||
dependencies:
|
||||
source-map: 0.6.1
|
||||
@ -11009,10 +11090,18 @@ snapshots:
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
|
||||
lucide-vue-next@0.474.0(vue@3.5.13(typescript@5.7.3)):
|
||||
dependencies:
|
||||
vue: 3.5.13(typescript@5.7.3)
|
||||
|
||||
magic-string-ast@0.6.3:
|
||||
dependencies:
|
||||
magic-string: 0.30.17
|
||||
|
||||
magic-string-ast@0.7.0:
|
||||
dependencies:
|
||||
magic-string: 0.30.17
|
||||
|
||||
magic-string@0.25.9:
|
||||
dependencies:
|
||||
sourcemap-codec: 1.4.8
|
||||
@ -12856,6 +12945,14 @@ snapshots:
|
||||
- rollup
|
||||
- vue
|
||||
|
||||
unplugin-vue-define-options@1.5.5(vue@3.5.13(typescript@5.7.3)):
|
||||
dependencies:
|
||||
'@vue-macros/common': 1.16.1(vue@3.5.13(typescript@5.7.3))
|
||||
ast-walker-scope: 0.6.2
|
||||
unplugin: 2.1.2
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
unplugin-vue-macros@2.13.8(@vueuse/core@12.4.0(typescript@5.7.3))(esbuild@0.23.1)(rollup@4.30.1)(typescript@5.7.3)(vite@6.0.7(@types/node@20.2.3)(jiti@2.4.0)(stylus@0.57.0)(terser@5.17.6)(tsx@4.19.2)(yaml@2.6.1))(vue-tsc@2.2.0(typescript@5.7.3))(vue@3.5.13(typescript@5.7.3)):
|
||||
dependencies:
|
||||
'@vue-macros/better-define': 1.11.1(rollup@4.30.1)(vue@3.5.13(typescript@5.7.3))
|
||||
@ -12971,6 +13068,14 @@ snapshots:
|
||||
|
||||
vary@1.1.2: {}
|
||||
|
||||
vaul-vue@0.2.1(radix-vue@1.9.13(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3)):
|
||||
dependencies:
|
||||
'@vueuse/core': 10.11.1(vue@3.5.13(typescript@5.7.3))
|
||||
radix-vue: 1.9.13(vue@3.5.13(typescript@5.7.3))
|
||||
vue: 3.5.13(typescript@5.7.3)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
|
||||
verror@1.10.0:
|
||||
dependencies:
|
||||
assert-plus: 1.0.0
|
||||
|
||||
@ -25,5 +25,7 @@ useHead({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
<TooltipProvider>
|
||||
<RouterView />
|
||||
</TooltipProvider>
|
||||
</template>
|
||||
|
||||
4
src/auto-imports.d.ts
vendored
4
src/auto-imports.d.ts
vendored
@ -9,6 +9,7 @@ declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||
const baseFetch: typeof import('./composables/baseFetch')['baseFetch']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const computedAsync: typeof import('@vueuse/core')['computedAsync']
|
||||
const computedEager: typeof import('@vueuse/core')['computedEager']
|
||||
@ -212,6 +213,7 @@ declare global {
|
||||
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
|
||||
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
|
||||
const useOnline: typeof import('@vueuse/core')['useOnline']
|
||||
const useOptions: typeof import('./composables/dateOpt')['useOptions']
|
||||
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
|
||||
const useParallax: typeof import('@vueuse/core')['useParallax']
|
||||
const useParentElement: typeof import('@vueuse/core')['useParentElement']
|
||||
@ -316,6 +318,7 @@ declare module 'vue' {
|
||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
|
||||
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
|
||||
readonly baseFetch: UnwrapRef<typeof import('./composables/baseFetch')['baseFetch']>
|
||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||
readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
|
||||
readonly computedEager: UnwrapRef<typeof import('@vueuse/core')['computedEager']>
|
||||
@ -517,6 +520,7 @@ declare module 'vue' {
|
||||
readonly useObjectUrl: UnwrapRef<typeof import('@vueuse/core')['useObjectUrl']>
|
||||
readonly useOffsetPagination: UnwrapRef<typeof import('@vueuse/core')['useOffsetPagination']>
|
||||
readonly useOnline: UnwrapRef<typeof import('@vueuse/core')['useOnline']>
|
||||
readonly useOptions: UnwrapRef<typeof import('./composables/dateOpt')['useOptions']>
|
||||
readonly usePageLeave: UnwrapRef<typeof import('@vueuse/core')['usePageLeave']>
|
||||
readonly useParallax: UnwrapRef<typeof import('@vueuse/core')['useParallax']>
|
||||
readonly useParentElement: UnwrapRef<typeof import('@vueuse/core')['useParentElement']>
|
||||
|
||||
48
src/components.d.ts
vendored
48
src/components.d.ts
vendored
@ -7,11 +7,59 @@ export {}
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Badge: typeof import('./components/ui/badge/Badge.vue')['default']
|
||||
Button: typeof import('./components/ui/button/Button.vue')['default']
|
||||
Calendar: typeof import('./components/ui/calendar/Calendar.vue')['default']
|
||||
CalendarCell: typeof import('./components/ui/calendar/CalendarCell.vue')['default']
|
||||
CalendarCellTrigger: typeof import('./components/ui/calendar/CalendarCellTrigger.vue')['default']
|
||||
CalendarGrid: typeof import('./components/ui/calendar/CalendarGrid.vue')['default']
|
||||
CalendarGridBody: typeof import('./components/ui/calendar/CalendarGridBody.vue')['default']
|
||||
CalendarGridHead: typeof import('./components/ui/calendar/CalendarGridHead.vue')['default']
|
||||
CalendarGridRow: typeof import('./components/ui/calendar/CalendarGridRow.vue')['default']
|
||||
CalendarHeadCell: typeof import('./components/ui/calendar/CalendarHeadCell.vue')['default']
|
||||
CalendarHeader: typeof import('./components/ui/calendar/CalendarHeader.vue')['default']
|
||||
CalendarHeading: typeof import('./components/ui/calendar/CalendarHeading.vue')['default']
|
||||
CalendarNextButton: typeof import('./components/ui/calendar/CalendarNextButton.vue')['default']
|
||||
CalendarPrevButton: typeof import('./components/ui/calendar/CalendarPrevButton.vue')['default']
|
||||
Card: typeof import('./components/ui/card/Card.vue')['default']
|
||||
CardContent: typeof import('./components/ui/card/CardContent.vue')['default']
|
||||
CardDescription: typeof import('./components/ui/card/CardDescription.vue')['default']
|
||||
CardFooter: typeof import('./components/ui/card/CardFooter.vue')['default']
|
||||
CardHeader: typeof import('./components/ui/card/CardHeader.vue')['default']
|
||||
CardTitle: typeof import('./components/ui/card/CardTitle.vue')['default']
|
||||
Drawer: typeof import('./components/ui/drawer/Drawer.vue')['default']
|
||||
DrawerContent: typeof import('./components/ui/drawer/DrawerContent.vue')['default']
|
||||
DrawerDescription: typeof import('./components/ui/drawer/DrawerDescription.vue')['default']
|
||||
DrawerFooter: typeof import('./components/ui/drawer/DrawerFooter.vue')['default']
|
||||
DrawerHeader: typeof import('./components/ui/drawer/DrawerHeader.vue')['default']
|
||||
DrawerOverlay: typeof import('./components/ui/drawer/DrawerOverlay.vue')['default']
|
||||
DrawerTitle: typeof import('./components/ui/drawer/DrawerTitle.vue')['default']
|
||||
Input: typeof import('./components/ui/input/Input.vue')['default']
|
||||
Label: typeof import('./components/ui/label/Label.vue')['default']
|
||||
Popover: typeof import('./components/ui/popover/Popover.vue')['default']
|
||||
PopoverContent: typeof import('./components/ui/popover/PopoverContent.vue')['default']
|
||||
PopoverTrigger: typeof import('./components/ui/popover/PopoverTrigger.vue')['default']
|
||||
README: typeof import('./components/README.md')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
Select: typeof import('./components/ui/select/Select.vue')['default']
|
||||
SelectContent: typeof import('./components/ui/select/SelectContent.vue')['default']
|
||||
SelectGroup: typeof import('./components/ui/select/SelectGroup.vue')['default']
|
||||
SelectItem: typeof import('./components/ui/select/SelectItem.vue')['default']
|
||||
SelectItemText: typeof import('./components/ui/select/SelectItemText.vue')['default']
|
||||
SelectLabel: typeof import('./components/ui/select/SelectLabel.vue')['default']
|
||||
SelectScrollDownButton: typeof import('./components/ui/select/SelectScrollDownButton.vue')['default']
|
||||
SelectScrollUpButton: typeof import('./components/ui/select/SelectScrollUpButton.vue')['default']
|
||||
SelectSeparator: typeof import('./components/ui/select/SelectSeparator.vue')['default']
|
||||
SelectTrigger: typeof import('./components/ui/select/SelectTrigger.vue')['default']
|
||||
SelectValue: typeof import('./components/ui/select/SelectValue.vue')['default']
|
||||
Textarea: typeof import('./components/ui/textarea/Textarea.vue')['default']
|
||||
TheCounter: typeof import('./components/TheCounter.vue')['default']
|
||||
TheFooter: typeof import('./components/TheFooter.vue')['default']
|
||||
TheInput: typeof import('./components/TheInput.vue')['default']
|
||||
Tooltip: typeof import('./components/ui/tooltip/Tooltip.vue')['default']
|
||||
TooltipContent: typeof import('./components/ui/tooltip/TooltipContent.vue')['default']
|
||||
TooltipProvider: typeof import('./components/ui/tooltip/TooltipProvider.vue')['default']
|
||||
TooltipTrigger: typeof import('./components/ui/tooltip/TooltipTrigger.vue')['default']
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
## Components
|
||||
|
||||
Components in this dir will be auto-registered and on-demand, powered by [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components).
|
||||
|
||||
### Icons
|
||||
|
||||
You can use icons from almost any icon sets by the power of [Iconify](https://iconify.design/).
|
||||
|
||||
It will only bundle the icons you use. Check out [`unplugin-icons`](https://github.com/antfu/unplugin-icons) for more details.
|
||||
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { availableLocales, loadLanguageAsync } from '~/modules/i18n'
|
||||
import { availableLocales, loadLanguageAsync } from '@/modules/i18n'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
|
||||
16
src/components/ui/badge/Badge.vue
Normal file
16
src/components/ui/badge/Badge.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { type BadgeVariants, badgeVariants } from '.'
|
||||
|
||||
const props = defineProps<{
|
||||
variant?: BadgeVariants['variant']
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn(badgeVariants({ variant }), props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
25
src/components/ui/badge/index.ts
Normal file
25
src/components/ui/badge/index.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
|
||||
export { default as Badge } from './Badge.vue'
|
||||
|
||||
export const badgeVariants = cva(
|
||||
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
||||
secondary:
|
||||
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
destructive:
|
||||
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
||||
outline: 'text-foreground',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export type BadgeVariants = VariantProps<typeof badgeVariants>
|
||||
26
src/components/ui/button/Button.vue
Normal file
26
src/components/ui/button/Button.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Primitive, type PrimitiveProps } from 'radix-vue'
|
||||
import { type ButtonVariants, buttonVariants } from '.'
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
variant?: ButtonVariants['variant']
|
||||
size?: ButtonVariants['size']
|
||||
class?: HTMLAttributes['class']
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: 'button',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn(buttonVariants({ variant, size }), props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
35
src/components/ui/button/index.ts
Normal file
35
src/components/ui/button/index.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
|
||||
export { default as Button } from './Button.vue'
|
||||
|
||||
export const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
||||
outline:
|
||||
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: 'h-9 px-4 py-2',
|
||||
sm: 'h-8 rounded-md px-3 text-xs',
|
||||
lg: 'h-10 rounded-md px-8',
|
||||
icon: 'h-9 w-9',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export type ButtonVariants = VariantProps<typeof buttonVariants>
|
||||
60
src/components/ui/calendar/Calendar.vue
Normal file
60
src/components/ui/calendar/Calendar.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CalendarRoot, type CalendarRootEmits, type CalendarRootProps, useForwardPropsEmits } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
import { CalendarCell, CalendarCellTrigger, CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading, CalendarNextButton, CalendarPrevButton } from '.'
|
||||
|
||||
const props = defineProps<CalendarRootProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const emits = defineEmits<CalendarRootEmits>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarRoot
|
||||
v-slot="{ grid, weekDays }"
|
||||
:class="cn('p-3', props.class)"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<CalendarHeader>
|
||||
<CalendarPrevButton />
|
||||
<CalendarHeading />
|
||||
<CalendarNextButton />
|
||||
</CalendarHeader>
|
||||
|
||||
<div class="mt-4 flex flex-col gap-y-4 sm:flex-row sm:gap-x-4 sm:gap-y-0">
|
||||
<CalendarGrid v-for="month in grid" :key="month.value.toString()">
|
||||
<CalendarGridHead>
|
||||
<CalendarGridRow>
|
||||
<CalendarHeadCell
|
||||
v-for="day in weekDays" :key="day"
|
||||
>
|
||||
{{ day }}
|
||||
</CalendarHeadCell>
|
||||
</CalendarGridRow>
|
||||
</CalendarGridHead>
|
||||
<CalendarGridBody>
|
||||
<CalendarGridRow v-for="(weekDates, index) in month.rows" :key="`weekDate-${index}`" class="mt-2 w-full">
|
||||
<CalendarCell
|
||||
v-for="weekDate in weekDates"
|
||||
:key="weekDate.toString()"
|
||||
:date="weekDate"
|
||||
>
|
||||
<CalendarCellTrigger
|
||||
:day="weekDate"
|
||||
:month="month.value"
|
||||
/>
|
||||
</CalendarCell>
|
||||
</CalendarGridRow>
|
||||
</CalendarGridBody>
|
||||
</CalendarGrid>
|
||||
</div>
|
||||
</CalendarRoot>
|
||||
</template>
|
||||
24
src/components/ui/calendar/CalendarCell.vue
Normal file
24
src/components/ui/calendar/CalendarCell.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CalendarCell, type CalendarCellProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<CalendarCellProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarCell
|
||||
:class="cn('relative h-9 w-9 p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md [&:has([data-selected])]:bg-accent [&:has([data-selected][data-outside-view])]:bg-accent/50', props.class)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
</CalendarCell>
|
||||
</template>
|
||||
38
src/components/ui/calendar/CalendarCellTrigger.vue
Normal file
38
src/components/ui/calendar/CalendarCellTrigger.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<script lang="ts" setup>
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CalendarCellTrigger, type CalendarCellTriggerProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<CalendarCellTriggerProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarCellTrigger
|
||||
:class="cn(
|
||||
buttonVariants({ variant: 'ghost' }),
|
||||
'h-9 w-9 p-0 font-normal',
|
||||
'[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground',
|
||||
// Selected
|
||||
'data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:opacity-100 data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground data-[selected]:focus:bg-primary data-[selected]:focus:text-primary-foreground',
|
||||
// Disabled
|
||||
'data-[disabled]:text-muted-foreground data-[disabled]:opacity-50',
|
||||
// Unavailable
|
||||
'data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through',
|
||||
// Outside months
|
||||
'data-[outside-view]:text-muted-foreground data-[outside-view]:opacity-50 [&[data-outside-view][data-selected]]:bg-accent/50 [&[data-outside-view][data-selected]]:text-muted-foreground [&[data-outside-view][data-selected]]:opacity-30',
|
||||
props.class,
|
||||
)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
</CalendarCellTrigger>
|
||||
</template>
|
||||
24
src/components/ui/calendar/CalendarGrid.vue
Normal file
24
src/components/ui/calendar/CalendarGrid.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CalendarGrid, type CalendarGridProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<CalendarGridProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarGrid
|
||||
:class="cn('w-full border-collapse space-y-1', props.class)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
</CalendarGrid>
|
||||
</template>
|
||||
11
src/components/ui/calendar/CalendarGridBody.vue
Normal file
11
src/components/ui/calendar/CalendarGridBody.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { CalendarGridBody, type CalendarGridBodyProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<CalendarGridBodyProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarGridBody v-bind="props">
|
||||
<slot />
|
||||
</CalendarGridBody>
|
||||
</template>
|
||||
11
src/components/ui/calendar/CalendarGridHead.vue
Normal file
11
src/components/ui/calendar/CalendarGridHead.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { CalendarGridHead, type CalendarGridHeadProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<CalendarGridHeadProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarGridHead v-bind="props">
|
||||
<slot />
|
||||
</CalendarGridHead>
|
||||
</template>
|
||||
21
src/components/ui/calendar/CalendarGridRow.vue
Normal file
21
src/components/ui/calendar/CalendarGridRow.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CalendarGridRow, type CalendarGridRowProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<CalendarGridRowProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarGridRow :class="cn('flex', props.class)" v-bind="forwardedProps">
|
||||
<slot />
|
||||
</CalendarGridRow>
|
||||
</template>
|
||||
21
src/components/ui/calendar/CalendarHeadCell.vue
Normal file
21
src/components/ui/calendar/CalendarHeadCell.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CalendarHeadCell, type CalendarHeadCellProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<CalendarHeadCellProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarHeadCell :class="cn('w-9 rounded-md text-[0.8rem] font-normal text-muted-foreground', props.class)" v-bind="forwardedProps">
|
||||
<slot />
|
||||
</CalendarHeadCell>
|
||||
</template>
|
||||
21
src/components/ui/calendar/CalendarHeader.vue
Normal file
21
src/components/ui/calendar/CalendarHeader.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CalendarHeader, type CalendarHeaderProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<CalendarHeaderProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarHeader :class="cn('relative flex w-full items-center justify-between pt-1', props.class)" v-bind="forwardedProps">
|
||||
<slot />
|
||||
</CalendarHeader>
|
||||
</template>
|
||||
27
src/components/ui/calendar/CalendarHeading.vue
Normal file
27
src/components/ui/calendar/CalendarHeading.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CalendarHeading, type CalendarHeadingProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<CalendarHeadingProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarHeading
|
||||
v-slot="{ headingValue }"
|
||||
:class="cn('text-sm font-medium', props.class)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot :heading-value>
|
||||
{{ headingValue }}
|
||||
</slot>
|
||||
</CalendarHeading>
|
||||
</template>
|
||||
32
src/components/ui/calendar/CalendarNextButton.vue
Normal file
32
src/components/ui/calendar/CalendarNextButton.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<script lang="ts" setup>
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { ChevronRight } from 'lucide-vue-next'
|
||||
import { CalendarNext, type CalendarNextProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<CalendarNextProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarNext
|
||||
:class="cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||
props.class,
|
||||
)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot>
|
||||
<ChevronRight class="h-4 w-4" />
|
||||
</slot>
|
||||
</CalendarNext>
|
||||
</template>
|
||||
32
src/components/ui/calendar/CalendarPrevButton.vue
Normal file
32
src/components/ui/calendar/CalendarPrevButton.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<script lang="ts" setup>
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { ChevronLeft } from 'lucide-vue-next'
|
||||
import { CalendarPrev, type CalendarPrevProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<CalendarPrevProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarPrev
|
||||
:class="cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||
props.class,
|
||||
)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot>
|
||||
<ChevronLeft class="h-4 w-4" />
|
||||
</slot>
|
||||
</CalendarPrev>
|
||||
</template>
|
||||
12
src/components/ui/calendar/index.ts
Normal file
12
src/components/ui/calendar/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export { default as Calendar } from './Calendar.vue'
|
||||
export { default as CalendarCell } from './CalendarCell.vue'
|
||||
export { default as CalendarCellTrigger } from './CalendarCellTrigger.vue'
|
||||
export { default as CalendarGrid } from './CalendarGrid.vue'
|
||||
export { default as CalendarGridBody } from './CalendarGridBody.vue'
|
||||
export { default as CalendarGridHead } from './CalendarGridHead.vue'
|
||||
export { default as CalendarGridRow } from './CalendarGridRow.vue'
|
||||
export { default as CalendarHeadCell } from './CalendarHeadCell.vue'
|
||||
export { default as CalendarHeader } from './CalendarHeader.vue'
|
||||
export { default as CalendarHeading } from './CalendarHeading.vue'
|
||||
export { default as CalendarNextButton } from './CalendarNextButton.vue'
|
||||
export { default as CalendarPrevButton } from './CalendarPrevButton.vue'
|
||||
21
src/components/ui/card/Card.vue
Normal file
21
src/components/ui/card/Card.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'rounded-lg border bg-card text-card-foreground shadow-sm',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/card/CardContent.vue
Normal file
14
src/components/ui/card/CardContent.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('p-6 pt-0', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/card/CardDescription.vue
Normal file
14
src/components/ui/card/CardDescription.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p :class="cn('text-sm text-muted-foreground', props.class)">
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
14
src/components/ui/card/CardFooter.vue
Normal file
14
src/components/ui/card/CardFooter.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex items-center p-6 pt-0', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/card/CardHeader.vue
Normal file
14
src/components/ui/card/CardHeader.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex flex-col gap-y-1.5 p-6', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
18
src/components/ui/card/CardTitle.vue
Normal file
18
src/components/ui/card/CardTitle.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h3
|
||||
:class="
|
||||
cn('text-2xl font-semibold leading-none tracking-tight', props.class)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</h3>
|
||||
</template>
|
||||
6
src/components/ui/card/index.ts
Normal file
6
src/components/ui/card/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { default as Card } from './Card.vue'
|
||||
export { default as CardContent } from './CardContent.vue'
|
||||
export { default as CardDescription } from './CardDescription.vue'
|
||||
export { default as CardFooter } from './CardFooter.vue'
|
||||
export { default as CardHeader } from './CardHeader.vue'
|
||||
export { default as CardTitle } from './CardTitle.vue'
|
||||
19
src/components/ui/drawer/Drawer.vue
Normal file
19
src/components/ui/drawer/Drawer.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import type { DrawerRootEmits, DrawerRootProps } from 'vaul-vue'
|
||||
import { useForwardPropsEmits } from 'radix-vue'
|
||||
import { DrawerRoot } from 'vaul-vue'
|
||||
|
||||
const props = withDefaults(defineProps<DrawerRootProps>(), {
|
||||
shouldScaleBackground: true,
|
||||
})
|
||||
|
||||
const emits = defineEmits<DrawerRootEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</DrawerRoot>
|
||||
</template>
|
||||
28
src/components/ui/drawer/DrawerContent.vue
Normal file
28
src/components/ui/drawer/DrawerContent.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import type { DialogContentEmits, DialogContentProps } from 'radix-vue'
|
||||
import type { HtmlHTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useForwardPropsEmits } from 'radix-vue'
|
||||
import { DrawerContent, DrawerPortal } from 'vaul-vue'
|
||||
import DrawerOverlay from './DrawerOverlay.vue'
|
||||
|
||||
const props = defineProps<DialogContentProps & { class?: HtmlHTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<DialogContentEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerPortal>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent
|
||||
v-bind="forwarded" :class="cn(
|
||||
'fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<div class="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
||||
<slot />
|
||||
</DrawerContent>
|
||||
</DrawerPortal>
|
||||
</template>
|
||||
20
src/components/ui/drawer/DrawerDescription.vue
Normal file
20
src/components/ui/drawer/DrawerDescription.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import type { DrawerDescriptionProps } from 'vaul-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { DrawerDescription } from 'vaul-vue'
|
||||
import { computed, type HtmlHTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<DrawerDescriptionProps & { class?: HtmlHTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerDescription v-bind="delegatedProps" :class="cn('text-sm text-muted-foreground', props.class)">
|
||||
<slot />
|
||||
</DrawerDescription>
|
||||
</template>
|
||||
14
src/components/ui/drawer/DrawerFooter.vue
Normal file
14
src/components/ui/drawer/DrawerFooter.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import type { HtmlHTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HtmlHTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('mt-auto flex flex-col gap-2 p-4', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/drawer/DrawerHeader.vue
Normal file
14
src/components/ui/drawer/DrawerHeader.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import type { HtmlHTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HtmlHTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('grid gap-1.5 p-4 text-center sm:text-left', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
18
src/components/ui/drawer/DrawerOverlay.vue
Normal file
18
src/components/ui/drawer/DrawerOverlay.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<script lang="ts" setup>
|
||||
import type { DialogOverlayProps } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { DrawerOverlay } from 'vaul-vue'
|
||||
import { computed, type HtmlHTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<DialogOverlayProps & { class?: HtmlHTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerOverlay v-bind="delegatedProps" :class="cn('fixed inset-0 z-50 bg-black/80', props.class)" />
|
||||
</template>
|
||||
20
src/components/ui/drawer/DrawerTitle.vue
Normal file
20
src/components/ui/drawer/DrawerTitle.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import type { DrawerTitleProps } from 'vaul-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { DrawerTitle } from 'vaul-vue'
|
||||
import { computed, type HtmlHTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<DrawerTitleProps & { class?: HtmlHTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerTitle v-bind="delegatedProps" :class="cn('text-lg font-semibold leading-none tracking-tight', props.class)">
|
||||
<slot />
|
||||
</DrawerTitle>
|
||||
</template>
|
||||
8
src/components/ui/drawer/index.ts
Normal file
8
src/components/ui/drawer/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export { default as Drawer } from './Drawer.vue'
|
||||
export { default as DrawerContent } from './DrawerContent.vue'
|
||||
export { default as DrawerDescription } from './DrawerDescription.vue'
|
||||
export { default as DrawerFooter } from './DrawerFooter.vue'
|
||||
export { default as DrawerHeader } from './DrawerHeader.vue'
|
||||
export { default as DrawerOverlay } from './DrawerOverlay.vue'
|
||||
export { default as DrawerTitle } from './DrawerTitle.vue'
|
||||
export { DrawerClose, DrawerPortal, DrawerTrigger } from 'vaul-vue'
|
||||
24
src/components/ui/input/Input.vue
Normal file
24
src/components/ui/input/Input.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
|
||||
const props = defineProps<{
|
||||
defaultValue?: string | number
|
||||
modelValue?: string | number
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:modelValue', payload: string | number): void
|
||||
}>()
|
||||
|
||||
const modelValue = useVModel(props, 'modelValue', emits, {
|
||||
passive: true,
|
||||
defaultValue: props.defaultValue,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input v-model="modelValue" :class="cn('flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', props.class)">
|
||||
</template>
|
||||
1
src/components/ui/input/index.ts
Normal file
1
src/components/ui/input/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Input } from './Input.vue'
|
||||
27
src/components/ui/label/Label.vue
Normal file
27
src/components/ui/label/Label.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Label, type LabelProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<LabelProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Label
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</Label>
|
||||
</template>
|
||||
1
src/components/ui/label/index.ts
Normal file
1
src/components/ui/label/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Label } from './Label.vue'
|
||||
15
src/components/ui/popover/Popover.vue
Normal file
15
src/components/ui/popover/Popover.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import type { PopoverRootEmits, PopoverRootProps } from 'radix-vue'
|
||||
import { PopoverRoot, useForwardPropsEmits } from 'radix-vue'
|
||||
|
||||
const props = defineProps<PopoverRootProps>()
|
||||
const emits = defineEmits<PopoverRootEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</PopoverRoot>
|
||||
</template>
|
||||
48
src/components/ui/popover/PopoverContent.vue
Normal file
48
src/components/ui/popover/PopoverContent.vue
Normal file
@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
PopoverContent,
|
||||
type PopoverContentEmits,
|
||||
type PopoverContentProps,
|
||||
PopoverPortal,
|
||||
useForwardPropsEmits,
|
||||
} from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<PopoverContentProps & { class?: HTMLAttributes['class'] }>(),
|
||||
{
|
||||
align: 'center',
|
||||
sideOffset: 4,
|
||||
},
|
||||
)
|
||||
const emits = defineEmits<PopoverContentEmits>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverPortal>
|
||||
<PopoverContent
|
||||
v-bind="{ ...forwarded, ...$attrs }"
|
||||
:class="
|
||||
cn(
|
||||
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</PopoverContent>
|
||||
</PopoverPortal>
|
||||
</template>
|
||||
11
src/components/ui/popover/PopoverTrigger.vue
Normal file
11
src/components/ui/popover/PopoverTrigger.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { PopoverTrigger, type PopoverTriggerProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<PopoverTriggerProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverTrigger v-bind="props">
|
||||
<slot />
|
||||
</PopoverTrigger>
|
||||
</template>
|
||||
3
src/components/ui/popover/index.ts
Normal file
3
src/components/ui/popover/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as Popover } from './Popover.vue'
|
||||
export { default as PopoverContent } from './PopoverContent.vue'
|
||||
export { default as PopoverTrigger } from './PopoverTrigger.vue'
|
||||
15
src/components/ui/select/Select.vue
Normal file
15
src/components/ui/select/Select.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectRootEmits, SelectRootProps } from 'radix-vue'
|
||||
import { SelectRoot, useForwardPropsEmits } from 'radix-vue'
|
||||
|
||||
const props = defineProps<SelectRootProps>()
|
||||
const emits = defineEmits<SelectRootEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</SelectRoot>
|
||||
</template>
|
||||
53
src/components/ui/select/SelectContent.vue
Normal file
53
src/components/ui/select/SelectContent.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
SelectContent,
|
||||
type SelectContentEmits,
|
||||
type SelectContentProps,
|
||||
SelectPortal,
|
||||
SelectViewport,
|
||||
useForwardPropsEmits,
|
||||
} from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
import { SelectScrollDownButton, SelectScrollUpButton } from '.'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<SelectContentProps & { class?: HTMLAttributes['class'] }>(),
|
||||
{
|
||||
position: 'popper',
|
||||
},
|
||||
)
|
||||
const emits = defineEmits<SelectContentEmits>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectPortal>
|
||||
<SelectContent
|
||||
v-bind="{ ...forwarded, ...$attrs }" :class="cn(
|
||||
'relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
position === 'popper'
|
||||
&& 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectViewport :class="cn('p-1', position === 'popper' && 'h-[--radix-select-trigger-height] w-full min-w-[--radix-select-trigger-width]')">
|
||||
<slot />
|
||||
</SelectViewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectContent>
|
||||
</SelectPortal>
|
||||
</template>
|
||||
19
src/components/ui/select/SelectGroup.vue
Normal file
19
src/components/ui/select/SelectGroup.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import { SelectGroup, type SelectGroupProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<SelectGroupProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectGroup :class="cn('p-1 w-full', props.class)" v-bind="delegatedProps">
|
||||
<slot />
|
||||
</SelectGroup>
|
||||
</template>
|
||||
44
src/components/ui/select/SelectItem.vue
Normal file
44
src/components/ui/select/SelectItem.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Check } from 'lucide-vue-next'
|
||||
import {
|
||||
SelectItem,
|
||||
SelectItemIndicator,
|
||||
type SelectItemProps,
|
||||
SelectItemText,
|
||||
useForwardProps,
|
||||
} from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<SelectItemProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItem
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<span class="absolute left-2 h-3.5 w-3.5 flex items-center justify-center">
|
||||
<SelectItemIndicator>
|
||||
<Check class="h-4 w-4" />
|
||||
</SelectItemIndicator>
|
||||
</span>
|
||||
|
||||
<SelectItemText>
|
||||
<slot />
|
||||
</SelectItemText>
|
||||
</SelectItem>
|
||||
</template>
|
||||
11
src/components/ui/select/SelectItemText.vue
Normal file
11
src/components/ui/select/SelectItemText.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { SelectItemText, type SelectItemTextProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<SelectItemTextProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItemText v-bind="props">
|
||||
<slot />
|
||||
</SelectItemText>
|
||||
</template>
|
||||
13
src/components/ui/select/SelectLabel.vue
Normal file
13
src/components/ui/select/SelectLabel.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { SelectLabel, type SelectLabelProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<SelectLabelProps & { class?: HTMLAttributes['class'] }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectLabel :class="cn('py-1.5 pl-8 pr-2 text-sm font-semibold', props.class)">
|
||||
<slot />
|
||||
</SelectLabel>
|
||||
</template>
|
||||
24
src/components/ui/select/SelectScrollDownButton.vue
Normal file
24
src/components/ui/select/SelectScrollDownButton.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import { ChevronDown } from 'lucide-vue-next'
|
||||
import { SelectScrollDownButton, type SelectScrollDownButtonProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<SelectScrollDownButtonProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectScrollDownButton v-bind="forwardedProps" :class="cn('flex cursor-default items-center justify-center py-1', props.class)">
|
||||
<slot>
|
||||
<ChevronDown class="h-4 w-4" />
|
||||
</slot>
|
||||
</SelectScrollDownButton>
|
||||
</template>
|
||||
24
src/components/ui/select/SelectScrollUpButton.vue
Normal file
24
src/components/ui/select/SelectScrollUpButton.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import { ChevronUp } from 'lucide-vue-next'
|
||||
import { SelectScrollUpButton, type SelectScrollUpButtonProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<SelectScrollUpButtonProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectScrollUpButton v-bind="forwardedProps" :class="cn('flex cursor-default items-center justify-center py-1', props.class)">
|
||||
<slot>
|
||||
<ChevronUp class="h-4 w-4" />
|
||||
</slot>
|
||||
</SelectScrollUpButton>
|
||||
</template>
|
||||
17
src/components/ui/select/SelectSeparator.vue
Normal file
17
src/components/ui/select/SelectSeparator.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import { SelectSeparator, type SelectSeparatorProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<SelectSeparatorProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectSeparator v-bind="delegatedProps" :class="cn('-mx-1 my-1 h-px bg-muted', props.class)" />
|
||||
</template>
|
||||
31
src/components/ui/select/SelectTrigger.vue
Normal file
31
src/components/ui/select/SelectTrigger.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import { ChevronDown } from 'lucide-vue-next'
|
||||
import { SelectIcon, SelectTrigger, type SelectTriggerProps, useForwardProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<SelectTriggerProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectTrigger
|
||||
v-bind="forwardedProps"
|
||||
:class="cn(
|
||||
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:truncate text-start',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
<SelectIcon as-child>
|
||||
<ChevronDown class="h-4 w-4 shrink-0 opacity-50" />
|
||||
</SelectIcon>
|
||||
</SelectTrigger>
|
||||
</template>
|
||||
11
src/components/ui/select/SelectValue.vue
Normal file
11
src/components/ui/select/SelectValue.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { SelectValue, type SelectValueProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<SelectValueProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectValue v-bind="props">
|
||||
<slot />
|
||||
</SelectValue>
|
||||
</template>
|
||||
11
src/components/ui/select/index.ts
Normal file
11
src/components/ui/select/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export { default as Select } from './Select.vue'
|
||||
export { default as SelectContent } from './SelectContent.vue'
|
||||
export { default as SelectGroup } from './SelectGroup.vue'
|
||||
export { default as SelectItem } from './SelectItem.vue'
|
||||
export { default as SelectItemText } from './SelectItemText.vue'
|
||||
export { default as SelectLabel } from './SelectLabel.vue'
|
||||
export { default as SelectScrollDownButton } from './SelectScrollDownButton.vue'
|
||||
export { default as SelectScrollUpButton } from './SelectScrollUpButton.vue'
|
||||
export { default as SelectSeparator } from './SelectSeparator.vue'
|
||||
export { default as SelectTrigger } from './SelectTrigger.vue'
|
||||
export { default as SelectValue } from './SelectValue.vue'
|
||||
24
src/components/ui/textarea/Textarea.vue
Normal file
24
src/components/ui/textarea/Textarea.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
defaultValue?: string | number
|
||||
modelValue?: string | number
|
||||
}>()
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:modelValue', payload: string | number): void
|
||||
}>()
|
||||
|
||||
const modelValue = useVModel(props, 'modelValue', emits, {
|
||||
passive: true,
|
||||
defaultValue: props.defaultValue,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<textarea v-model="modelValue" :class="cn('flex min-h-20 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', props.class)" />
|
||||
</template>
|
||||
1
src/components/ui/textarea/index.ts
Normal file
1
src/components/ui/textarea/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Textarea } from './Textarea.vue'
|
||||
14
src/components/ui/tooltip/Tooltip.vue
Normal file
14
src/components/ui/tooltip/Tooltip.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { TooltipRoot, type TooltipRootEmits, type TooltipRootProps, useForwardPropsEmits } from 'radix-vue'
|
||||
|
||||
const props = defineProps<TooltipRootProps>()
|
||||
const emits = defineEmits<TooltipRootEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</TooltipRoot>
|
||||
</template>
|
||||
31
src/components/ui/tooltip/TooltipContent.vue
Normal file
31
src/components/ui/tooltip/TooltipContent.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import { TooltipContent, type TooltipContentEmits, type TooltipContentProps, TooltipPortal, useForwardPropsEmits } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const props = withDefaults(defineProps<TooltipContentProps & { class?: HTMLAttributes['class'] }>(), {
|
||||
sideOffset: 4,
|
||||
})
|
||||
|
||||
const emits = defineEmits<TooltipContentEmits>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipPortal>
|
||||
<TooltipContent v-bind="{ ...forwarded, ...$attrs }" :class="cn('z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', props.class)">
|
||||
<slot />
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</template>
|
||||
11
src/components/ui/tooltip/TooltipProvider.vue
Normal file
11
src/components/ui/tooltip/TooltipProvider.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { TooltipProvider, type TooltipProviderProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<TooltipProviderProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipProvider v-bind="props">
|
||||
<slot />
|
||||
</TooltipProvider>
|
||||
</template>
|
||||
11
src/components/ui/tooltip/TooltipTrigger.vue
Normal file
11
src/components/ui/tooltip/TooltipTrigger.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { TooltipTrigger, type TooltipTriggerProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<TooltipTriggerProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipTrigger v-bind="props">
|
||||
<slot />
|
||||
</TooltipTrigger>
|
||||
</template>
|
||||
4
src/components/ui/tooltip/index.ts
Normal file
4
src/components/ui/tooltip/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export { default as Tooltip } from './Tooltip.vue'
|
||||
export { default as TooltipContent } from './TooltipContent.vue'
|
||||
export { default as TooltipProvider } from './TooltipProvider.vue'
|
||||
export { default as TooltipTrigger } from './TooltipTrigger.vue'
|
||||
5
src/composables/baseFetch.ts
Normal file
5
src/composables/baseFetch.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { createFetch } from '@vueuse/core'
|
||||
|
||||
export const baseFetch = createFetch({
|
||||
baseUrl: import.meta.env.VITE_BACKEND_URL,
|
||||
})
|
||||
14
src/composables/dateOpt.ts
Normal file
14
src/composables/dateOpt.ts
Normal file
@ -0,0 +1,14 @@
|
||||
function _useOptions() {
|
||||
const options = reactive({
|
||||
castBeginDate: '2022-01-01',
|
||||
castDayNo: 0,
|
||||
renderMode: 'heatmap' as 'heatmap' | 'image',
|
||||
isPlaying: false,
|
||||
})
|
||||
|
||||
return {
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
export const useOptions = createSharedComposable(() => _useOptions())
|
||||
@ -1,12 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
const router = useRouter()
|
||||
onMounted(() => {
|
||||
router.push('/map')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main
|
||||
px-4 py-10
|
||||
text="center gray-700 dark:gray-200"
|
||||
>
|
||||
<RouterView />
|
||||
<TheFooter />
|
||||
<div mx-auto mt-5 text-center text-sm opacity-50>
|
||||
[Default Layout]
|
||||
<div class="grid h-screen w-full">
|
||||
<div class="flex flex-col">
|
||||
<header
|
||||
class="sticky top-0 z-10 h-[53px] flex items-center gap-1 border-b bg-background px-4"
|
||||
>
|
||||
<h1 class="text-xl font-semibold">
|
||||
次季节台风预报系统
|
||||
</h1>
|
||||
</header>
|
||||
<main
|
||||
class="grid flex-1 gap-4 overflow-auto p-4 lg:grid-cols-3 md:grid-cols-2"
|
||||
>
|
||||
<RouterView />
|
||||
</main>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
233
src/layouts/map.vue
Normal file
233
src/layouts/map.vue
Normal file
@ -0,0 +1,233 @@
|
||||
<script setup lang="ts">
|
||||
import { useOptions } from '@/composables/dateOpt'
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
DateFormatter,
|
||||
type DateValue,
|
||||
getLocalTimeZone,
|
||||
isSameDay,
|
||||
parseDate,
|
||||
} from '@internationalized/date'
|
||||
import { DrawerTrigger } from 'vaul-vue'
|
||||
|
||||
const [DefineFormTemplate, ReuseFormTemplate] = createReusableTemplate()
|
||||
const df = new DateFormatter('en-US', {
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
const castBeginDate = ref<DateValue>()
|
||||
const castTargetDate = ref<DateValue>()
|
||||
|
||||
const { options } = useOptions()
|
||||
|
||||
const mapping = ref < Record<string, string>>({})
|
||||
|
||||
onMounted(async () => {
|
||||
const resp = await baseFetch('/tc/metadata').json()
|
||||
const data = resp.data.value.data
|
||||
mapping.value = data
|
||||
})
|
||||
|
||||
const isDateDisabled = computed(() => {
|
||||
const availableDates = Object.keys(mapping.value)
|
||||
.map((date: string) => {
|
||||
// date is like 20020202, we need to first trans to 2002-02-02
|
||||
try {
|
||||
const newDateStr = `${date.slice(0, 4)}-${date.slice(4, 6)}-${date.slice(6, 8)}`
|
||||
const newDate = parseDate(newDateStr)
|
||||
return newDate
|
||||
}
|
||||
catch {
|
||||
return null
|
||||
}
|
||||
})
|
||||
.filter(date => date !== null) as DateValue[]
|
||||
function _isDateDisabled(date: DateValue): boolean {
|
||||
const hasMatch = availableDates.some((thisDate) => {
|
||||
return isSameDay(date, thisDate)
|
||||
})
|
||||
return !hasMatch
|
||||
}
|
||||
return _isDateDisabled
|
||||
})
|
||||
|
||||
const isTargetDateDisabled = computed(() => {
|
||||
const castBegin = castBeginDate.value
|
||||
function _functionIsTargetDateDisabled(date: DateValue): boolean {
|
||||
if (!castBegin) {
|
||||
return true
|
||||
}
|
||||
const castEnd = castBegin.add({ days: 29 })
|
||||
return date.compare(castBegin) < 0 || date.compare(castEnd) > 0
|
||||
}
|
||||
return _functionIsTargetDateDisabled
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid h-screen w-full">
|
||||
<DefineFormTemplate>
|
||||
<form class="grid w-full items-start gap-6">
|
||||
<fieldset class="grid gap-6 border rounded-lg p-4">
|
||||
<legend class="px-1 text-sm font-medium -ml-1">
|
||||
日期设置
|
||||
</legend>
|
||||
<div class="grid gap-3">
|
||||
<Label for="model">渲染模式</Label>
|
||||
<Select v-model="options.renderMode">
|
||||
<SelectTrigger
|
||||
id="model"
|
||||
class="items-start [&_[data-description]]:hidden"
|
||||
>
|
||||
<SelectValue placeholder="Select a model" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="heatmap">
|
||||
<div class="flex items-start gap-3 text-muted-foreground">
|
||||
<Rabbit class="size-5" />
|
||||
<div class="grid gap-0.5">
|
||||
<p>
|
||||
热图
|
||||
<span class="text-foreground font-medium">
|
||||
使用点状热力图渲染
|
||||
</span>
|
||||
</p>
|
||||
<p class="text-xs" data-description>
|
||||
放大缩小可能不准确
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="image">
|
||||
<div class="flex items-start gap-3 text-muted-foreground">
|
||||
<Bird class="size-5" />
|
||||
<div class="grid gap-0.5">
|
||||
<p>
|
||||
图片
|
||||
<span class="text-foreground font-medium">
|
||||
使用图片叠加层渲染
|
||||
</span>
|
||||
</p>
|
||||
<p class="text-xs" data-description>
|
||||
相对准确
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label for="temperature">发布日期</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
variant="outline"
|
||||
:class="cn(
|
||||
'w-full justify-start text-left font-normal',
|
||||
!castBeginDate && 'text-muted-foreground',
|
||||
)"
|
||||
>
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
{{ castBeginDate ? df.format(castBeginDate.toDate(getLocalTimeZone())) : "Pick a date" }}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0">
|
||||
<Calendar v-model="castBeginDate" initial-focus :is-date-disabled="isDateDisabled" />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div>
|
||||
<Label for="top-p">目标预报日期</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
variant="outline"
|
||||
:class="cn(
|
||||
'w-full justify-start text-left font-normal',
|
||||
!castTargetDate && 'text-muted-foreground',
|
||||
)"
|
||||
>
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
{{ castTargetDate ? df.format(castTargetDate.toDate(getLocalTimeZone())) : "Pick a date" }}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0">
|
||||
<Calendar
|
||||
v-model="castTargetDate" initial-focus
|
||||
:is-date-disabled="isTargetDateDisabled"
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-3" />
|
||||
</fieldset>
|
||||
<fieldset class="grid gap-6 border rounded-lg p-4">
|
||||
<legend class="px-1 text-sm font-medium -ml-1">
|
||||
渲染
|
||||
</legend>
|
||||
<Button>
|
||||
开始自动播放
|
||||
</Button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</DefineFormTemplate>
|
||||
<div class="flex flex-col">
|
||||
<header
|
||||
class="sticky top-0 z-10 h-[53px] flex items-center gap-1 border-b bg-background px-4"
|
||||
>
|
||||
<h1 class="text-xl font-semibold">
|
||||
次季节台风预报系统
|
||||
</h1>
|
||||
|
||||
<Drawer>
|
||||
<DrawerTrigger as-child>
|
||||
<Button variant="ghost" size="icon" class="md:hidden">
|
||||
<Settings class="size-4" />
|
||||
<span class="sr-only">地图设置</span>
|
||||
</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent class="max-h-[80vh]">
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>地图设置</DrawerTitle>
|
||||
<DrawerDescription>
|
||||
选择日期和渲染方式
|
||||
</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
<ReuseFormTemplate />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</header>
|
||||
<main
|
||||
class="grid flex-1 gap-4 overflow-auto p-4 lg:grid-cols-3 md:grid-cols-2"
|
||||
>
|
||||
<div class="relative hidden flex-col items-start gap-8 md:flex">
|
||||
<ReuseFormTemplate />
|
||||
</div>
|
||||
<div
|
||||
class="relative h-full min-h-[50vh] flex flex-col rounded-xl bg-muted/50 p-4 lg:col-span-2"
|
||||
>
|
||||
<Badge variant="outline" class="absolute left-3 top-3 z-20 bg-slate-100">
|
||||
地图
|
||||
</Badge>
|
||||
<div class="flex-1" />
|
||||
<RouterView />
|
||||
<form
|
||||
class="relative overflow-hidden border rounded-lg bg-background focus-within:ring-1 focus-within:ring-ring"
|
||||
>
|
||||
<Label for="message" class="sr-only"> Message </Label>
|
||||
<Card
|
||||
id="message"
|
||||
placeholder="Type your message here..."
|
||||
class="min-h-22 resize-none border-0 p-3 shadow-none focus-visible:ring-0"
|
||||
>
|
||||
something here
|
||||
</Card>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -3,7 +3,7 @@
|
||||
A custom user module system. Place a `.ts` file with the following template, it will be installed automatically.
|
||||
|
||||
```ts
|
||||
import type { UserModule } from '~/types'
|
||||
import type { UserModule } from '@/types'
|
||||
|
||||
export const install: UserModule = ({ app, router, isClient }) => {
|
||||
// do something
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { UserModule } from '@/types'
|
||||
import type { Locale } from 'vue-i18n'
|
||||
import type { UserModule } from '~/types'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
// Import i18n resources
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { UserModule } from '~/types'
|
||||
import type { UserModule } from '@/types'
|
||||
import NProgress from 'nprogress'
|
||||
|
||||
export const install: UserModule = ({ isClient, router }) => {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { UserModule } from '~/types'
|
||||
import type { UserModule } from '@/types'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
// Setup Pinia
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { UserModule } from '~/types'
|
||||
import type { UserModule } from '@/types'
|
||||
|
||||
// https://github.com/antfu/vite-plugin-pwa#automatic-reload-when-new-content-available
|
||||
export const install: UserModule = ({ isClient, router }) => {
|
||||
|
||||
@ -5,7 +5,7 @@ Check out [`unplugin-vue-router`](https://github.com/posva/unplugin-vue-router)
|
||||
|
||||
### Path Aliasing
|
||||
|
||||
`~/` is aliased to `./src/` folder.
|
||||
`@/` is aliased to `./src/` folder.
|
||||
|
||||
For example, instead of having
|
||||
|
||||
@ -16,5 +16,5 @@ import { isDark } from '../../../../composables'
|
||||
now, you can use
|
||||
|
||||
```ts
|
||||
import { isDark } from '~/composables'
|
||||
import { isDark } from '@/composables'
|
||||
```
|
||||
|
||||
@ -52,5 +52,5 @@ const { t } = useI18n()
|
||||
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
layout: home
|
||||
layout: default
|
||||
</route>
|
||||
|
||||
34
src/pages/map.vue
Normal file
34
src/pages/map.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
layout: map
|
||||
</route>
|
||||
|
||||
<script setup lang="ts">
|
||||
import AMapLoader from '@amap/amap-jsapi-loader'
|
||||
|
||||
const amapInstance = ref(null)
|
||||
const map = ref(null)
|
||||
|
||||
const apiKey = import.meta.env.VITE_AMAP_API_KEY
|
||||
onMounted(async () => {
|
||||
const AMap = await AMapLoader.load({
|
||||
key: apiKey,
|
||||
version: '2.0',
|
||||
plugins: [''],
|
||||
})
|
||||
amapInstance.value = AMap
|
||||
|
||||
map.value = new AMap.Map('amap-container', {
|
||||
zoom: 5,
|
||||
center: [130, 17],
|
||||
// layers: [
|
||||
/* new AMap.TileLayer.Satellite() */
|
||||
// new AMap.TileLayer.RoadNet(),
|
||||
// ],
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="amap-container" class="m-0 h-full w-full p-0" />
|
||||
</template>
|
||||
1
src/typed-router.d.ts
vendored
1
src/typed-router.d.ts
vendored
@ -22,6 +22,7 @@ declare module 'vue-router/auto-routes' {
|
||||
'/[...all]': RouteRecordInfo<'/[...all]', '/:all(.*)', { all: ParamValue<true> }, { all: ParamValue<false> }>,
|
||||
'/about': RouteRecordInfo<'/about', '/about', Record<never, never>, Record<never, never>>,
|
||||
'/hi/[name]': RouteRecordInfo<'/hi/[name]', '/hi/:name', { name: ParamValue<true> }, { name: ParamValue<false> }>,
|
||||
'/map': RouteRecordInfo<'/map', '/map', Record<never, never>, Record<never, never>>,
|
||||
'/README': RouteRecordInfo<'/README', '/README', Record<never, never>, Record<never, never>>,
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"paths": {
|
||||
"~/*": ["src/*"]
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"resolveJsonModule": true,
|
||||
"types": [
|
||||
|
||||
@ -31,6 +31,7 @@ export default defineConfig({
|
||||
include: [/\.vue$/, /\.md$/],
|
||||
}),
|
||||
},
|
||||
betterDefine: false,
|
||||
}),
|
||||
|
||||
// https://github.com/posva/unplugin-vue-router
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user