feat(dialog-with-scroll-overlay): add demos with overlay scroll for Dialog component

This commit is contained in:
hrynevych.romann 2024-01-17 22:32:41 +02:00
parent af804cf110
commit 0eb0677dc3
11 changed files with 203 additions and 4 deletions

View File

@ -383,10 +383,17 @@ export const Index = {
DialogScrollContentDemo: { DialogScrollContentDemo: {
name: 'DialogScrollContentDemo', name: 'DialogScrollContentDemo',
type: 'components:example', type: 'components:example',
registryDependencies: ['button', 'dialog', 'input', 'label'], registryDependencies: ['button', 'dialog'],
component: () => import('../src/lib/registry/default/example/DialogScrollContentDemo.vue').then(m => m.default), component: () => import('../src/lib/registry/default/example/DialogScrollContentDemo.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/DialogScrollContentDemo.vue'], files: ['../src/lib/registry/default/example/DialogScrollContentDemo.vue'],
}, },
DialogScrollOverlayDemo: {
name: 'DialogScrollOverlayDemo',
type: 'components:example',
registryDependencies: ['button', 'dialog'],
component: () => import('../src/lib/registry/default/example/DialogScrollOverlayDemo.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/DialogScrollOverlayDemo.vue'],
},
DropdownMenuDemo: { DropdownMenuDemo: {
name: 'DropdownMenuDemo', name: 'DropdownMenuDemo',
type: 'components:example', type: 'components:example',
@ -1274,10 +1281,17 @@ export const Index = {
DialogScrollContentDemo: { DialogScrollContentDemo: {
name: 'DialogScrollContentDemo', name: 'DialogScrollContentDemo',
type: 'components:example', type: 'components:example',
registryDependencies: ['button', 'dialog', 'input', 'label'], registryDependencies: ['button', 'dialog'],
component: () => import('../src/lib/registry/new-york/example/DialogScrollContentDemo.vue').then(m => m.default), component: () => import('../src/lib/registry/new-york/example/DialogScrollContentDemo.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/DialogScrollContentDemo.vue'], files: ['../src/lib/registry/new-york/example/DialogScrollContentDemo.vue'],
}, },
DialogScrollOverlayDemo: {
name: 'DialogScrollOverlayDemo',
type: 'components:example',
registryDependencies: ['button', 'dialog'],
component: () => import('../src/lib/registry/new-york/example/DialogScrollOverlayDemo.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/DialogScrollOverlayDemo.vue'],
},
DropdownMenuDemo: { DropdownMenuDemo: {
name: 'DropdownMenuDemo', name: 'DropdownMenuDemo',
type: 'components:example', type: 'components:example',

View File

@ -59,6 +59,10 @@ import {
<ComponentPreview name="DialogScrollContentDemo" /> <ComponentPreview name="DialogScrollContentDemo" />
### Dialog with Scroll Overlay
<ComponentPreview name="DialogScrollOverlayDemo" />
## Notes ## Notes
To activate the `Dialog` component from within a `Context Menu` or `Dropdown Menu`, you must encase the `Context Menu` or `Dropdown Menu` component in the `Dialog` component. For more information, refer to the linked issue [here](https://github.com/radix-ui/primitives/issues/1836). To activate the `Dialog` component from within a `Context Menu` or `Dropdown Menu`, you must encase the `Context Menu` or `Dropdown Menu` component in the `Dialog` component. For more information, refer to the linked issue [here](https://github.com/radix-ui/primitives/issues/1836).

View File

@ -0,0 +1,40 @@
<script setup lang="ts">
import { Button } from '@/lib/registry/default/ui/button'
import {
Dialog,
DialogDescription,
DialogFooter,
DialogHeader,
DialogScrollContent,
DialogTitle,
DialogTrigger,
} from '@/lib/registry/default/ui/dialog'
</script>
<template>
<Dialog>
<DialogTrigger as-child>
<Button variant="outline">
Edit Profile
</Button>
</DialogTrigger>
<DialogScrollContent class="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Modal title</DialogTitle>
<DialogDescription>
Here is modal with overlay scroll
</DialogDescription>
</DialogHeader>
<div class="grid gap-4 py-4 h-[300dvh]">
<p>
This is some placeholder content to show the scrolling behavior for modals. Instead of repeating the text in the modal, we use an inline style to set a minimum height, thereby extending the length of the overall modal and demonstrating the overflow scrolling. When content becomes longer than the height of the viewport, scrolling will move the modal as needed.
</p>
</div>
<DialogFooter>
<Button type="submit">
Save changes
</Button>
</DialogFooter>
</DialogScrollContent>
</Dialog>
</template>

View File

@ -0,0 +1,45 @@
<script setup lang="ts">
import {
DialogClose,
DialogContent,
type DialogContentEmits,
type DialogContentProps,
DialogOverlay,
DialogPortal,
useEmitAsProps,
} from 'radix-vue'
import { X } from 'lucide-vue-next'
import { cn } from '@/lib/utils'
const props = defineProps<DialogContentProps & { class?: string }>()
const emits = defineEmits<DialogContentEmits>()
const emitsAsProps = useEmitAsProps(emits)
</script>
<template>
<DialogPortal>
<DialogOverlay
class="fixed inset-0 z-50 grid place-items-center overflow-y-auto 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(
'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-border bg-background p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',
props.class,
)
"
v-bind="{ ...props, ...emitsAsProps }"
>
<slot />
<DialogClose
class="absolute top-3 right-3 p-0.5 transition-colors rounded-md hover:bg-secondary"
>
<X class="w-4 h-4" />
<span class="sr-only">Close</span>
</DialogClose>
</DialogContent>
</DialogOverlay>
</DialogPortal>
</template>

View File

@ -5,4 +5,5 @@ export { default as DialogHeader } from './DialogHeader.vue'
export { default as DialogTitle } from './DialogTitle.vue' export { default as DialogTitle } from './DialogTitle.vue'
export { default as DialogDescription } from './DialogDescription.vue' export { default as DialogDescription } from './DialogDescription.vue'
export { default as DialogContent } from './DialogContent.vue' export { default as DialogContent } from './DialogContent.vue'
export { default as DialogScrollContent } from './DialogScrollContent.vue'
export { default as DialogFooter } from './DialogFooter.vue' export { default as DialogFooter } from './DialogFooter.vue'

View File

@ -0,0 +1,40 @@
<script setup lang="ts">
import { Button } from '@/lib/registry/new-york/ui/button'
import {
Dialog,
DialogDescription,
DialogFooter,
DialogHeader,
DialogScrollContent,
DialogTitle,
DialogTrigger,
} from '@/lib/registry/new-york/ui/dialog'
</script>
<template>
<Dialog>
<DialogTrigger as-child>
<Button variant="outline">
Edit Profile
</Button>
</DialogTrigger>
<DialogScrollContent class="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Modal title</DialogTitle>
<DialogDescription>
Here is modal with overlay scroll
</DialogDescription>
</DialogHeader>
<div class="grid gap-4 py-4 h-[300dvh]">
<p>
This is some placeholder content to show the scrolling behavior for modals. Instead of repeating the text in the modal, we use an inline style to set a minimum height, thereby extending the length of the overall modal and demonstrating the overflow scrolling. When content becomes longer than the height of the viewport, scrolling will move the modal as needed.
</p>
</div>
<DialogFooter>
<Button type="submit">
Save changes
</Button>
</DialogFooter>
</DialogScrollContent>
</Dialog>
</template>

View File

@ -0,0 +1,45 @@
<script setup lang="ts">
import {
DialogClose,
DialogContent,
type DialogContentEmits,
type DialogContentProps,
DialogOverlay,
DialogPortal,
useEmitAsProps,
} from 'radix-vue'
import { Cross2Icon } from '@radix-icons/vue'
import { cn } from '@/lib/utils'
const props = defineProps<DialogContentProps & { class?: string }>()
const emits = defineEmits<DialogContentEmits>()
const emitsAsProps = useEmitAsProps(emits)
</script>
<template>
<DialogPortal>
<DialogOverlay
class="fixed inset-0 z-50 grid place-items-center overflow-y-auto 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(
'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-border bg-background p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',
props.class,
)
"
v-bind="{ ...props, ...emitsAsProps }"
>
<slot />
<DialogClose
class="absolute top-4 right-4 p-0.5 transition-colors rounded-md hover:bg-secondary"
>
<Cross2Icon class="w-4 h-4" />
<span class="sr-only">Close</span>
</DialogClose>
</DialogContent>
</DialogOverlay>
</DialogPortal>
</template>

View File

@ -5,4 +5,5 @@ export { default as DialogHeader } from './DialogHeader.vue'
export { default as DialogTitle } from './DialogTitle.vue' export { default as DialogTitle } from './DialogTitle.vue'
export { default as DialogDescription } from './DialogDescription.vue' export { default as DialogDescription } from './DialogDescription.vue'
export { default as DialogContent } from './DialogContent.vue' export { default as DialogContent } from './DialogContent.vue'
export { default as DialogScrollContent } from './DialogScrollContent.vue'
export { default as DialogFooter } from './DialogFooter.vue' export { default as DialogFooter } from './DialogFooter.vue'

View File

@ -236,6 +236,7 @@
"ui/dialog/DialogDescription.vue", "ui/dialog/DialogDescription.vue",
"ui/dialog/DialogFooter.vue", "ui/dialog/DialogFooter.vue",
"ui/dialog/DialogHeader.vue", "ui/dialog/DialogHeader.vue",
"ui/dialog/DialogScrollContent.vue",
"ui/dialog/DialogTitle.vue", "ui/dialog/DialogTitle.vue",
"ui/dialog/DialogTrigger.vue", "ui/dialog/DialogTrigger.vue",
"ui/dialog/index.ts" "ui/dialog/index.ts"

View File

@ -29,6 +29,10 @@
"name": "DialogHeader.vue", "name": "DialogHeader.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\n\ninterface DialogHeaderProps {\n class?: string\n}\n\nconst props = defineProps<DialogHeaderProps>()\n</script>\n\n<template>\n <div\n :class=\"cn('flex flex-col space-y-2 text-center sm:text-left', props.class)\"\n >\n <slot />\n </div>\n</template>\n" "content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\n\ninterface DialogHeaderProps {\n class?: string\n}\n\nconst props = defineProps<DialogHeaderProps>()\n</script>\n\n<template>\n <div\n :class=\"cn('flex flex-col space-y-2 text-center sm:text-left', props.class)\"\n >\n <slot />\n </div>\n</template>\n"
}, },
{
"name": "DialogScrollContent.vue",
"content": "<script setup lang=\"ts\">\nimport {\n DialogClose,\n DialogContent,\n type DialogContentEmits,\n type DialogContentProps,\n DialogOverlay,\n DialogPortal,\n useEmitAsProps,\n} from 'radix-vue'\nimport { X } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<DialogContentProps & { class?: string }>()\nconst emits = defineEmits<DialogContentEmits>()\n\nconst emitsAsProps = useEmitAsProps(emits)\n</script>\n\n<template>\n <DialogPortal>\n <DialogOverlay\n class=\"fixed inset-0 z-50 grid place-items-center overflow-y-auto 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\"\n >\n <DialogContent\n :class=\"\n cn(\n 'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-border bg-background p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',\n props.class,\n )\n \"\n v-bind=\"{ ...props, ...emitsAsProps }\"\n >\n <slot />\n\n <DialogClose\n class=\"absolute top-3 right-3 p-0.5 transition-colors rounded-md hover:bg-secondary\"\n >\n <X class=\"w-4 h-4\" />\n <span class=\"sr-only\">Close</span>\n </DialogClose>\n </DialogContent>\n </DialogOverlay>\n </DialogPortal>\n</template>\n"
},
{ {
"name": "DialogTitle.vue", "name": "DialogTitle.vue",
"content": "<script setup lang=\"ts\">\nimport { DialogTitle, type DialogTitleProps } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<DialogTitleProps & { class?: string }>()\n</script>\n\n<template>\n <DialogTitle\n v-bind=\"props\"\n :class=\"\n cn(\n 'text-lg text-foreground font-semibold leading-none tracking-tight',\n props.class,\n )\n \"\n >\n <slot />\n </DialogTitle>\n</template>\n" "content": "<script setup lang=\"ts\">\nimport { DialogTitle, type DialogTitleProps } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<DialogTitleProps & { class?: string }>()\n</script>\n\n<template>\n <DialogTitle\n v-bind=\"props\"\n :class=\"\n cn(\n 'text-lg text-foreground font-semibold leading-none tracking-tight',\n props.class,\n )\n \"\n >\n <slot />\n </DialogTitle>\n</template>\n"
@ -39,7 +43,7 @@
}, },
{ {
"name": "index.ts", "name": "index.ts",
"content": "export { default as Dialog } from './Dialog.vue'\nexport { default as DialogClose } from './DialogClose.vue'\nexport { default as DialogTrigger } from './DialogTrigger.vue'\nexport { default as DialogHeader } from './DialogHeader.vue'\nexport { default as DialogTitle } from './DialogTitle.vue'\nexport { default as DialogDescription } from './DialogDescription.vue'\nexport { default as DialogContent } from './DialogContent.vue'\nexport { default as DialogFooter } from './DialogFooter.vue'\n" "content": "export { default as Dialog } from './Dialog.vue'\nexport { default as DialogClose } from './DialogClose.vue'\nexport { default as DialogTrigger } from './DialogTrigger.vue'\nexport { default as DialogHeader } from './DialogHeader.vue'\nexport { default as DialogTitle } from './DialogTitle.vue'\nexport { default as DialogDescription } from './DialogDescription.vue'\nexport { default as DialogContent } from './DialogContent.vue'\nexport { default as DialogScrollContent } from './DialogScrollContent.vue'\nexport { default as DialogFooter } from './DialogFooter.vue'\n"
} }
], ],
"type": "components:ui" "type": "components:ui"

View File

@ -29,6 +29,10 @@
"name": "DialogHeader.vue", "name": "DialogHeader.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\n\ninterface DialogHeaderProps {\n class?: string\n}\n\nconst props = defineProps<DialogHeaderProps>()\n</script>\n\n<template>\n <div\n :class=\"cn('flex flex-col space-y-2 text-center sm:text-left', props.class)\"\n >\n <slot />\n </div>\n</template>\n" "content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\n\ninterface DialogHeaderProps {\n class?: string\n}\n\nconst props = defineProps<DialogHeaderProps>()\n</script>\n\n<template>\n <div\n :class=\"cn('flex flex-col space-y-2 text-center sm:text-left', props.class)\"\n >\n <slot />\n </div>\n</template>\n"
}, },
{
"name": "DialogScrollContent.vue",
"content": "<script setup lang=\"ts\">\nimport {\n DialogClose,\n DialogContent,\n type DialogContentEmits,\n type DialogContentProps,\n DialogOverlay,\n DialogPortal,\n useEmitAsProps,\n} from 'radix-vue'\nimport { Cross2Icon } from '@radix-icons/vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<DialogContentProps & { class?: string }>()\nconst emits = defineEmits<DialogContentEmits>()\n\nconst emitsAsProps = useEmitAsProps(emits)\n</script>\n\n<template>\n <DialogPortal>\n <DialogOverlay\n class=\"fixed inset-0 z-50 grid place-items-center overflow-y-auto 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\"\n >\n <DialogContent\n :class=\"\n cn(\n 'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-border bg-background p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',\n props.class,\n )\n \"\n v-bind=\"{ ...props, ...emitsAsProps }\"\n >\n <slot />\n\n <DialogClose\n class=\"absolute top-4 right-4 p-0.5 transition-colors rounded-md hover:bg-secondary\"\n >\n <Cross2Icon class=\"w-4 h-4\" />\n <span class=\"sr-only\">Close</span>\n </DialogClose>\n </DialogContent>\n </DialogOverlay>\n </DialogPortal>\n</template>\n"
},
{ {
"name": "DialogTitle.vue", "name": "DialogTitle.vue",
"content": "<script setup lang=\"ts\">\nimport { DialogTitle, type DialogTitleProps } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<DialogTitleProps & { class?: string }>()\n</script>\n\n<template>\n <DialogTitle\n v-bind=\"props\"\n :class=\"\n cn(\n 'text-lg text-foreground font-semibold leading-none tracking-tight',\n props.class,\n )\n \"\n >\n <slot />\n </DialogTitle>\n</template>\n" "content": "<script setup lang=\"ts\">\nimport { DialogTitle, type DialogTitleProps } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<DialogTitleProps & { class?: string }>()\n</script>\n\n<template>\n <DialogTitle\n v-bind=\"props\"\n :class=\"\n cn(\n 'text-lg text-foreground font-semibold leading-none tracking-tight',\n props.class,\n )\n \"\n >\n <slot />\n </DialogTitle>\n</template>\n"
@ -39,7 +43,7 @@
}, },
{ {
"name": "index.ts", "name": "index.ts",
"content": "export { default as Dialog } from './Dialog.vue'\nexport { default as DialogClose } from './DialogClose.vue'\nexport { default as DialogTrigger } from './DialogTrigger.vue'\nexport { default as DialogHeader } from './DialogHeader.vue'\nexport { default as DialogTitle } from './DialogTitle.vue'\nexport { default as DialogDescription } from './DialogDescription.vue'\nexport { default as DialogContent } from './DialogContent.vue'\nexport { default as DialogFooter } from './DialogFooter.vue'\n" "content": "export { default as Dialog } from './Dialog.vue'\nexport { default as DialogClose } from './DialogClose.vue'\nexport { default as DialogTrigger } from './DialogTrigger.vue'\nexport { default as DialogHeader } from './DialogHeader.vue'\nexport { default as DialogTitle } from './DialogTitle.vue'\nexport { default as DialogDescription } from './DialogDescription.vue'\nexport { default as DialogContent } from './DialogContent.vue'\nexport { default as DialogScrollContent } from './DialogScrollContent.vue'\nexport { default as DialogFooter } from './DialogFooter.vue'\n"
} }
], ],
"type": "components:ui" "type": "components:ui"