mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
feat(Popover): add anchor slot (#4119)
Co-authored-by: Jakub <jakub.michalek@freelo.io> Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
const open = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPopover
|
||||
v-model:open="open"
|
||||
:dismissible="false"
|
||||
:ui="{ content: 'w-(--reka-popper-anchor-width) p-4' }"
|
||||
>
|
||||
<template #anchor>
|
||||
<UInput placeholder="Focus to open" @focus="open = true" @blur="open = false" />
|
||||
</template>
|
||||
|
||||
<template #content>
|
||||
<Placeholder class="w-full aspect-square" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</template>
|
||||
@@ -202,6 +202,21 @@ name: 'popover-command-palette-example'
|
||||
---
|
||||
::
|
||||
|
||||
### With anchor slot
|
||||
|
||||
You can use the `#anchor` slot to position the Popover against a custom element.
|
||||
|
||||
::warning
|
||||
This slot only works when `mode` is `click`.
|
||||
::
|
||||
|
||||
::component-example
|
||||
---
|
||||
collapse: true
|
||||
name: 'popover-anchor-slot-example'
|
||||
---
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
const open = ref(false)
|
||||
const openCustomAnchor = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
function send() {
|
||||
@@ -51,6 +52,21 @@ function send() {
|
||||
</div>
|
||||
</template>
|
||||
</UPopover>
|
||||
|
||||
<div class="mt-8 relative">
|
||||
<UPopover
|
||||
v-model:open="openCustomAnchor"
|
||||
:dismissible="false"
|
||||
>
|
||||
<template #anchor>
|
||||
<UInput placeholder="Search" class="w-56" @focus="openCustomAnchor = true" />
|
||||
</template>
|
||||
|
||||
<template #content>
|
||||
<Placeholder class="size-48 m-4 inline-flex" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-24">
|
||||
|
||||
@@ -43,6 +43,7 @@ export interface PopoverEmits extends PopoverRootEmits {
|
||||
export interface PopoverSlots {
|
||||
default(props: { open: boolean }): any
|
||||
content(props?: {}): any
|
||||
anchor(props?: {}): any
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -103,6 +104,10 @@ const Component = computed(() => props.mode === 'hover' ? HoverCard : Popover)
|
||||
<slot :open="open" />
|
||||
</Component.Trigger>
|
||||
|
||||
<Component.Anchor v-if="'Anchor' in Component && !!slots.anchor" as-child>
|
||||
<slot name="anchor" />
|
||||
</Component.Anchor>
|
||||
|
||||
<Component.Portal v-bind="portalProps">
|
||||
<Component.Content v-bind="contentProps" :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })" v-on="contentEvents">
|
||||
<slot name="content" />
|
||||
|
||||
@@ -13,7 +13,8 @@ describe('Popover', () => {
|
||||
['with ui', { props: { ...props, ui: { content: 'shadow-xl' } } }],
|
||||
// Slots
|
||||
['with default slot', { props, slots: { default: () => 'Default slot' } }],
|
||||
['with content slot', { props, slots: { content: () => 'Content slot' } }]
|
||||
['with content slot', { props, slots: { content: () => 'Content slot' } }],
|
||||
['with anchor slot', { props, slots: { anchor: () => 'Anchor slot' } }]
|
||||
])('renders %s correctly', async (nameOrHtml: string, options: { props?: PopoverProps, slots?: Partial<PopoverSlots> }) => {
|
||||
const html = await ComponentRender(nameOrHtml, options, Popover)
|
||||
expect(html).toMatchSnapshot()
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Popover > renders with anchor slot correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
Anchor slot
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
<div data-reka-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
|
||||
<div id="reka-popover-content-v-0" data-state="open" aria-labelledby="" style="--reka-popover-content-transform-origin: var(--reka-popper-transform-origin); --reka-popover-content-available-width: var(--reka-popper-available-width); --reka-popover-content-available-height: var(--reka-popper-available-height); --reka-popover-trigger-width: var(--reka-popper-anchor-width); --reka-popover-trigger-height: var(--reka-popper-anchor-height); animation: none;" role="dialog" data-dismissable-layer="" tabindex="-1" class="bg-default shadow-lg rounded-md ring ring-default data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] origin-(--reka-popover-content-transform-origin) focus:outline-none pointer-events-auto" data-side="bottom" data-align="center"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--teleport end-->"
|
||||
`;
|
||||
|
||||
exports[`Popover > renders with arrow correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
@@ -15,6 +30,7 @@ exports[`Popover > renders with arrow correctly 1`] = `
|
||||
|
||||
exports[`Popover > renders with class correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
@@ -28,6 +44,7 @@ exports[`Popover > renders with class correctly 1`] = `
|
||||
|
||||
exports[`Popover > renders with content slot correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
@@ -43,6 +60,7 @@ exports[`Popover > renders with content slot correctly 1`] = `
|
||||
|
||||
exports[`Popover > renders with default slot correctly 1`] = `
|
||||
"Default slot
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
@@ -56,6 +74,7 @@ exports[`Popover > renders with default slot correctly 1`] = `
|
||||
|
||||
exports[`Popover > renders with open correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
@@ -69,6 +88,7 @@ exports[`Popover > renders with open correctly 1`] = `
|
||||
|
||||
exports[`Popover > renders with ui correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Popover > renders with anchor slot correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
Anchor slot
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
<div data-reka-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
|
||||
<div id="reka-popover-content-v-0-0-0" data-state="open" aria-labelledby="" style="--reka-popover-content-transform-origin: var(--reka-popper-transform-origin); --reka-popover-content-available-width: var(--reka-popper-available-width); --reka-popover-content-available-height: var(--reka-popper-available-height); --reka-popover-trigger-width: var(--reka-popper-anchor-width); --reka-popover-trigger-height: var(--reka-popper-anchor-height); animation: none;" role="dialog" data-dismissable-layer="" tabindex="-1" class="bg-default shadow-lg rounded-md ring ring-default data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] origin-(--reka-popover-content-transform-origin) focus:outline-none pointer-events-auto" data-side="bottom" data-align="center"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--teleport end-->"
|
||||
`;
|
||||
|
||||
exports[`Popover > renders with arrow correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
@@ -15,6 +30,7 @@ exports[`Popover > renders with arrow correctly 1`] = `
|
||||
|
||||
exports[`Popover > renders with class correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
@@ -28,6 +44,7 @@ exports[`Popover > renders with class correctly 1`] = `
|
||||
|
||||
exports[`Popover > renders with content slot correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
@@ -43,6 +60,7 @@ exports[`Popover > renders with content slot correctly 1`] = `
|
||||
|
||||
exports[`Popover > renders with default slot correctly 1`] = `
|
||||
"Default slot
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
@@ -56,6 +74,7 @@ exports[`Popover > renders with default slot correctly 1`] = `
|
||||
|
||||
exports[`Popover > renders with open correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
@@ -69,6 +88,7 @@ exports[`Popover > renders with open correctly 1`] = `
|
||||
|
||||
exports[`Popover > renders with ui correctly 1`] = `
|
||||
"<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user