Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/content/docs/2.components/modal.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,36 @@ slots:
:placeholder{class="h-full"}
::

### Force Mount

Use the `portal` prop with an object to force the Modal content to render even when closed. This is useful for SSR when the modal should be visible on initial page load.

::component-code
---
prettier: true
ignore:
- title
props:
portal:
to: false
forceMount: true
title: 'Modal with force mount'
slots:
default: |

<UButton label="Open" color="neutral" variant="subtle" />

body: |

<Placeholder class="h-48" />
---

:u-button{label="Open" color="neutral" variant="subtle"}

#body
:placeholder{class="h-48"}
::
Comment on lines +363 to +391
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Fix markdownlint MD007/MD018 in the Force Mount example.

Line 371 (MD007) and Line 389 (MD018) are flagged in this new block. Consider scoping a markdownlint disable to this component-code snippet.

πŸ› οΈ Suggested fix
 ### Force Mount
+
+<!-- markdownlint-disable MD007 MD018 -->
 ::component-code
 ---
 prettier: true
 ignore:
   - title
 props:
   portal:
     to: false
     forceMount: true
   title: 'Modal with force mount'
@@
 `#body`
 :placeholder{class="h-48"}
 ::
+<!-- markdownlint-enable MD007 MD018 -->
🧰 Tools
πŸͺ› markdownlint-cli2 (0.20.0)

371-371: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


389-389: No space after hash on atx style heading

(MD018, no-missing-space-atx)

πŸ€– Prompt for AI Agents
In `@docs/content/docs/2.components/modal.md` around lines 363 - 391, The new
"Force Mount" component-code block triggers markdownlint MD007/MD018; scope a
markdownlint disable to just this snippet by wrapping the component-code block
with per-snippet directives (e.g., add a short markdownlint-disable/enable
comment immediately before and after the component-code block) so lint rules are
suppressed only for this example; look for the ":::component-code" snippet that
contains the portal prop (portal: { to: false, forceMount: true }) and apply the
scoped disable/enable around it to fix MD007/MD018 without affecting other docs.


## Examples

### Control open state
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/ContextMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import theme from '#build/ui/context-menu'
import type { AvatarProps, IconProps, KbdProps, LinkProps } from '../types'
import type { ArrayOrNested, DynamicSlots, GetItemKeys, MergeTypes, NestedItem, EmitsToProps } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type ContextMenu = ComponentConfig<typeof theme, AppConfig, 'contextMenu'>

Expand Down Expand Up @@ -70,7 +71,7 @@ export interface ContextMenuProps<T extends ArrayOrNested<ContextMenuItem> = Arr
* Render the menu in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* The key used to get the label from the item.
* @defaultValue 'label'
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/ContextMenuContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import type theme from '#build/ui/context-menu'
import type { AvatarProps, ContextMenuItem, ContextMenuSlots, IconProps, KbdProps } from '../types'
import type { ArrayOrNested, GetItemKeys } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type ContextMenu = ComponentConfig<typeof theme, AppConfig, 'contextMenu'>

interface ContextMenuContentProps<T extends ArrayOrNested<ContextMenuItem>> extends Omit<RekaContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
items?: T
portal?: boolean | string | HTMLElement
portal?: PortalProps
sub?: boolean
labelKey: GetItemKeys<T>
descriptionKey: GetItemKeys<T>
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/Drawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/drawer'
import type { EmitsToProps } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type Drawer = ComponentConfig<typeof theme, AppConfig, 'drawer'>

Expand Down Expand Up @@ -37,7 +38,7 @@ export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | '
* Render the drawer in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* Whether the drawer is nested in another drawer.
* @defaultValue false
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/DropdownMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import theme from '#build/ui/dropdown-menu'
import type { AvatarProps, IconProps, KbdProps, LinkProps } from '../types'
import type { ArrayOrNested, DynamicSlots, GetItemKeys, MergeTypes, NestedItem, EmitsToProps } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type DropdownMenu = ComponentConfig<typeof theme, AppConfig, 'dropdownMenu'>

Expand Down Expand Up @@ -78,7 +79,7 @@ export interface DropdownMenuProps<T extends ArrayOrNested<DropdownMenuItem> = A
* Render the menu in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* The key used to get the label from the item.
* @defaultValue 'label'
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/DropdownMenuContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import type theme from '#build/ui/dropdown-menu'
import type { KbdProps, AvatarProps, DropdownMenuItem, DropdownMenuSlots, IconProps } from '../types'
import type { ArrayOrNested, GetItemKeys, NestedItem, DynamicSlots, MergeTypes } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type DropdownMenu = ComponentConfig<typeof theme, AppConfig, 'dropdownMenu'>

