Skip to content

Commit 473513c

Browse files
J-MichalekJakubbenjamincanac
authored
feat(Popover): add anchor slot (#4119)
Co-authored-by: Jakub <[email protected]> Co-authored-by: Benjamin Canac <[email protected]>
1 parent fe4e1f8 commit 473513c

File tree

7 files changed

+97
-1
lines changed

7 files changed

+97
-1
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script lang="ts" setup>
2+
const open = ref(false)
3+
</script>
4+
5+
<template>
6+
<UPopover
7+
v-model:open="open"
8+
:dismissible="false"
9+
:ui="{ content: 'w-(--reka-popper-anchor-width) p-4' }"
10+
>
11+
<template #anchor>
12+
<UInput placeholder="Focus to open" @focus="open = true" @blur="open = false" />
13+
</template>
14+
15+
<template #content>
16+
<Placeholder class="w-full aspect-square" />
17+
</template>
18+
</UPopover>
19+
</template>

docs/content/3.components/popover.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,21 @@ name: 'popover-command-palette-example'
202202
---
203203
::
204204

205+
### With anchor slot
206+
207+
You can use the `#anchor` slot to position the Popover against a custom element.
208+
209+
::warning
210+
This slot only works when `mode` is `click`.
211+
::
212+
213+
::component-example
214+
---
215+
collapse: true
216+
name: 'popover-anchor-slot-example'
217+
---
218+
::
219+
205220
## API
206221

207222
### Props

playground/app/pages/components/popover.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
const open = ref(false)
3+
const openCustomAnchor = ref(false)
34
const loading = ref(false)
45
56
function send() {
@@ -51,6 +52,21 @@ function send() {
5152
</div>
5253
</template>
5354
</UPopover>
55+
56+
<div class="mt-8 relative">
57+
<UPopover
58+
v-model:open="openCustomAnchor"
59+
:dismissible="false"
60+
>
61+
<template #anchor>
62+
<UInput placeholder="Search" class="w-56" @focus="openCustomAnchor = true" />
63+
</template>
64+
65+
<template #content>
66+
<Placeholder class="size-48 m-4 inline-flex" />
67+
</template>
68+
</UPopover>
69+
</div>
5470
</div>
5571

5672
<div class="mt-24">

src/runtime/components/Popover.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export interface PopoverEmits extends PopoverRootEmits {
4343
export interface PopoverSlots {
4444
default(props: { open: boolean }): any
4545
content(props?: {}): any
46+
anchor(props?: {}): any
4647
}
4748
</script>
4849

@@ -103,6 +104,10 @@ const Component = computed(() => props.mode === 'hover' ? HoverCard : Popover)
103104
<slot :open="open" />
104105
</Component.Trigger>
105106

107+
<Component.Anchor v-if="'Anchor' in Component && !!slots.anchor" as-child>
108+
<slot name="anchor" />
109+
</Component.Anchor>
110+
106111
<Component.Portal v-bind="portalProps">
107112
<Component.Content v-bind="contentProps" :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })" v-on="contentEvents">
108113
<slot name="content" />

test/components/Popover.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ describe('Popover', () => {
1313
['with ui', { props: { ...props, ui: { content: 'shadow-xl' } } }],
1414
// Slots
1515
['with default slot', { props, slots: { default: () => 'Default slot' } }],
16-
['with content slot', { props, slots: { content: () => 'Content slot' } }]
16+
['with content slot', { props, slots: { content: () => 'Content slot' } }],
17+
['with anchor slot', { props, slots: { anchor: () => 'Anchor slot' } }]
1718
])('renders %s correctly', async (nameOrHtml: string, options: { props?: PopoverProps, slots?: Partial<PopoverSlots> }) => {
1819
const html = await ComponentRender(nameOrHtml, options, Popover)
1920
expect(html).toMatchSnapshot()

test/components/__snapshots__/Popover-vue.spec.ts.snap

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`Popover > renders with anchor slot correctly 1`] = `
4+
"<!--v-if-->
5+
Anchor slot
6+
<!--teleport start-->
7+
8+
9+
<div data-reka-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
10+
<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>
11+
</div>
12+
13+
14+
<!--teleport end-->"
15+
`;
16+
317
exports[`Popover > renders with arrow correctly 1`] = `
418
"<!--v-if-->
19+
<!--v-if-->
520
<!--teleport start-->
621
722
@@ -15,6 +30,7 @@ exports[`Popover > renders with arrow correctly 1`] = `
1530

1631
exports[`Popover > renders with class correctly 1`] = `
1732
"<!--v-if-->
33+
<!--v-if-->
1834
<!--teleport start-->
1935
2036
@@ -28,6 +44,7 @@ exports[`Popover > renders with class correctly 1`] = `
2844

2945
exports[`Popover > renders with content slot correctly 1`] = `
3046
"<!--v-if-->
47+
<!--v-if-->
3148
<!--teleport start-->
3249
3350
@@ -43,6 +60,7 @@ exports[`Popover > renders with content slot correctly 1`] = `
4360

4461
exports[`Popover > renders with default slot correctly 1`] = `
4562
"Default slot
63+
<!--v-if-->
4664
<!--teleport start-->
4765
4866
@@ -56,6 +74,7 @@ exports[`Popover > renders with default slot correctly 1`] = `
5674

5775
exports[`Popover > renders with open correctly 1`] = `
5876
"<!--v-if-->
77+
<!--v-if-->
5978
<!--teleport start-->
6079
6180
@@ -69,6 +88,7 @@ exports[`Popover > renders with open correctly 1`] = `
6988

7089
exports[`Popover > renders with ui correctly 1`] = `
7190
"<!--v-if-->
91+
<!--v-if-->
7292
<!--teleport start-->
7393
7494

test/components/__snapshots__/Popover.spec.ts.snap

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`Popover > renders with anchor slot correctly 1`] = `
4+
"<!--v-if-->
5+
Anchor slot
6+
<!--teleport start-->
7+
8+
9+
<div data-reka-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
10+
<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>
11+
</div>
12+
13+
14+
<!--teleport end-->"
15+
`;
16+
317
exports[`Popover > renders with arrow correctly 1`] = `
418
"<!--v-if-->
19+
<!--v-if-->
520
<!--teleport start-->
621
722
@@ -15,6 +30,7 @@ exports[`Popover > renders with arrow correctly 1`] = `
1530

1631
exports[`Popover > renders with class correctly 1`] = `
1732
"<!--v-if-->
33+
<!--v-if-->
1834
<!--teleport start-->
1935
2036
@@ -28,6 +44,7 @@ exports[`Popover > renders with class correctly 1`] = `
2844

2945
exports[`Popover > renders with content slot correctly 1`] = `
3046
"<!--v-if-->
47+
<!--v-if-->
3148
<!--teleport start-->
3249
3350
@@ -43,6 +60,7 @@ exports[`Popover > renders with content slot correctly 1`] = `
4360

4461
exports[`Popover > renders with default slot correctly 1`] = `
4562
"Default slot
63+
<!--v-if-->
4664
<!--teleport start-->
4765
4866
@@ -56,6 +74,7 @@ exports[`Popover > renders with default slot correctly 1`] = `
5674

5775
exports[`Popover > renders with open correctly 1`] = `
5876
"<!--v-if-->
77+
<!--v-if-->
5978
<!--teleport start-->
6079
6180
@@ -69,6 +88,7 @@ exports[`Popover > renders with open correctly 1`] = `
6988

7089
exports[`Popover > renders with ui correctly 1`] = `
7190
"<!--v-if-->
91+
<!--v-if-->
7292
<!--teleport start-->
7393
7494

0 commit comments

Comments
 (0)