[Feature]: Sheet (#23)

* fix: fix alert dialog missing emits

* feat: add sheet component
This commit is contained in:
Ahmed Mayara 2023-09-02 15:36:45 +01:00 committed by GitHub
parent b34e1dbc62
commit 309c87a293
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 264 additions and 12 deletions

View File

@ -26,6 +26,7 @@
"@iconify-json/tabler": "^1.1.89", "@iconify-json/tabler": "^1.1.89",
"@iconify/json": "^2.2.108", "@iconify/json": "^2.2.108",
"@iconify/vue": "^4.1.1", "@iconify/vue": "^4.1.1",
"@types/node": "^20.5.7",
"@vitejs/plugin-vue": "^4.1.0", "@vitejs/plugin-vue": "^4.1.0",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"postcss": "^8.4.24", "postcss": "^8.4.24",

View File

@ -1,11 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { type AlertDialogProps, AlertDialogRoot } from 'radix-vue' import { type AlertDialogEmits, type AlertDialogProps, AlertDialogRoot } from 'radix-vue'
import { useEmitAsProps } from '@/lib/utils'
const props = defineProps<AlertDialogProps>() const props = defineProps<AlertDialogProps>()
const emits = defineEmits<AlertDialogEmits>()
const emitsAsProps = useEmitAsProps(emits)
</script> </script>
<template> <template>
<AlertDialogRoot :default-open="props.defaultOpen" :open="props.open"> <AlertDialogRoot v-bind="{ ...props, ...emitsAsProps }">
<slot /> <slot />
</AlertDialogRoot> </AlertDialogRoot>
</template> </template>

View File

@ -1,22 +1,27 @@
<script setup lang="ts"> <script setup lang="ts">
import { import {
AlertDialogContent, AlertDialogContent,
type AlertDialogContentEmits,
type AlertDialogContentProps, type AlertDialogContentProps,
AlertDialogOverlay, AlertDialogOverlay,
AlertDialogPortal, AlertDialogPortal,
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn, useEmitAsProps } from '@/lib/utils'
const props = defineProps<AlertDialogContentProps & { class?: string }>() const props = defineProps<AlertDialogContentProps & { class?: string }>()
const emits = defineEmits<AlertDialogContentEmits>()
const emitsAsProps = useEmitAsProps(emits)
</script> </script>
<template> <template>
<AlertDialogPortal> <AlertDialogPortal>
<AlertDialogOverlay <AlertDialogOverlay
class="fixed inset-0 z-50 bg-white/80 dark:bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" class="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
/> />
<AlertDialogContent <AlertDialogContent
v-bind="props" v-bind="{ ...props, ...emitsAsProps }"
:class=" :class="
cn( cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full', 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full',

View File

@ -0,0 +1,9 @@
<script setup lang="ts">
import { DialogRoot } from 'radix-vue'
</script>
<template>
<DialogRoot>
<slot />
</DialogRoot>
</template>

View File

@ -0,0 +1,11 @@
<script setup lang="ts">
import { DialogClose, type DialogCloseProps } from 'radix-vue'
const props = defineProps<DialogCloseProps>()
</script>
<template>
<DialogClose v-bind="props">
<slot />
</DialogClose>
</template>

View File

@ -0,0 +1,63 @@
<script setup lang="ts">
import {
DialogClose,
DialogContent,
type DialogContentEmits,
type DialogContentProps,
DialogOverlay,
DialogPortal,
} from 'radix-vue'
import { X } from 'lucide-vue-next'
import { cva } from 'class-variance-authority'
import { cn, useEmitAsProps } from '@/lib/utils'
interface SheetContentProps extends DialogContentProps {
side?: 'left' | 'right' | 'top' | 'bottom'
class?: string
}
const props = defineProps<SheetContentProps>()
const emits = defineEmits<DialogContentEmits>()
const emitsAsProps = useEmitAsProps(emits)
const sheetVariants = cva(
'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
{
variants: {
side: {
top: 'inset-x-0 top-0 border-b border-border data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
bottom:
'inset-x-0 bottom-0 border-t border-border data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
left: 'inset-y-0 left-0 h-full w-3/4 border-r border-border data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
right:
'inset-y-0 right-0 h-full w-3/4 border-l border-border data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
},
},
defaultVariants: {
side: 'right',
},
},
)
</script>
<template>
<DialogPortal>
<DialogOverlay
class="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
/>
<DialogContent
:class="cn(sheetVariants({ side: props.side }), props.class)"
v-bind="{ ...props, ...emitsAsProps }"
>
<slot />
<DialogClose
class="absolute top-4 right-4 p-0.5 transition-colors rounded-md hover:bg-secondary"
>
<X class="w-4 h-4 text-muted-foreground" />
</DialogClose>
</DialogContent>
</DialogPortal>
</template>

View File

@ -0,0 +1,15 @@
<script setup lang="ts">
import { DialogDescription, type DialogDescriptionProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<DialogDescriptionProps & { class?: string }>()
</script>
<template>
<DialogDescription
:class="cn('text-sm text-muted-foreground', props.class)"
v-bind="props"
>
<slot />
</DialogDescription>
</template>

View File

@ -0,0 +1,18 @@
<script setup lang="ts">
import { cn } from '@/lib/utils'
const props = defineProps<{ class?: string }>()
</script>
<template>
<div
:class="
cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
props.class,
)
"
>
<slot />
</div>
</template>

View File

@ -0,0 +1,15 @@
<script setup lang="ts">
import { cn } from '@/lib/utils'
const props = defineProps<{ class?: string }>()
</script>
<template>
<div
:class="
cn('flex flex-col space-y-2 text-center sm:text-left', props.class)
"
>
<slot />
</div>
</template>

View File

@ -0,0 +1,15 @@
<script setup lang="ts">
import { DialogTitle, type DialogTitleProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<DialogTitleProps & { class?: string }>()
</script>
<template>
<DialogTitle
:class="cn('text-lg font-semibold text-foreground', props.class)"
v-bind="props"
>
<slot />
</DialogTitle>
</template>

View File

@ -0,0 +1,11 @@
<script setup lang="ts">
import { DialogTrigger, type DialogTriggerProps } from 'radix-vue'
const props = defineProps<DialogTriggerProps>()
</script>
<template>
<DialogTrigger v-bind="props">
<slot />
</DialogTrigger>
</template>

View File

@ -0,0 +1,8 @@
export { default as Sheet } from './Sheet.vue'
export { default as SheetTrigger } from './SheetTrigger.vue'
export { default as SheetClose } from './SheetClose.vue'
export { default as SheetContent } from './SheetContent.vue'
export { default as SheetHeader } from './SheetHeader.vue'
export { default as SheetTitle } from './SheetTitle.vue'
export { default as SheetDescription } from './SheetDescription.vue'
export { default as SheetFooter } from './SheetFooter.vue'

View File

@ -1,5 +1,5 @@
{ {
"include": ["/**/*.vue", ".vitepress/**/*.vue", "/**/*.ts", ".vitepress/**/*.mts"], "include": ["/**/*.vue", ".vitepress/**/*.vue", "/**/*.ts", ".vitepress/**/*.mts", "src/lib/**/*"],
"compilerOptions": { "compilerOptions": {
"target": "esnext", "target": "esnext",
"module": "esnext", "module": "esnext",

View File

@ -64,7 +64,7 @@ importers:
version: 1.0.6(tailwindcss@3.3.2) version: 1.0.6(tailwindcss@3.3.2)
vitepress: vitepress:
specifier: ^1.0.0-rc.10 specifier: ^1.0.0-rc.10
version: 1.0.0-rc.10(@algolia/client-search@4.19.1)(@types/node@20.4.7)(search-insights@2.7.0) version: 1.0.0-rc.10(@algolia/client-search@4.19.1)(@types/node@20.5.7)(search-insights@2.7.0)
vue: vue:
specifier: ^3.3.4 specifier: ^3.3.4
version: 3.3.4 version: 3.3.4
@ -81,6 +81,9 @@ importers:
'@iconify/vue': '@iconify/vue':
specifier: ^4.1.1 specifier: ^4.1.1
version: 4.1.1(vue@3.3.4) version: 4.1.1(vue@3.3.4)
'@types/node':
specifier: ^20.5.7
version: 20.5.7
'@vitejs/plugin-vue': '@vitejs/plugin-vue':
specifier: ^4.1.0 specifier: ^4.1.0
version: 4.1.0(vite@4.3.9)(vue@3.3.4) version: 4.1.0(vite@4.3.9)(vue@3.3.4)
@ -107,7 +110,7 @@ importers:
version: 0.16.6 version: 0.16.6
vite: vite:
specifier: ^4.3.9 specifier: ^4.3.9
version: 4.3.9(@types/node@20.4.7) version: 4.3.9(@types/node@20.5.7)
vue-tsc: vue-tsc:
specifier: ^1.4.2 specifier: ^1.4.2
version: 1.4.2(typescript@5.0.2) version: 1.4.2(typescript@5.0.2)
@ -1852,6 +1855,9 @@ packages:
/@types/node@20.4.7: /@types/node@20.4.7:
resolution: {integrity: sha512-bUBrPjEry2QUTsnuEjzjbS7voGWCc30W0qzgMf90GPeDGFRakvrz47ju+oqDAKCXLUCe39u57/ORMl/O/04/9g==} resolution: {integrity: sha512-bUBrPjEry2QUTsnuEjzjbS7voGWCc30W0qzgMf90GPeDGFRakvrz47ju+oqDAKCXLUCe39u57/ORMl/O/04/9g==}
/@types/node@20.5.7:
resolution: {integrity: sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==}
/@types/normalize-package-data@2.4.1: /@types/normalize-package-data@2.4.1:
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
dev: true dev: true
@ -2015,7 +2021,7 @@ packages:
'@babel/core': 7.22.10 '@babel/core': 7.22.10
'@babel/plugin-transform-typescript': 7.22.10(@babel/core@7.22.10) '@babel/plugin-transform-typescript': 7.22.10(@babel/core@7.22.10)
'@vue/babel-plugin-jsx': 1.1.5(@babel/core@7.22.10) '@vue/babel-plugin-jsx': 1.1.5(@babel/core@7.22.10)
vite: 4.3.9(@types/node@20.4.7) vite: 4.3.9(@types/node@20.5.7)
vue: 3.3.4 vue: 3.3.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -2028,7 +2034,7 @@ packages:
vite: ^4.0.0 vite: ^4.0.0
vue: ^3.2.25 vue: ^3.2.25
dependencies: dependencies:
vite: 4.3.9(@types/node@20.4.7) vite: 4.3.9(@types/node@20.5.7)
vue: 3.3.4 vue: 3.3.4
dev: true dev: true
@ -6554,6 +6560,39 @@ packages:
rollup: 3.28.1 rollup: 3.28.1
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
dev: true
/vite@4.3.9(@types/node@20.5.7):
resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
'@types/node': '>= 14'
less: '*'
sass: '*'
stylus: '*'
sugarss: '*'
terser: ^5.4.0
peerDependenciesMeta:
'@types/node':
optional: true
less:
optional: true
sass:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
dependencies:
'@types/node': 20.5.7
esbuild: 0.17.19
postcss: 8.4.24
rollup: 3.28.1
optionalDependencies:
fsevents: 2.3.3
/vite@4.4.9(@types/node@20.4.7): /vite@4.4.9(@types/node@20.4.7):
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
@ -6589,8 +6628,45 @@ packages:
rollup: 3.28.1 rollup: 3.28.1
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
dev: true
/vitepress@1.0.0-rc.10(@algolia/client-search@4.19.1)(@types/node@20.4.7)(search-insights@2.7.0): /vite@4.4.9(@types/node@20.5.7):
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
'@types/node': '>= 14'
less: '*'
lightningcss: ^1.21.0
sass: '*'
stylus: '*'
sugarss: '*'
terser: ^5.4.0
peerDependenciesMeta:
'@types/node':
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
dependencies:
'@types/node': 20.5.7
esbuild: 0.18.20
postcss: 8.4.28
rollup: 3.28.1
optionalDependencies:
fsevents: 2.3.3
dev: false
/vitepress@1.0.0-rc.10(@algolia/client-search@4.19.1)(@types/node@20.5.7)(search-insights@2.7.0):
resolution: {integrity: sha512-+MsahIWqq5WUEmj6MR4obcKYbT7im07jZPCQPdNJExkeOSbOAJ4xypSLx88x7rvtzWHhHc5aXbOhCRvGEGjFrw==} resolution: {integrity: sha512-+MsahIWqq5WUEmj6MR4obcKYbT7im07jZPCQPdNJExkeOSbOAJ4xypSLx88x7rvtzWHhHc5aXbOhCRvGEGjFrw==}
hasBin: true hasBin: true
dependencies: dependencies:
@ -6603,7 +6679,7 @@ packages:
mark.js: 8.11.1 mark.js: 8.11.1
minisearch: 6.1.0 minisearch: 6.1.0
shiki: 0.14.3 shiki: 0.14.3
vite: 4.4.9(@types/node@20.4.7) vite: 4.4.9(@types/node@20.5.7)
vue: 3.3.4 vue: 3.3.4
transitivePeerDependencies: transitivePeerDependencies:
- '@algolia/client-search' - '@algolia/client-search'