Dialog
LhDialog is used for displaying important information or requiring user interaction within a modal overlay.
Type Definitions
The TypeScript type structures exported by this component (auto-extracted from packages/components/dialog/src/types.ts and @lh-ui/share/core/* sources; click each entry to expand; see the Props table above for attribute descriptions):
LhDialogProps
ts
export interface LhDialogProps {
modelValue?: boolean
title?: string
width?: string | number
fullscreen?: boolean
showClose?: boolean
closeOnClickModal?: boolean
closeOnPressEscape?: boolean
draggable?: boolean
center?: boolean
position?: 'top' | 'center' | 'bottom'
lockScroll?: boolean
beforeClose?: (done: () => void) => void
top?: string
zIndex?: number
themeVars?: Record<string, string>
bgPattern?: string
sx?: LhSxProps
}Basic Usage
Basic Usage
vue
<script setup lang="ts">
import { ref } from 'vue'
import { LhButton, LhDialog } from '@lh-ui/vue3'
const visible = ref(false)
</script>
<template>
<!-- Basic usage -->
<LhButton @click="visible = true">Open Dialog</LhButton>
<LhDialog v-model="visible" title="Dialog Title">
<p>Dialog content goes here</p>
<template #footer>
<LhButton @click="visible = false">Cancel</LhButton>
<LhButton type="primary">Confirm</LhButton>
</template>
</LhDialog>
</template>vue
<script>
import { LhButton, LhDialog } from '@lh-ui/vue3'
export default {
components: { LhButton, LhDialog },
data() {
return {
visible: false,
}
},
}
</script>
<template>
<!-- Basic usage -->
<LhButton @click="visible = true">Open Dialog</LhButton>
<LhDialog v-model="visible" title="Dialog Title">
<p>Dialog content goes here</p>
<template #footer>
<LhButton @click="visible = false">Cancel</LhButton>
<LhButton type="primary">Confirm</LhButton>
</template>
</LhDialog>
</template>Custom Width
Custom Width
vue
<script setup lang="ts">
import { ref } from 'vue'
import { LhButton, LhDialog } from '@lh-ui/vue3'
const visible2 = ref(false)
</script>
<template>
<!-- Custom width -->
<LhButton @click="visible2 = true">Open Wide Dialog</LhButton>
<LhDialog v-model="visible2" title="Wide Dialog" width="600px">
<p>Dialog with custom width</p>
</LhDialog>
</template>vue
<script>
import { LhButton, LhDialog } from '@lh-ui/vue3'
export default {
components: { LhButton, LhDialog },
data() {
return {
visible2: false,
}
},
}
</script>
<template>
<!-- Custom width -->
<LhButton @click="visible2 = true">Open Wide Dialog</LhButton>
<LhDialog v-model="visible2" title="Wide Dialog" width="600px">
<p>Custom width dialog</p>
</LhDialog>
</template>Draggable
Draggable
vue
<script setup lang="ts">
import { ref } from 'vue'
import { LhButton, LhDialog } from '@lh-ui/vue3'
const visible3 = ref(false)
</script>
<template>
<!-- Draggable -->
<LhButton @click="visible3 = true">Open Draggable Dialog</LhButton>
<LhDialog v-model="visible3" title="Draggable" draggable>
<p>Drag the title bar to move this dialog</p>
</LhDialog>
</template>vue
<script>
import { LhButton, LhDialog } from '@lh-ui/vue3'
export default {
components: { LhButton, LhDialog },
data() {
return {
visible3: false,
}
},
}
</script>
<template>
<!-- Draggable -->
<LhButton @click="visible3 = true">Open Draggable Dialog</LhButton>
<LhDialog v-model="visible3" title="Draggable" draggable>
<p>Drag the title bar to move this dialog</p>
</LhDialog>
</template>sx Quick Layout
Use the sx prop to quickly set spacing and sizing on the dialog root, without writing extra CSS.
sx max width
vue
<script setup lang="ts">
import { ref } from 'vue'
import { LhButton, LhDialog } from '@lh-ui/vue3'
const visible4 = ref(false)
</script>
<template>
<!-- sx max width -->
<LhButton @click="visible4 = true">Open Narrow Dialog</LhButton>
<LhDialog v-model="visible4" title="Narrow" :sx="{ maxWidth: 360 }">
<p>This dialog is capped at 360px wide</p>
</LhDialog>
</template>vue
<script>
import { LhButton, LhDialog } from '@lh-ui/vue3'
export default {
components: { LhButton, LhDialog },
data() {
return {
visible4: false,
}
},
}
</script>
<template>
<!-- sx max width -->
<LhButton @click="visible4 = true">Open Narrow Dialog</LhButton>
<LhDialog v-model="visible4" title="Narrow" :sx="{ maxWidth: 360 }">
<p>This dialog is capped at 360px wide</p>
</LhDialog>
</template>
sxis scaled on an8pxbase. See sx Quick Layout for the full API.
Behavior Options
Close behavior / scroll lock / beforeClose
vue
<script setup lang="ts">
import { ref } from 'vue'
import { LhButton, LhDialog } from '@lh-ui/vue3'
const advVisible1 = ref(false)
const advVisible2 = ref(false)
const advVisible3 = ref(false)
function handleAdvBeforeClose(done: () => void) {
setTimeout(done, 500)
}
</script>
<template>
<!-- Close by button only -->
<LhButton @click="advVisible1 = true">Close by button only</LhButton>
<LhDialog
v-model="advVisible1"
title="Close by button only"
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<p>The corner close button is hidden; clicking the overlay or pressing Escape will not close it — only the footer button does.</p>
<template #footer>
<LhButton type="primary" @click="advVisible1 = false">Got it</LhButton>
</template>
</LhDialog>
<!-- No scroll lock -->
<LhButton @click="advVisible2 = true">No scroll lock</LhButton>
<LhDialog v-model="advVisible2" title="No scroll lock" :lock-scroll="false">
<p>The page keeps scrolling while this dialog is open — handy for short, lightweight notices.</p>
</LhDialog>
<!-- Async close -->
<LhButton @click="advVisible3 = true">Async close</LhButton>
<LhDialog v-model="advVisible3" title="Async close" :before-close="handleAdvBeforeClose">
<p>After the close button, overlay click, or Escape, the dialog actually closes 500ms later (simulating a pre-close check).</p>
</LhDialog>
</template>vue
<script>
import { LhButton, LhDialog } from '@lh-ui/vue3'
export default {
components: { LhButton, LhDialog },
data() {
return {
advVisible1: false,
advVisible2: false,
advVisible3: false,
}
},
methods: {
handleAdvBeforeClose(done) {
setTimeout(done, 500)
},
},
}
</script>
<template>
<!-- Close by button only -->
<LhButton @click="advVisible1 = true">Close by button only</LhButton>
<LhDialog
v-model="advVisible1"
title="Close by button only"
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<p>The corner close button is hidden; clicking the overlay or pressing Escape will not close it — only the footer button does.</p>
<template #footer>
<LhButton type="primary" @click="advVisible1 = false">Got it</LhButton>
</template>
</LhDialog>
<!-- No scroll lock -->
<LhButton @click="advVisible2 = true">No scroll lock</LhButton>
<LhDialog v-model="advVisible2" title="No scroll lock" :lock-scroll="false">
<p>The page keeps scrolling while this dialog is open — handy for short, lightweight notices.</p>
</LhDialog>
<!-- Async close -->
<LhButton @click="advVisible3 = true">Async close</LhButton>
<LhDialog v-model="advVisible3" title="Async close" :before-close="handleAdvBeforeClose">
<p>After the close button, overlay click, or Escape, the dialog actually closes 500ms later (simulating a pre-close check).</p>
</LhDialog>
</template>Position and Stacking
fullscreen / position / top / zIndex / center
vue
<script setup lang="ts">
import { ref } from 'vue'
import { LhButton, LhDialog } from '@lh-ui/vue3'
const advVisible4 = ref(false)
const advVisible5 = ref(false)
const advVisible6 = ref(false)
const advVisible7 = ref(false)
</script>
<template>
<!-- Fullscreen -->
<LhButton @click="advVisible4 = true">Fullscreen</LhButton>
<LhDialog v-model="advVisible4" title="Fullscreen Dialog" fullscreen>
<p>With fullscreen the dialog covers the whole viewport.</p>
</LhDialog>
<!-- Bottom position -->
<LhButton @click="advVisible5 = true">Bottom position</LhButton>
<LhDialog v-model="advVisible5" title="Bottom Position" position="bottom">
<p>With position set to bottom, the dialog sits near the bottom of the viewport.</p>
</LhDialog>
<!-- Top offset + z-index -->
<LhButton @click="advVisible6 = true">Top offset + z-index</LhButton>
<LhDialog v-model="advVisible6" title="Top Offset + Stacking" position="top" top="10vh" :z-index="3000">
<p>The top prop sets the distance from the viewport top; z-index controls the stacking order.</p>
</LhDialog>
<!-- Centered content -->
<LhButton @click="advVisible7 = true">Centered content</LhButton>
<LhDialog v-model="advVisible7" title="Centered Content" center>
<p>center centers the title and content (legacy; prefer position in new code).</p>
<template #footer>
<LhButton @click="advVisible7 = false">Cancel</LhButton>
<LhButton type="primary" @click="advVisible7 = false">Confirm</LhButton>
</template>
</LhDialog>
</template>vue
<script>
import { LhButton, LhDialog } from '@lh-ui/vue3'
export default {
components: { LhButton, LhDialog },
data() {
return {
advVisible4: false,
advVisible5: false,
advVisible6: false,
advVisible7: false,
}
},
}
</script>
<template>
<!-- Fullscreen -->
<LhButton @click="advVisible4 = true">Fullscreen</LhButton>
<LhDialog v-model="advVisible4" title="Fullscreen Dialog" fullscreen>
<p>With fullscreen the dialog covers the whole viewport.</p>
</LhDialog>
<!-- Bottom position -->
<LhButton @click="advVisible5 = true">Bottom position</LhButton>
<LhDialog v-model="advVisible5" title="Bottom Position" position="bottom">
<p>With position set to bottom, the dialog sits near the bottom of the viewport.</p>
</LhDialog>
<!-- Top offset + z-index -->
<LhButton @click="advVisible6 = true">Top offset + z-index</LhButton>
<LhDialog v-model="advVisible6" title="Top Offset + Stacking" position="top" top="10vh" :z-index="3000">
<p>The top prop sets the distance from the viewport top; z-index controls the stacking order.</p>
</LhDialog>
<!-- Centered content -->
<LhButton @click="advVisible7 = true">Centered content</LhButton>
<LhDialog v-model="advVisible7" title="Centered Content" center>
<p>center centers the title and content (legacy; prefer position in new code).</p>
<template #footer>
<LhButton @click="advVisible7 = false">Cancel</LhButton>
<LhButton type="primary" @click="advVisible7 = false">Confirm</LhButton>
</template>
</LhDialog>
</template>Props
| Prop | Description | Type | Default |
|---|---|---|---|
| modelValue | Visibility | boolean | false |
| title | Dialog title | string | '' |
| width | Dialog width | string | number | '480px' |
| fullscreen | Whether fullscreen | boolean | false |
| showClose | Show close button | boolean | true |
| closeOnClickModal | Close on overlay click | boolean | true |
| closeOnPressEscape | Close on Escape key | boolean | true |
| draggable | Enable dragging | boolean | false |
| center | Whether to center the dialog (legacy; prefer position) | boolean | false |
| position | Dialog position: top / center / bottom | "top" | "center" | "bottom" | 'center' |
| lockScroll | Whether to lock scrolling | boolean | true |
| beforeClose | Callback before close | (done: () => void) => void | — |
| top | Distance from the top (px) | string | '15vh' |
| zIndex | z-index | number | — |
| themeVars | Component-level theme variables, overriding ConfigProvider configuration | { [x: string]: string; } | — |
| bgPattern | Component-level background pattern, overriding ConfigProvider configuration | string | — |
| sx | sx quick-layout props | LhSxProps | — |
Events
| Event | Description | Callback |
|---|---|---|
| update:modelValue | Visibility change | (value: boolean) => void |
| open | Dialog opened | () => void |
| close | Dialog closed | () => void |
| opened | Emitted after the dialog is fully opened | () => void |
| closed | Emitted after the dialog is fully closed | () => void |
Slots
| Slot | Description |
|---|---|
| default | Dialog body content |
| title | Custom title |
| footer | Footer area |