Skip to content

Popover

Popover can be used for dropdowns and other popovers when element is clicked and focused.

View Source 

Usage

css
@import "winduum/src/components/popover/index.css" layer(components);
html
<div class="c-popover focus">
    <div role="button" class="ui-btn" tabindex="0">Popover</div>
    <div class="c-popover-content center shadow dark:bg-body-secondary mt-2.5 p-2 w-32 flex-col">
        <button class="ui-btn ghosted accent-main justify-start w-full">Item 1</button>
        <button class="ui-btn ghosted accent-main justify-start w-full">Item 2</button>
    </div>
</div>
vue
<script setup>
    import { ref } from 'vue'
    import { Popover, PopoverContent } from '@/components/popover'
    import { UiBtn } from '@/components/ui/btn'
</script>

<template>
    <Popover>
        <UiBtn>Open Popover</UiBtn>
        <PopoverContent>
            Popover content
        </PopoverContent>
    </Popover>
</template>

Variants

  • default 
  • content 

Tokens

Applicable to c-popover-content

  • bottom
  • bottom-start
  • bottom-end
  • right
  • right-start
  • right-end
  • left
  • left-start
  • left-end
  • top
  • top-start
  • top-end
  • end
  • block-end
  • center
  • block-center

Installation

Follow instructions for individual framework usage below

  • winduum 
  • winduum-vue 
  • winduum-react 

Examples

Basic

html
<div class="c-popover focus">
    <div role="button" class="ui-btn" tabindex="0">Popover</div>
    <div class="c-popover-content center shadow dark:bg-body-secondary mt-2.5 p-2 w-32 flex-col">
        <button class="ui-btn ghosted accent-main justify-start w-full">Item 1</button>
        <button class="ui-btn ghosted accent-main justify-start w-full">Item 2</button>
    </div>
</div>

Hover

html
<div class="c-popover hover">
    <div role="button" class="ui-btn" tabindex="0">Popover</div>
    <div class="c-popover-content center shadow dark:bg-body-secondary mt-2.5 p-2 w-32 flex-col">
        <button class="ui-btn ghosted accent-main justify-start w-full">Item 1</button>
        <button class="ui-btn ghosted accent-main justify-start w-full">Item 2</button>
    </div>
</div>

Popover API

This is using advantages of Popover API with floating-ui. It's also backwards compatible as it's leveraging only the showPopover and hidePopover for a top-layer support.

Popover is placed dynamically upon available space, and auto updates itself when needed.

html
<div class="c-popover w-full flex-col items-center">
    <button class="ui-btn" popovertargetaction="toggle" popovertarget="popover">Popover</button>
    <div class="c-popover-content shadow dark:bg-body-secondary p-2" id="popover" popover="manual">
        <input type="text" autofocus>
    </div>
</div>

JavaScript API

showPopover

  • Type: (element: HTMLElement | Element, options?: ShowPopoverOptions) => Promise<void>
  • Kind: async

Example

js
import { showPopover, hidePopover } from '/src/components/popover'

const popoverActionElement = document?.querySelector('[popovertargetaction="show"]')

popoverActionElement?.addEventListener('click', async (e) => {
    e.preventDefault()
    
    const currentTarget = e.currentTarget
    await showPopover(currentTarget, {
        placement: 'right-end',
    })
})

// close on esc
window.addEventListener('keydown', ({ key }) => {
    if (key === 'Escape') {
        hidePopover(popoverActionElement)
    }
})

// outside dismiss
window.addEventListener('click', ({ target }) => {
    if (!window.popover?.contains(target) && !popoverActionElement?.isEqualNode(target) && popoverActionElement?.ariaExpanded === 'true') {
        hidePopover(popoverActionElement)
    }
})

ShowPopoverOptions


visibleClass
  • Type: string
  • Default: in

A string representing a CSS class that will be added when popover is visible.


compute
  • Type: boolean
  • Default: true

Determines if the popover should be anchored and computed with @floating-ui/dom


placement
  • Type: Placement
  • Default: undefined

Determines placement of the popover with @floating-ui/dom, also adds a corresponding class to the popover target.


middleware
  • Type: Array<Middleware | null | undefined | false>
  • Default: [offset(12 ?? options?.offset), flip(options?.flip), shift({ padding: 8, ...options?.shift })]

Customize middleware for @floating-ui/dom


offset
  • Type: OffsetOptions
  • Default: 12

Customize offset options for @floating-ui/dom


flip
  • Type: FlipOptions
  • Default: undefined

Customize flip options for @floating-ui/dom


shift
  • Type: ShiftOptions
  • Default: undefined

Customize shift options for @floating-ui/dom



hidePopover

  • Type: (element: HTMLElement | Element) => Promise<void>
  • Kind: async

Example

js
import { hidePopover } from '/src/components/popover'

const popoverActionElement = document?.querySelector('[popovertargetaction="hide"]')

popoverActionElement?.addEventListener('click', async (e) => {
    e.preventDefault()
    
    const currentTarget = e.currentTarget
    await hidePopover(currentTarget)
})

togglePopover

  • Type: (element: HTMLElement | Element, options?: ShowPopoverOptions) => Promise<void>
  • Kind: async

Example

js
import { togglePopover, hidePopover } from '/src/components/popover'

const popoverActionElement = document?.querySelector('[popovertargetaction="toggle"]')

popoverActionElement?.addEventListener('click', async (e) => {
    e.preventDefault()
    
    const currentTarget = e.currentTarget
    await togglePopover(currentTarget)
})

// close on esc
window.addEventListener('keydown', ({ key }) => {
    if (key === 'Escape') {
        hidePopover(popoverActionElement)
    }
})

// outside dismiss
window.addEventListener('click', ({ target }) => {
    if (!window.popover?.contains(target) && !popoverActionElement?.isEqualNode(target) && popoverActionElement?.ariaExpanded === 'true') {
        hidePopover(popoverActionElement)
    }
})

Released under the MIT License.