interface DropdownMenuContentProps<T extends ArrayOrNested<DropdownMenuItem>> extends Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
items?: T
portal?: boolean | string | HTMLElement
portal?: PortalProps
sub?: boolean
labelKey: GetItemKeys<T>
descriptionKey: GetItemKeys<T>
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/InputMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { ModelModifiers } from '../types/input'
import type { InputHTMLAttributes } from '../types/html'
import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetItemValue, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type InputMenu = ComponentConfig<typeof theme, AppConfig, 'inputMenu'>

Expand Down Expand Up @@ -104,7 +105,7 @@ export interface InputMenuProps<T extends ArrayOrNested<InputMenuItem> = ArrayOr
* Render the menu in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* Enable virtualization for large lists.
* Note: when enabled, all groups are flattened into a single list due to a limitation of Reka UI (https://github.com/unovue/reka-ui/issues/1885).
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/Modal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import theme from '#build/ui/modal'
import type { ButtonProps, IconProps, LinkPropsKeys } from '../types'
import type { EmitsToProps } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type Modal = ComponentConfig<typeof theme, AppConfig, 'modal'>

Expand Down Expand Up @@ -37,7 +38,7 @@ export interface ModalProps extends DialogRootProps {
* Render the modal in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* Display a close button to dismiss the modal.
* `{ size: 'md', color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/Popover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/popover'
import type { EmitsToProps } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type Popover = ComponentConfig<typeof theme, AppConfig, 'popover'>
type PopoverMode = 'click' | 'hover'
Expand All @@ -28,7 +29,7 @@ export interface PopoverProps<M extends PopoverMode = PopoverMode> extends Popov
* Render the popover in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* The reference (or anchor) element that is being referred to for positioning.
*
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { ModelModifiers } from '../types/input'
import type { ButtonHTMLAttributes } from '../types/html'
import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetItemValue, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type Select = ComponentConfig<typeof theme, AppConfig, 'select'>

Expand Down Expand Up @@ -77,7 +78,7 @@ export interface SelectProps<T extends ArrayOrNested<SelectItem> = ArrayOrNested
* Render the menu in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* When `items` is an array of objects, select the field to use as the value.
* @defaultValue 'value'
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/SelectMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { ModelModifiers } from '../types/input'
import type { ButtonHTMLAttributes } from '../types/html'
import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetItemValue, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type SelectMenu = ComponentConfig<typeof theme, AppConfig, 'selectMenu'>

Expand Down Expand Up @@ -97,7 +98,7 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
* Render the menu in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* Enable virtualization for large lists.
* Note: when enabled, all groups are flattened into a single list due to a limitation of Reka UI (https://github.com/unovue/reka-ui/issues/1885).
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/Slideover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import theme from '#build/ui/slideover'
import type { ButtonProps, IconProps, LinkPropsKeys } from '../types'
import type { EmitsToProps } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type Slideover = ComponentConfig<typeof theme, AppConfig, 'slideover'>

Expand Down Expand Up @@ -37,7 +38,7 @@ export interface SlideoverProps extends DialogRootProps {
* Render the slideover in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* Display a close button to dismiss the slideover.
* `{ size: 'md', color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/Toaster.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { ToastProviderProps } from 'reka-ui'
import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/toaster'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type Toaster = ComponentConfig<typeof theme, AppConfig, 'toaster'>

Expand All @@ -26,7 +27,7 @@ export interface ToasterProps extends Omit<ToastProviderProps, 'swipeDirection'>
* Render the toaster in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* Maximum number of toasts to display at once.
* @defaultValue 5
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/components/Tooltip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import theme from '#build/ui/tooltip'
import type { KbdProps } from '../types'
import type { EmitsToProps } from '../types/utils'
import type { ComponentConfig } from '../types/tv'
import type { PortalProps } from '../composables/usePortal'

type Tooltip = ComponentConfig<typeof theme, AppConfig, 'tooltip'>

Expand All @@ -27,7 +28,7 @@ export interface TooltipProps extends TooltipRootProps {
* Render the tooltip in a portal.
* @defaultValue true
*/
portal?: boolean | string | HTMLElement
portal?: PortalProps
/**
* The reference (or anchor) element that is being referred to for positioning.
*
Expand Down
53 changes: 48 additions & 5 deletions src/runtime/composables/usePortal.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,61 @@
import { inject, computed } from 'vue'
import type { Ref, InjectionKey } from 'vue'
import type { DialogPortalProps } from 'reka-ui'

export const portalTargetInjectionKey: InjectionKey<Ref<boolean | string | HTMLElement>> = Symbol('nuxt-ui.portal-target')

export function usePortal(portal: Ref<boolean | string | HTMLElement | undefined>) {
export type PortalProps = boolean | string | HTMLElement | DialogPortalProps

function isDialogPortalProps(p: unknown): p is DialogPortalProps {
return typeof p === 'object' && p !== null && ('to' in p || 'disabled' in p || 'defer' in p || 'forceMount' in p)
}
Comment on lines +9 to +11
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against HTMLElements in isDialogPortalProps.

Line 9-11: any object with disabled (e.g., an HTMLButtonElement) is treated as DialogPortalProps, which can misclassify a portal target element and drop it (p.to becomes undefined). Consider excluding DOM nodes/HTMLElements before property checks.

πŸ› οΈ Suggested fix
function isDialogPortalProps(p: unknown): p is DialogPortalProps {
-  return typeof p === 'object' && p !== null && ('to' in p || 'disabled' in p || 'defer' in p || 'forceMount' in p)
+  if (typeof p !== 'object' || p === null) return false
+  // Exclude DOM nodes/HTMLElements from being treated as portal config objects
+  if ('nodeType' in (p as any)) return false
+  return 'to' in p || 'disabled' in p || 'defer' in p || 'forceMount' in p
}
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function isDialogPortalProps(p: unknown): p is DialogPortalProps {
return typeof p === 'object' && p !== null && ('to' in p || 'disabled' in p || 'defer' in p || 'forceMount' in p)
}
function isDialogPortalProps(p: unknown): p is DialogPortalProps {
if (typeof p !== 'object' || p === null) return false
// Exclude DOM nodes/HTMLElements from being treated as portal config objects
if ('nodeType' in (p as any)) return false
return 'to' in p || 'disabled' in p || 'defer' in p || 'forceMount' in p
}
πŸ€– Prompt for AI Agents
In `@src/runtime/composables/usePortal.ts` around lines 9 - 11, The type-guard
isDialogPortalProps currently treats any object with properties like "disabled"
as DialogPortalProps, which misclassifies HTMLElements (e.g., buttons); update
isDialogPortalProps to first exclude DOM nodes by checking that p is not an
HTMLElement (or Node) instance before testing for 'to', 'disabled', 'defer', or
'forceMount' properties so real DOM elements are never considered
DialogPortalProps; locate the isDialogPortalProps function and add the
HTMLElement/Node exclusion prior to the property-in checks.


export function usePortal(portal: Ref<PortalProps | undefined>) {
const globalPortal = inject(portalTargetInjectionKey, undefined)

const value = computed(() => portal.value === true ? globalPortal?.value : portal.value)
const value = computed((): boolean | string | HTMLElement | undefined => {
const p = portal.value

if (p === true) {
return globalPortal?.value
}

if (isDialogPortalProps(p)) {
return p.to
}

return p
})

const disabled = computed(() => {
const p = portal.value

if (isDialogPortalProps(p) && p.disabled !== undefined) {
return p.disabled
}

return typeof value.value === 'boolean' ? !value.value : false
})

const to = computed(() => {
if (isDialogPortalProps(portal.value) && (typeof portal.value?.to === 'boolean' || typeof portal.value?.to === 'undefined')) {
return 'body'
}

if (typeof value.value === 'boolean') {
return 'body'
}

const disabled = computed(() => typeof value.value === 'boolean' ? !value.value : false)
const to = computed(() => typeof value.value === 'boolean' ? 'body' : value.value)
return value.value
})
const forceMount = computed(() => {
const p = portal.value
return isDialogPortalProps(p) && p.forceMount === true
})

return computed(() => ({
to: to.value,
disabled: disabled.value
disabled: disabled.value,
forceMount: forceMount.value
}))
Comment on lines +51 to 60
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "usePortal.ts" -o -name "*Portal*" | head -20

Repository: nuxt/ui

Length of output: 89


🏁 Script executed:

cat -n ./src/runtime/composables/usePortal.ts

Repository: nuxt/ui

Length of output: 2141


🏁 Script executed:

rg "usePortal" -A 5 -B 2 --type ts | head -50

Repository: nuxt/ui

Length of output: 588


🏁 Script executed:

rg "from.*usePortal|import.*usePortal" --type ts -A 3

Repository: nuxt/ui

Length of output: 33


🏁 Script executed:

rg "usePortal" --type ts -B 2 -A 5

Repository: nuxt/ui

Length of output: 588


🏁 Script executed:

fd -e ts -e tsx | xargs grep -l "usePortal" 2>/dev/null | head -10

Repository: nuxt/ui

Length of output: 87


🏁 Script executed:

fd -type f -name "*Dialog*" -o -name "*dialog*" | grep -E "\.(ts|tsx|vue)$"

Repository: nuxt/ui

Length of output: 221


🏁 Script executed:

fd "Dialog|dialog" -e ts -e tsx -e vue

Repository: nuxt/ui

Length of output: 33


🏁 Script executed:

ls -la src/runtime/ && echo "---" && find src/runtime -maxdepth 2 -type d

Repository: nuxt/ui

Length of output: 1121


🏁 Script executed:

ls src/runtime/components/ && echo "---" && find src/runtime/components -maxdepth 1 -type d

Repository: nuxt/ui

Length of output: 1925


🏁 Script executed:

cat -n src/runtime/components/Modal.vue | head -100

Repository: nuxt/ui

Length of output: 3880


🏁 Script executed:

cat -n src/runtime/components/Modal.vue | sed -n '100,200p'

Repository: nuxt/ui

Length of output: 4653


🏁 Script executed:

cat -n src/runtime/components/Modal.vue | sed -n '200,250p'

Repository: nuxt/ui

Length of output: 1270


🏁 Script executed:

grep -r "DialogPortal" --include="*.ts" --include="*.tsx" -A 5 -B 2 | head -50

Repository: nuxt/ui

Length of output: 3151


🏁 Script executed:

cat package.json | grep -A 20 "dependencies"

Repository: nuxt/ui

Length of output: 835


🏁 Script executed:

cat package.json | grep "reka-ui"

Repository: nuxt/ui

Length of output: 74


Forward defer property from portal props to DialogPortal component.

Line 10 of the guard confirms defer is a valid property in DialogPortalProps, but lines 56-60 show it is not forwarded in the returned object. This causes portal: { defer: true } to be silently ignored. Since forceMount is forwarded, defer should be handled consistently:

Suggested fix
+  const defer = computed(() => {
+    const p = portal.value
+    return isDialogPortalProps(p) ? p.defer : undefined
+  })
   return computed(() => ({
     to: to.value,
     disabled: disabled.value,
-    forceMount: forceMount.value
+    forceMount: forceMount.value,
+    defer: defer.value
   }))
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const forceMount = computed(() => {
const p = portal.value
return isDialogPortalProps(p) && p.forceMount === true
})
return computed(() => ({
to: to.value,
disabled: disabled.value
disabled: disabled.value,
forceMount: forceMount.value
}))
const forceMount = computed(() => {
const p = portal.value
return isDialogPortalProps(p) && p.forceMount === true
})
const defer = computed(() => {
const p = portal.value
return isDialogPortalProps(p) ? p.defer : undefined
})
return computed(() => ({
to: to.value,
disabled: disabled.value,
forceMount: forceMount.value,
defer: defer.value
}))
πŸ€– Prompt for AI Agents
In `@src/runtime/composables/usePortal.ts` around lines 51 - 60, The returned
computed object omits the defer flag from DialogPortalProps, so when
portal.value is a DialogPortalProps the defer true/false is ignored; update the
computed return (the function that currently returns { to: to.value, disabled:
disabled.value, forceMount: forceMount.value }) to also include defer, e.g.
compute defer the same way as forceMount by checking portal via
isDialogPortalProps(portal.value) and forwarding p.defer (or false if absent),
ensuring the new property name is defer and handled alongside forceMount, to and
disabled.

}
1 change: 1 addition & 0 deletions test/components/Modal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('Modal', () => {
['with closeIcon', { props: { ...props, closeIcon: 'i-lucide-trash' } }],
['with class', { props: { ...props, class: 'bg-elevated' } }],
['with ui', { props: { ...props, ui: { close: 'end-2' } } }],
['with forceMount', { props: { ...props, portal: { forceMount: true, disabled: true } } }],
// Slots
['with default slot', { props, slots: { default: () => 'Default slot' } }],
['with content slot', { props, slots: { content: () => 'Content slot' } }],
Expand Down
27 changes: 27 additions & 0 deletions test/components/__snapshots__/Modal-vue.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,33 @@ exports[`Modal > renders with footer slot correctly 1`] = `



<!--teleport end-->"
`;

exports[`Modal > renders with forceMount correctly 1`] = `
"<!--v-if-->
<!--teleport start-->



<div data-state="open" style="pointer-events: auto;" data-slot="overlay" class="fixed inset-0 data-[state=open]:animate-[fade-in_200ms_ease-out] data-[state=closed]:animate-[fade-out_200ms_ease-in] bg-elevated/75"></div>
<div data-dismissable-layer="" style="pointer-events: auto;" tabindex="-1" data-slot="content" class="bg-default divide-y divide-default flex flex-col focus:outline-none data-[state=open]:animate-[scale-in_200ms_ease-out] data-[state=closed]:animate-[scale-out_200ms_ease-in] w-[calc(100vw-2rem)] max-w-lg rounded-lg shadow-lg ring ring-default fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 max-h-[calc(100dvh-2rem)] sm:max-h-[calc(100dvh-4rem)] overflow-hidden" id="" role="dialog" aria-describedby="reka-dialog-description-v-1" aria-labelledby="reka-dialog-title-v-0" data-state="open">
<!--v-if-->
<div data-slot="header" class="flex items-center gap-1.5 p-4 sm:px-6 min-h-16">
<div data-slot="wrapper" class="">
<!--v-if-->
<!--v-if-->
</div><button type="button" aria-label="Close" data-slot="base" class="rounded-md font-medium inline-flex items-center disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-default hover:bg-elevated active:bg-elevated focus:outline-none focus-visible:bg-elevated hover:disabled:bg-transparent dark:hover:disabled:bg-transparent hover:aria-disabled:bg-transparent dark:hover:aria-disabled:bg-transparent p-1.5 absolute top-4 end-4"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" data-slot="leadingIcon" class="shrink-0 size-5"></svg>
<!--v-if-->
<!--v-if-->
</button>
</div>
<!--v-if-->
<!--v-if-->
</div>



<!--teleport end-->"
`;

Expand Down
27 changes: 27 additions & 0 deletions test/components/__snapshots__/Modal.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,33 @@ exports[`Modal > renders with footer slot correctly 1`] = `



<!--teleport end-->"
`;

exports[`Modal > renders with forceMount correctly 1`] = `
"<!--v-if-->
<!--teleport start-->



<div data-state="open" style="pointer-events: auto;" data-slot="overlay" class="fixed inset-0 data-[state=open]:animate-[fade-in_200ms_ease-out] data-[state=closed]:animate-[fade-out_200ms_ease-in] bg-elevated/75"></div>
<div data-dismissable-layer="" style="pointer-events: auto;" tabindex="-1" data-slot="content" class="bg-default divide-y divide-default flex flex-col focus:outline-none data-[state=open]:animate-[scale-in_200ms_ease-out] data-[state=closed]:animate-[scale-out_200ms_ease-in] w-[calc(100vw-2rem)] max-w-lg rounded-lg shadow-lg ring ring-default fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 max-h-[calc(100dvh-2rem)] sm:max-h-[calc(100dvh-4rem)] overflow-hidden" id="" role="dialog" aria-describedby="reka-dialog-description-v-0-0-1" aria-labelledby="reka-dialog-title-v-0-0-0" data-state="open">
<!--v-if-->
<div data-slot="header" class="flex items-center gap-1.5 p-4 sm:px-6 min-h-16">
<div data-slot="wrapper" class="">
<!--v-if-->
<!--v-if-->
</div><button type="button" aria-label="Close" data-slot="base" class="rounded-md font-medium inline-flex items-center disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-default hover:bg-elevated active:bg-elevated focus:outline-none focus-visible:bg-elevated hover:disabled:bg-transparent dark:hover:disabled:bg-transparent hover:aria-disabled:bg-transparent dark:hover:aria-disabled:bg-transparent p-1.5 absolute top-4 end-4"><span class="iconify i-lucide:x shrink-0 size-5" aria-hidden="true" data-slot="leadingIcon"></span>
<!--v-if-->
<!--v-if-->
</button>
</div>
<!--v-if-->
<!--v-if-->
</div>



<!--teleport end-->"
`;

Expand Down
Loading