Modal ​
Usage ​
Use a Button or any other component in the default slot of the Modal.
Then, use the #content
slot to add the content displayed when the Modal is open.
<template>
<B24Modal>
<B24Button label="Open" />
<template #content>
<Placeholder class="h-40 -m-[10px]" />
</template>
</B24Modal>
</template>
You can also use the #header
, #body
and #footer
slots to customize the Modal's content.
Title ​
Use the title
prop to set the title of the Modal's header.
Details
<script setup lang="ts">
export interface ExampleProps {
title?: string
}
withDefaults(defineProps<ExampleProps>(), {
title: 'Heads up!'
})
</script>
<template>
<B24Modal
:title="title"
>
<B24Button label="Open" />
<template #body>
<Placeholder class="h-40" />
</template>
</B24Modal>
</template>
Description ​
Use the description
prop to set the description of the Modal's header.
Details
<script setup lang="ts">
export interface ExampleProps {
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>
<template>
<B24Modal
:title="title"
:description="description"
>
<B24Button label="Open" />
<template #body>
<Placeholder class="h-40" />
</template>
</B24Modal>
</template>
Close ​
Use the close
prop to customize or hide the close button (with false
value) displayed in the Modal's header.
You can pass any property from the Button component to customize it.
TIP
The close button is not displayed if the #content
slot is used as it's a part of the header.
Details
<script setup lang="ts">
export interface ExampleProps {
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>
<template>
<B24Modal
:title="title"
:description="description"
:close="{ color: 'air-primary-alert', size: 'lg', rounded: false }"
>
<B24Button label="Open" />
<template #body>
<Placeholder class="h-40" />
</template>
</B24Modal>
</template>
Close Icon ​
Use the close-icon
prop to customize the close button @bitrix24/b24icons. Defaults to Cross30Icon
.
Details
<script setup lang="ts">
import EyeClosedIcon from '@bitrix24/b24icons-vue/button/EyeClosedIcon'
export interface ExampleProps {
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>
<template>
<B24Modal
:title="title"
:description="description"
:close-icon="EyeClosedIcon"
>
<B24Button label="Open" />
<template #body>
<Placeholder class="h-40" />
</template>
</B24Modal>
</template>
Overlay ​
Use the overlay
prop to control whether the Modal has an overlay or not. Defaults to true
.
Details
<script setup lang="ts">
export interface ExampleProps {
isOverlay?: boolean
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
isOverlay: true,
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>
<template>
<B24Modal
:overlay="isOverlay"
:title="title"
:description="description"
>
<B24Button label="Open" />
<template #body>
<Placeholder class="h-40" />
</template>
</B24Modal>
</template>
If you want to disable background blur, you should use the overlayBlur
prop. The overlayBlur
prop has 3 options:
auto
: (default) when the user has not requested reduced motionon
: always use bluroff
: do not use blur
Details
<script setup lang="ts">
import type { ModalProps } from '@bitrix24/b24ui-nuxt'
export interface ExampleProps {
overlayBlur?: ModalProps['overlayBlur']
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
overlayBlur: 'auto' as ModalProps['overlayBlur'],
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>
<template>
<B24Modal
:overlay-blur="overlayBlur"
:title="title"
:description="description"
>
<B24Button label="Open" />
<template #body>
<Placeholder class="h-40" />
</template>
</B24Modal>
</template>
Transition ​
Use the transition
prop to control whether the Modal is animated or not. Defaults to true
.
INFO
Reduced movement is taken into account
Details
<script setup lang="ts">
export interface ExampleProps {
isTransition?: boolean
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
isTransition: true,
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>
<template>
<B24Modal
:transition="isTransition"
:title="title"
:description="description"
>
<B24Button label="Open" />
<template #body>
<Placeholder class="h-40" />
</template>
</B24Modal>
</template>
Fullscreen ​
Use the fullscreen
prop to make the Modal fullscreen.
Details
<script setup lang="ts">
export interface ExampleProps {
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>
<template>
<B24Modal
fullscreen
:title="title"
:description="description"
:b24ui="{ body: 'max-h-[calc(100vh-90px)]' }"
>
<B24Button label="Open" />
<template #body>
<Placeholder class="h-[3000px]" />
</template>
</B24Modal>
</template>
Examples ​
Control open state ​
You can control the open state by using the default-open
prop or the v-model:open
directive.
INFO
In this example, leveraging defineShortcuts
, you can toggle the Modal by pressing O
.
TIP
This allows you to move the trigger outside of the Modal or remove it entirely.
Details
<script setup lang="ts">
import { ref } from 'vue'
export interface ExampleProps {
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
const open = ref(false)
defineShortcuts({
o: () => open.value = !open.value
})
</script>
<template>
<B24Modal
v-model:open="open"
:title="title"
:description="description"
>
<B24Button label="Open" />
<template #body>
<Placeholder class="h-40" />
</template>
</B24Modal>
</template>
Disable dismissal ​
Set the dismissible
prop to false
to prevent the Modal from being closed when clicking outside of it or pressing escape. A close:prevent
event will be emitted when the user tries to close it.
Details
<script setup lang="ts">
export interface ExampleProps {
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>
<template>
<B24Modal
:dismissible="false"
:title="title"
:description="description"
>
<B24Button label="Open" color="link" depth="dark" />
<template #body>
<ProseP accent="less-more" small>
Modal non-dismissible: <ProseCode color="air-primary-copilot">Esc</ProseCode> also doesn't work
</ProseP>
</template>
</B24Modal>
</template>
Programmatic usage ​
You can use the useOverlay
composable to open a Modal programmatically.
WARNING
Make sure to wrap your app with the App
component which uses the OverlayProvider
component.
First, create a modal component that will be opened programmatically:
<script setup lang="ts">
defineProps<{
count: number
}>()
const emit = defineEmits<{ close: [boolean] }>()
</script>
<template>
<B24Modal
:close="{ onClick: () => emit('close', false) }"
:title="`This modal was opened programmatically ${count} times`"
>
<template #footer>
<div class="flex flex-row gap-[10px]">
<B24Button
color="air-primary-success"
label="Success"
@click="emit('close', true)"
/>
<B24Button
color="air-tertiary"
label="Close"
@click="emit('close', false)"
/>
</div>
</template>
</B24Modal>
</template>
INFO
We are emitting a close
event when the modal is closed or dismissed here. You can emit any data through the close
event, however, the event must be emitted in order to capture the return value.
Then, use it in your app:
TIP
You can close the modal within the modal component by emitting emit('close')
.
Details
<script setup lang="ts">
import { ref } from 'vue'
import LazyModal from './LazyModal.vue'
const count = ref(0)
const toast = useToast()
const overlay = useOverlay()
const modal = overlay.create(LazyModal)
async function open() {
const instance = modal.open({
count: count.value
})
const shouldIncrement = await instance.result
if (shouldIncrement) {
count.value++
toast.add({
title: `Success: ${shouldIncrement}`,
color: 'air-primary-success',
id: 'modal-success'
})
// Update the count
modal.patch({
count: count.value
})
return
}
toast.add({
title: `Dismissed: ${shouldIncrement}`,
color: 'air-primary-alert',
id: 'modal-dismiss'
})
}
</script>
<template>
<B24Button label="Open" @click="open" />
</template>
Nested modals ​
You can nest modals within each other.
Details
<script setup lang="ts">
import { ref } from 'vue'
export interface ExampleProps {
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
const first = ref(false)
const second = ref(false)
</script>
<template>
<B24Modal
v-model:open="first"
:title="title"
:description="description"
>
<B24Button label="Open" />
<template #footer>
<div class="flex flex-row gap-[10px]">
<B24Modal
v-model:open="second"
title="Second modal"
>
<B24Button
color="air-primary"
label="Open second"
/>
<template #footer>
<B24Button
color="air-tertiary"
label="Close"
@click="second = false"
/>
</template>
</B24Modal>
<B24Button
color="air-tertiary"
label="Close"
@click="first = false"
/>
</div>
</template>
</B24Modal>
</template>
With footer slot ​
Use the #footer
slot to add content after the Modal's body.
TIP
You can also close the dialog box using the B24ModalDialogClose
component.
Details
<script setup lang="ts">
export interface ExampleProps {
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>
<template>
<B24Modal
:title="title"
:description="description"
:b24ui="{ footer: 'flex-row-reverse justify-start' }"
>
<B24Button label="Open" />
<template #body>
<Placeholder class="h-40" />
</template>
<template #footer="{ close }">
<B24Button
label="Cancel"
color="air-tertiary"
@click="close"
/>
<B24ModalDialogClose>
<B24Button label="Send" color="air-primary" />
</B24ModalDialogClose>
</template>
</B24Modal>
</template>
With body slot ​
Use the #body
slot to add content.
Details
<script setup lang="ts">
import FileUploadIcon from '@bitrix24/b24icons-vue/main/FileUploadIcon'
export interface ExampleProps {
title?: string
description?: string
}
withDefaults(defineProps<ExampleProps>(), {
title: 'Heads up!',
description: 'Let\'s signal the manager that the deal is not moving.'
})
</script>
<template>
<B24Modal
:title="title"
:description="description"
:b24ui="{ footer: 'border-t-0 mt-2.5 pt-0' }"
>
<B24Button label="Open" />
<template #body>
<div class="flex flex-col gap-[14px]">
<B24Separator />
<div class="w-full flex flex-row flex-nowrap items-center justify-start gap-3">
<div class="size-8xl rounded-xs border border-(--ui-color-divider-default) flex flex-col items-center justify-center">
<FileUploadIcon class="size-10 text-(--b24ui-typography-description-color)" />
</div>
<div class="flex flex-col flex-nowrap gap-1">
<ProseH5 class="mb-0">
start-ui.md
</ProseH5>
<ProseP small accent="less-more">
650 bytes
</ProseP>
</div>
</div>
<B24Separator />
<div class="w-full mt-[6px]">
<B24Textarea autofocus placeholder="Add a comment" autoresize :rows="1" :maxrows="4" />
</div>
</div>
</template>
<template #footer>
<div class="flex flex-row gap-[10px]">
<B24ModalDialogClose>
<B24Button label="Send" color="air-primary" />
</B24ModalDialogClose>
<B24ModalDialogClose>
<B24Button label="Cancel" color="air-tertiary" />
</B24ModalDialogClose>
</div>
<div>
<B24Button label="Full version" size="sm" color="air-tertiary-no-accent" />
</div>
</template>
</B24Modal>
</template>
API ​
Props ​
Prop | Default | Type |
---|---|---|
title | string | |
description | string | |
content | Omit<DialogContentProps, "asChild" | "as" | "forceMount"> & Partial<EmitsToProps<DialogContentImplEmits>> The content of the modal | |
overlay | true | boolean Render an overlay behind the modal. |
overlayBlur | "auto" | "auto" | "on" | "off" Render an overlay blur behind the modal.
`auto` use `motion-safe`. |
transition | true | boolean Animate the modal when opening or closing. |
fullscreen | false | boolean When `true`, the modal will take up the full screen. |
portal | true | string | false | true | HTMLElement Render the modal in a portal. |
close | true | boolean | Partial<ButtonProps> Display a close button to dismiss the modal.
`{ size: 'xs', color: 'link' }`{lang="ts"} |
closeIcon | icons.close | (props: HTMLAttributes & VNodeProps & {}, ctx: Omit<{ attrs: Data; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<...>>(exposed?: Exposed) => void; }, "expose">): any The icon displayed in the close button. |
dismissible | true | boolean When `false`, the modal will not close when clicking outside or pressing escape. |
scrollbarThin | true | boolean |
open | boolean The controlled open state of the dialog. Can be binded as `v-model:open`. | |
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 `true`, <br>
interaction with outside elements will be disabled and only dialog content will be visible to screen readers. |
b24ui | { overlay?: ClassNameValue; content?: ClassNameValue; contentWrapper?: ClassNameValue; header?: ClassNameValue; wrapper?: ClassNameValue; title?: ClassNameValue; description?: ClassNameValue; close?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; } |
Slots ​
Slot | Type |
---|---|
default | { open: boolean; } |
content | { close: () => void; } |
header | { close: () => void; } |
title | {} |
description | {} |
actions | {} |
close | { close: () => void; b24ui: { overlay: (props?: Record<string, any>) => string; content: (props?: Record<string, any> | undefined) => string; contentWrapper: (props?: Record<string, any> | undefined) => string; header: (props?: Record<string, any> | undefined) => string; wrapper: (props?: Record<string, any> | undefined) => string; title: (props?: Record<string, any> | undefined) => string; description: (props?: Record<string, any> | undefined) => string; close: (props?: Record<string, any> | undefined) => string; body: (props?: Record<string, any> | undefined) => string; footer: (props?: Record<string, any> | undefined) => string; }; } |
body | { close: () => void; } |
footer | { close: () => void; } |
Emits ​
/**
* Emitted events for the Modal component
*/
interface ModalEmits {
after:leave: (payload: []) => void;
after:enter: (payload: []) => void;
close:prevent: (payload: []) => void;
update:open: (payload: [value: boolean]) => void;
}