Skip to content

Commit

Permalink
fix(Popover): allow manual mode without blocking normal behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamincanac committed Jan 3, 2024
1 parent 3844714 commit 3334e2a
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 36 deletions.
25 changes: 13 additions & 12 deletions docs/components/content/examples/PopoverExampleOpen.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
<script setup>
const open = ref(false)
const open = ref(true)
defineShortcuts({
o: () => open.value = !open.value
})
</script>

<template>
<div class="flex gap-4 items-center">
<UToggle v-model="open" />
<UPopover :open="open">
<UButton color="white" label="Open" trailing-icon="i-heroicons-chevron-down-20-solid" />
<UPopover v-model:open="open">
<UButton color="white" :label="open.toString()" trailing-icon="i-heroicons-chevron-down-20-solid" />

<template #panel>
<div class="p-4">
<Placeholder class="h-20 w-48" />
</div>
</template>
</UPopover>
</div>
<template #panel>
<div class="p-4">
<Placeholder class="h-20 w-48" />
</div>
</template>
</UPopover>
</template>
18 changes: 8 additions & 10 deletions docs/components/content/examples/PopoverExampleOverlay.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
<template>
<div class="flex gap-4 items-center">
<UPopover overlay>
<UButton color="white" label="Open" trailing-icon="i-heroicons-chevron-down-20-solid" />
<UPopover overlay>
<UButton color="white" label="Open" trailing-icon="i-heroicons-chevron-down-20-solid" />

<template #panel>
<div class="p-4">
<Placeholder class="h-20 w-48" />
</div>
</template>
</UPopover>
</div>
<template #panel>
<div class="p-4">
<Placeholder class="h-20 w-48" />
</div>
</template>
</UPopover>
</template>
6 changes: 1 addition & 5 deletions docs/content/6.overlays/3.popover.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,14 @@ Use the `mode` prop to switch between `click` and `hover` modes.

### Manual

Use the `open` prop to manually control showing the panel.
Use a `v-model:open` to manually control the state. In this example, press :shortcut{value="O"} to toggle the popover.

:component-example{component="popover-example-open"}

### Overlay

:component-example{component="popover-example-overlay"}

::callout{icon="i-heroicons-light-bulb"}
Clicking on the `overlay` emits `update:open`. If you are manually controlling the `open` prop, you will need to use a [`v-model` argument](https://vuejs.org/guide/components/v-model.html#v-model-arguments) (`v-model:open`).
::

## Popper

Use the `popper` prop to customize the popper instance.
Expand Down
36 changes: 27 additions & 9 deletions src/runtime/components/overlays/Popover.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<HPopover ref="popover" v-slot="{ open: headlessOpen, close }" :class="ui.wrapper" v-bind="attrs" @mouseleave="onMouseLeave">
<!-- eslint-disable-next-line vue/no-template-shadow -->
<HPopover ref="popover" v-slot="{ open, close }" :class="ui.wrapper" v-bind="attrs" @mouseleave="onMouseLeave">
<HPopoverButton
ref="trigger"
as="div"
Expand All @@ -8,24 +9,24 @@
role="button"
@mouseover="onMouseOver"
>
<slot :open="(open !== undefined) ? open : headlessOpen" :close="close">
<slot :open="open" :close="close">
<button :disabled="disabled">
Open
</button>
</slot>
</HPopoverButton>

<Transition v-if="overlay" appear v-bind="ui.overlay.transition">
<div v-if="(open !== undefined) ? open : headlessOpen" :class="[ui.overlay.base, ui.overlay.background]" @click="$emit('update:open')" />
<div v-if="open" :class="[ui.overlay.base, ui.overlay.background]" />
</Transition>

<div v-if="(open !== undefined) ? open : headlessOpen" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseover="onMouseOver">
<div v-if="open" ref="container" :class="[ui.container, ui.width]" :style="containerStyle" @mouseover="onMouseOver">
<Transition appear v-bind="ui.transition">
<div>
<div v-if="popper.arrow" data-popper-arrow :class="Object.values(ui.arrow)" />

<HPopoverPanel :class="[ui.base, ui.background, ui.ring, ui.rounded, ui.shadow]" static>
<slot name="panel" :open="(open !== undefined) ? open : headlessOpen" :close="close" />
<slot name="panel" :open="open" :close="close" />
</HPopoverPanel>
</div>
</Transition>
Expand Down Expand Up @@ -94,16 +95,16 @@ export default defineComponent({
default: () => ({})
}
},
emits: ['update:open', 'open', 'close'],
emits: ['update:open'],
setup (props, { emit }) {
const { ui, attrs } = useUI('popover', toRef(props, 'ui'), config, toRef(props, 'class'))
const popper = computed<PopperOptions>(() => defu(props.mode === 'hover' ? { offsetDistance: 0 } : {}, props.popper, ui.value.popper as PopperOptions))
const [trigger, container] = usePopper(popper.value)
// https://github.com/tailwindlabs/headlessui/blob/f66f4926c489fc15289d528294c23a3dc2aee7b1/packages/%40headlessui-vue/src/components/popover/popover.ts#L151
const popover = ref<any>(null)
// https://github.com/tailwindlabs/headlessui/blob/f66f4926c489fc15289d528294c23a3dc2aee7b1/packages/%40headlessui-vue/src/components/popover/popover.ts#L151
const popoverApi = ref<any>(null)
let openTimeout: NodeJS.Timeout | null = null
Expand All @@ -116,6 +117,10 @@ export default defineComponent({
}
const popoverProvidesSymbols = Object.getOwnPropertySymbols(popoverProvides)
popoverApi.value = popoverProvidesSymbols.length && popoverProvides[popoverProvidesSymbols[0]]
if (props.open) {
popoverApi.value?.togglePopover()
}
})
const containerStyle = computed(() => {
Expand Down Expand Up @@ -170,9 +175,22 @@ export default defineComponent({
}, props.closeDelay)
}
watch(() => props.open, (newValue: boolean, oldValue: boolean) => {
if (!popoverApi.value) return
if (oldValue === undefined || newValue === oldValue) return
if (newValue) {
// No `openPopover` method and `popoverApi.value.togglePopover` won't work because of the `watch` below
popoverApi.value.popoverState = 0
} else {
popoverApi.value.closePopover()
}
})
watch(() => popoverApi.value?.popoverState, (newValue: number, oldValue: number) => {
if (oldValue === undefined) return
emit(newValue === 0 ? 'open' : 'close')
if (oldValue === undefined || newValue === oldValue) return
emit('update:open', newValue === 0)
})
return {
Expand Down

1 comment on commit 3334e2a

@vercel
Copy link

@vercel vercel bot commented on 3334e2a Jan 3, 2024

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

ui – ./

ui-git-dev-nuxt-js.vercel.app
ui-nuxt-js.vercel.app
ui.nuxt.com

Please sign in to comment.