使用法
スライドオーバーのデフォルトスロットにあるButtonまたはその他のコンポーネントを使用します。
次に、#contentスロットを使用して、スライドオーバーが開いたときに表示されるコンテンツを追加します。
<template>
<USlideover>
<UButton label="Open" color="neutral" variant="subtle" />
<template #content>
<Placeholder class="h-full m-4" />
</template>
</USlideover>
</template>#header、#body、#footerスロットを使用して、スライドオーバーのコンテンツをカスタマイズすることもできます。
Title
titleプロパティを使用して、Slideoverのヘッダーのタイトルを設定します。
<template>
<USlideover title="Slideover with title">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>Description
descriptionプロパティを使用して、Slideoverのヘッダーの説明を設定します。
<template>
<USlideover
title="Slideover with description"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
>
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>閉じる
closeプロパティを使用して、スライドオーバーのヘッダーに表示される閉じるボタン(false値)をカスタマイズまたは非表示にします。
Buttonコンポーネントから任意のプロパティを渡してカスタマイズできます。
<template>
<USlideover
title="Slideover with close button"
:close="{
color: 'primary',
variant: 'outline',
class: 'rounded-full'
}"
>
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>#contentスロットがヘッダの一部であるため、閉じるボタンは表示されません。アイコンを閉じる
close-iconプロパティを使用して、閉じるボタンIconをカスタマイズします。デフォルトはi-lucide-xです。
<template>
<USlideover title="Slideover with close button" close-icon="i-lucide-arrow-right">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>Side
sideプロパティを使用して、スライドオーバーをスライドさせる画面の側面を設定します。デフォルトはrightです。
<template>
<USlideover side="left" title="Slideover with side">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full min-h-48" />
</template>
</USlideover>
</template>Inset badge{label="4.3+" class="align-text-top"}
insetプロパティを使用して、スライドオーバーをエッジから挿入します。
<template>
<USlideover side="right" inset title="Slideover with inset">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="min-w-96 min-h-96 size-full" />
</template>
</USlideover>
</template>Transition
transitionプロパティを使用して、スライドオーバーがアニメーション化されるかどうかを制御します。デフォルトはtrueです。
<template>
<USlideover :transition="false" title="Slideover without transition">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>オーバーレイ
overlayプロパティを使用して、スライドオーバーにオーバーレイがあるかどうかを制御します。デフォルトはtrueです。
<template>
<USlideover :overlay="false" title="Slideover without overlay">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>Modal
modalプロパティを使用して、Slideoverが外部コンテンツとのインタラクションをブロックするかどうかを制御します。デフォルトはtrueです。
modalがfalseに設定されると、オーバーレイは自動的に無効になり、外部コンテンツはインタラクティブになります。<template>
<USlideover :modal="false" title="Slideover interactive">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>Dismissible
dismissibleプロパティを使用して、スライドオーバーの外側をクリックするか、escapeを押したときにスライドオーバーを無効にするかを制御します。デフォルトはtrueです。
close:preventイベントは、ユーザーがクローズしようとすると発行されます。modal: falseとdismissible: falseを組み合わせて、スライドオーバーの背景を閉じずにインタラクティブにすることができます。<template>
<USlideover :dismissible="false" title="Slideover non-dismissible">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>アンマウントbadge{label="4.10+" class="align-text-top"}
unmount-on-hideプロパティを使用して、Slideoverのコンテンツが閉じたときにアンマウントされないようにします。デフォルトはtrueです。
<template>
<USlideover :unmount-on-hide="false" title="Slideover">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
</USlideover>
</template>portalプロパティがfalseに設定されている場合、コンテンツはサーバー上でもレンダリングされます。これはSSR中に開いているスライドオーバーをページ読み込み時にフラッシュなしでレンダリングしたり、SEOのためにコンテンツを公開したりするのに便利です。例
Controlオープンステート
オープン状態はdefault-openプロパティまたはv-model:openディレクティブを使用して制御できます。
<script setup lang="ts">
const open = ref(false)
defineShortcuts({
o: () => open.value = !open.value
})
</script>
<template>
<USlideover v-model:open="open">
<UButton label="Open" color="neutral" variant="subtle" />
<template #content>
<Placeholder class="h-full m-4" />
</template>
</USlideover>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const open = ref(false)
defineShortcuts({
o: () => open.value = !open.value
})
</script>
<template>
<USlideover v-model:open="open">
<UButton label="Open" color="neutral" variant="subtle" />
<template #content>
<Placeholder class="h-full m-4" />
</template>
</USlideover>
</template>
defineShortcutsを活用して、kbd{value="O"}を押してスライドオーバーを切り替えることができます。プログラムの使用法
useOverlayコンポーザブルを使用して、プログラムでスライドオーバーを開くことができます。
OverlayProviderコンポーネントを使用するAppコンポーネントでアプリをラップしてください。まず、プログラムで開くスライドオーバーコンポーネントを作成します。
<script setup lang="ts">
defineProps<{
count: number
}>()
const emit = defineEmits<{ close: [boolean] }>()
</script>
<template>
<USlideover
:close="{ onClick: () => emit('close', false) }"
:description="`This slideover was opened programmatically ${count} times`"
>
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer>
<div class="flex gap-2">
<UButton color="neutral" label="Dismiss" @click="emit('close', false)" />
<UButton label="Success" @click="emit('close', true)" />
</div>
</template>
</USlideover>
</template>closeイベントを発行しています。closeイベントを通じて任意のデータを発行することができ、そのデータはopen()の解決済み値になります。Promiseを解決するにはイベントを発行する必要があります。次に、アプリで使用します。
<script setup lang="ts">
import { LazySlideoverExample } from '#components'
const count = ref(0)
const toast = useToast()
const overlay = useOverlay()
const slideover = overlay.create(LazySlideoverExample)
async function open() {
const instance = slideover.open({
count: count.value
})
const shouldIncrement = await instance.result
if (shouldIncrement) {
count.value++
toast.add({
title: `Success: ${shouldIncrement}`,
color: 'success',
id: 'slideover-success'
})
// Update the count
slideover.patch({
count: count.value
})
return
}
toast.add({
title: `Dismissed: ${shouldIncrement}`,
color: 'error',
id: 'slideover-dismiss'
})
}
</script>
<template>
<UButton label="Open" color="neutral" variant="subtle" @click="open" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { LazySlideoverExample } from '#components'
const count = ref(0)
const toast = useToast()
const overlay = useOverlay()
const slideover = overlay.create(LazySlideoverExample)
async function open() {
const instance = slideover.open({
count: count.value
})
const shouldIncrement = await instance.result
if (shouldIncrement) {
count.value++
toast.add({
title: `Success: ${shouldIncrement}`,
color: 'success',
id: 'slideover-success'
})
// Update the count
slideover.patch({
count: count.value
})
return
}
toast.add({
title: `Dismissed: ${shouldIncrement}`,
color: 'error',
id: 'slideover-dismiss'
})
}
</script>
<template>
<UButton label="Open" color="neutral" variant="subtle" @click="open" />
</template>
emit('close')を出力することで、slideoverコンポーネント内でslideoverを閉じることができます。ネストされたスライドオーバー
お互いにスライドオーバーをネストできます。
<script setup lang="ts">
const first = ref(false)
const second = ref(false)
</script>
<template>
<USlideover v-model:open="first" title="First slideover" :ui="{ footer: 'justify-end' }">
<UButton color="neutral" variant="subtle" label="Open" />
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer>
<UButton label="Close" color="neutral" variant="outline" @click="first = false" />
<USlideover v-model:open="second" title="Second slideover" :ui="{ footer: 'justify-end' }">
<UButton label="Open second" color="neutral" />
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer>
<UButton label="Close" color="neutral" variant="outline" @click="second = false" />
</template>
</USlideover>
</template>
</USlideover>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const first = ref(false)
const second = ref(false)
</script>
<template>
<USlideover v-model:open="first" title="First slideover" :ui="{ footer: 'justify-end' }">
<UButton color="neutral" variant="subtle" label="Open" />
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer>
<UButton label="Close" color="neutral" variant="outline" @click="first = false" />
<USlideover v-model:open="second" title="Second slideover" :ui="{ footer: 'justify-end' }">
<UButton label="Open second" color="neutral" />
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer>
<UButton label="Close" color="neutral" variant="outline" @click="second = false" />
</template>
</USlideover>
</template>
</USlideover>
</template>
フッタースロット付き
#footerスロットを使用して、スライドオーバーの本体の後にコンテンツを追加します。
<script setup lang="ts">
const open = ref(false)
</script>
<template>
<USlideover v-model:open="open" title="Slideover with footer" description="This is useful when you want a form in a Slideover." :ui="{ footer: 'justify-end' }">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer="{ close }">
<UButton label="Cancel" color="neutral" variant="outline" @click="close" />
<UButton label="Submit" color="neutral" />
</template>
</USlideover>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<USlideover v-model:open="open" title="Slideover with footer" description="This is useful when you want a form in a Slideover." :ui="{ footer: 'justify-end' }">
<UButton label="Open" color="neutral" variant="subtle" />
<template #body>
<Placeholder class="h-full" />
</template>
<template #footer="{ close }">
<UButton label="Cancel" color="neutral" variant="outline" @click="close" />
<UButton label="Submit" color="neutral" />
</template>
</USlideover>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
title | string | |
description | string | |
content | DialogContentProps & Partial<EmitsToProps<DialogContentImplEmits>>The content of the slideover. | |
overlay | true | boolean Render an overlay behind the slideover. |
transition | true | boolean Animate the slideover when opening or closing. |
side | 'right' | "right" | "top" | "bottom" | "left"The side of the slideover. |
inset | false | boolean Whether to inset the slideover from the edges. |
portal | true | string | false | true | HTMLElementRender the slideover in a portal. |
close | true | boolean | Omit<ButtonProps, LinkPropsKeys> Display a close button to dismiss the slideover.
|
closeIcon | appConfig.ui.icons.close | anyThe icon displayed in the close button. |
dismissible | true | boolean When |
open | boolean The controlled open state of the dialog. Can be binded as | |
defaultOpen | boolean The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. | |
modal | true | boolean The modality of the dialog When set to |
unmountOnHide | true | boolean When set to |
ui | { overlay?: SlotClass; content?: SlotClass; header?: SlotClass; wrapper?: SlotClass; body?: SlotClass; footer?: SlotClass; title?: SlotClass; description?: SlotClass; close?: SlotClass; } |
スロット
| Slot | Type |
|---|---|
default | { open: boolean; } |
content | { close: () => void; } |
header | { close: () => void; } |
title | {} |
description | {} |
actions | {} |
close | { ui: object; } |
body | { close: () => void; } |
footer | { close: () => void; } |
Emits
| Event | Type |
|---|---|
leave | [] |
after:leave | [] |
enter | [] |
after:enter | [] |
close:prevent | [] |
update:open | [value: boolean] |
Theme
export default defineAppConfig({
ui: {
slideover: {
slots: {
overlay: 'fixed inset-0 bg-elevated/75',
content: 'fixed bg-default divide-y divide-default sm:ring ring-default sm:shadow-lg flex flex-col focus:outline-none',
header: 'flex items-center gap-1.5 p-4 sm:px-6 min-h-(--ui-header-height)',
wrapper: '',
body: 'flex-1 overflow-y-auto p-4 sm:p-6',
footer: 'flex items-center gap-1.5 p-4 sm:px-6',
title: 'text-highlighted font-semibold',
description: 'mt-1 text-muted text-sm',
close: 'absolute top-4 end-4'
},
variants: {
side: {
top: {
content: ''
},
right: {
content: 'max-w-md'
},
bottom: {
content: ''
},
left: {
content: 'max-w-md'
}
},
inset: {
true: {
content: 'rounded-lg'
}
},
transition: {
true: {
overlay: 'data-[state=open]:animate-[fade-in_200ms_var(--ease-out)] data-[state=closed]:animate-[fade-out_200ms_var(--ease-out)]'
}
}
},
compoundVariants: [
{
side: 'top',
inset: true,
class: {
content: 'max-h-[calc(100%-2rem)] inset-x-4 top-4'
}
},
{
side: 'top',
inset: false,
class: {
content: 'max-h-full inset-x-0 top-0'
}
},
{
side: 'right',
inset: true,
class: {
content: 'w-[calc(100%-2rem)] inset-y-4 right-4'
}
},
{
side: 'right',
inset: false,
class: {
content: 'w-full inset-y-0 right-0'
}
},
{
side: 'bottom',
inset: true,
class: {
content: 'max-h-[calc(100%-2rem)] inset-x-4 bottom-4'
}
},
{
side: 'bottom',
inset: false,
class: {
content: 'max-h-full inset-x-0 bottom-0'
}
},
{
side: 'left',
inset: true,
class: {
content: 'w-[calc(100%-2rem)] inset-y-4 left-4'
}
},
{
side: 'left',
inset: false,
class: {
content: 'w-full inset-y-0 left-0'
}
},
{
transition: true,
side: 'top',
class: {
content: 'data-[state=open]:animate-[slide-in-from-top_200ms_var(--ease-out)] data-[state=closed]:animate-[slide-out-to-top_200ms_var(--ease-out)]'
}
},
{
transition: true,
side: 'right',
class: {
content: 'data-[state=open]:animate-[slide-in-from-right_200ms_var(--ease-out)] data-[state=closed]:animate-[slide-out-to-right_200ms_var(--ease-out)]'
}
},
{
transition: true,
side: 'bottom',
class: {
content: 'data-[state=open]:animate-[slide-in-from-bottom_200ms_var(--ease-out)] data-[state=closed]:animate-[slide-out-to-bottom_200ms_var(--ease-out)]'
}
},
{
transition: true,
side: 'left',
class: {
content: 'data-[state=open]:animate-[slide-in-from-left_200ms_var(--ease-out)] data-[state=closed]:animate-[slide-out-to-left_200ms_var(--ease-out)]'
}
}
]
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
slideover: {
slots: {
overlay: 'fixed inset-0 bg-elevated/75',
content: 'fixed bg-default divide-y divide-default sm:ring ring-default sm:shadow-lg flex flex-col focus:outline-none',
header: 'flex items-center gap-1.5 p-4 sm:px-6 min-h-(--ui-header-height)',
wrapper: '',
body: 'flex-1 overflow-y-auto p-4 sm:p-6',
footer: 'flex items-center gap-1.5 p-4 sm:px-6',
title: 'text-highlighted font-semibold',
description: 'mt-1 text-muted text-sm',
close: 'absolute top-4 end-4'
},
variants: {
side: {
top: {
content: ''
},
right: {
content: 'max-w-md'
},
bottom: {
content: ''
},
left: {
content: 'max-w-md'
}
},
inset: {
true: {
content: 'rounded-lg'
}
},
transition: {
true: {
overlay: 'data-[state=open]:animate-[fade-in_200ms_var(--ease-out)] data-[state=closed]:animate-[fade-out_200ms_var(--ease-out)]'
}
}
},
compoundVariants: [
{
side: 'top',
inset: true,
class: {
content: 'max-h-[calc(100%-2rem)] inset-x-4 top-4'
}
},
{
side: 'top',
inset: false,
class: {
content: 'max-h-full inset-x-0 top-0'
}
},
{
side: 'right',
inset: true,
class: {
content: 'w-[calc(100%-2rem)] inset-y-4 right-4'
}
},
{
side: 'right',
inset: false,
class: {
content: 'w-full inset-y-0 right-0'
}
},
{
side: 'bottom',
inset: true,
class: {
content: 'max-h-[calc(100%-2rem)] inset-x-4 bottom-4'
}
},
{
side: 'bottom',
inset: false,
class: {
content: 'max-h-full inset-x-0 bottom-0'
}
},
{
side: 'left',
inset: true,
class: {
content: 'w-[calc(100%-2rem)] inset-y-4 left-4'
}
},
{
side: 'left',
inset: false,
class: {
content: 'w-full inset-y-0 left-0'
}
},
{
transition: true,
side: 'top',
class: {
content: 'data-[state=open]:animate-[slide-in-from-top_200ms_var(--ease-out)] data-[state=closed]:animate-[slide-out-to-top_200ms_var(--ease-out)]'
}
},
{
transition: true,
side: 'right',
class: {
content: 'data-[state=open]:animate-[slide-in-from-right_200ms_var(--ease-out)] data-[state=closed]:animate-[slide-out-to-right_200ms_var(--ease-out)]'
}
},
{
transition: true,
side: 'bottom',
class: {
content: 'data-[state=open]:animate-[slide-in-from-bottom_200ms_var(--ease-out)] data-[state=closed]:animate-[slide-out-to-bottom_200ms_var(--ease-out)]'
}
},
{
transition: true,
side: 'left',
class: {
content: 'data-[state=open]:animate-[slide-in-from-left_200ms_var(--ease-out)] data-[state=closed]:animate-[slide-out-to-left_200ms_var(--ease-out)]'
}
}
]
}
}
})
]
})