npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@qyu/reactcmp-dropdown

v2.2.0

Published

React Components for Dropdown Positioning

Downloads

216

Readme

@qyu/reactcmp-dropdown

React Component Library for solving dropdown positioning problems

Purpose

Library is intended as minimalistic solution for dropdown rendering

It supportas:

  • Nested Dropdpowns
  • List is rendered in Portal (no issues with overflow)
  • Basic focus management (for complex cases you would need external library)
  • Basic animations with css transitions (for complex animations you would need external library)

First include styles

// import styles with your bundler or copy them by hand and reference in your html
import "@qyu/reactcmp-dropdown/style/index"
// or import module and insert them directly to the component
import clmap from "@qyu/reactcmp-dropdown/stlye/index"

Basic usage

import * as ddn from "qyu/reactcmp-dropdown"

// will render dropdown
function DDN(props: { children: r.Children }) {
    // virtual container will not render any dom element, only create necessary contexts
    // there also a Component that renders a real one
    return <ddn.CmpContainerVirtual>
        <ddn.CmpButton>
            Press
        </ddn.CmpButton>

        <ddn.CmpListPortal
            gap={5}
            // find by id, you can pass HTLMElement directly alternatively
            portal={"ddnportal"}

            // optional
            direction={"ver"}
            // optional
            align={"start"}
            // optional
            justify={"end"}
            // optional
            stretch={"min"}
        >
            {/* Used to measure content. Required for any list */}
            <ddn.Content>
                {props.children}
            </ddn.Content>
        </ddn.CmpListPortal>
    </ddn.CmpContainer>
}

// nested dropdowns
function App() {
    // if you want to render nested dropdown 
    // - it will need its own context with <ddn.CmpContainerVirtual>
    // therefore - this works
    return <DDN>
        <DDN>
            List Content
        </DDN>
    </DDN>
}

Change Transition time and z-index

:root {
    --qyuddn-z-index: 3;
    --qyuddn-trtime: 0.2s;
}

Making your custom button

const CustomButton = function () {
    const ref = r.useRef<HTMLButtonElement | null>(null)
    const ctxstate_open = ddn.useCtxStateOpen()

    // this will give you calculated value of wether your dropdown is open or not
    const open = ddn.useOpenInfer({})

    // this wrapper is required, it registers necessary events and stuff
    return <ddn.ButtonVirtual target={ref}>
        <button 
            ref={ref} 
            onClick={() => ctxstate_open.open_set(o => !o)}
        >
            MyButton
        </button>
    </ddn.ButtonVirtual>
}

Usage with all properties

import * as ddn from "qyu/reactcmp-dropdown"

function App() {
    // it is generally recomended to use local state instead of declaring it here
    const [open, open_set] = r.useState(false)

    return <ddn.CmpContainer 
        // optional, will be added to other styles
        className={""}
        // here you can insert styles in modular form
        clmap={clmap}

        // optional, will use local state if not provided
        open={open}
        open_set={open_set}

        // on which events should close the dropdown {CloseEvt_ConfigFull}
        // optional, all true by default
        closeevents={{ 
            // focus going outside the container
            blur: true,
            // click anywhere outside
            click: true,
            // scroll of any parent
            scroll: true,
            // page resize
            resize: true,
            // press escape
            escape: true,
        }}

        // focus controls {Focus_Config}
        // optional, default values below
        focus={{
            // should capture focus
            capture: true,
            // focus options
            capture_options: { preventScroll: true },
            // should restore focus
            restore: true,
            // focus options
            restore_options: { preventScroll: true },
        }}

        // customize rendered element
        render_view={props => <div {...props} />}
    >
        <ddn.CmpButton 
            className={""}
            disabled={false}
            render_view={props => <button {...props} />}

            // if it should be the element list position is based around
            // if unset - applies automatically depending on the context (true for virtual container, false otherwise)
            isroot={false}
        >
            Press
        </ddn.CmpButton>

        {/* ListPortal renders inside a portal with position: fixed */}
        {/* There also ListAbs and ListFix. They share most of the properties. */}
        <ddn.CmpListPortal
            className={""}
            clmap={clmap}
            clmap_content={clmap}
            render_view={props => <div {...props} />}
            // how to measure content
            // offset (default) will use .offsetHeight and .offsetWidth
            // precise will use .getBoundingClientRect()
            // offset properties are integers, so they cut the floating point
            // .getBoundingClientRect() uses floats, but also measures after transforms
            measurement_kind={"offset" || "precise"}

            // specify the portal
            portal={"domid"}
            portal={document.getElementById("domid")}

            // disable types of transitions, default is false
            transition_nopos={false}
            transition_nosize={false}

            // do not render children when hidden, default is true
            lazy={true}

            // add gap between button and list, in pixels
            gap={5}

            // inverse direction default if "ver"
            direction
            // set direction directly
            direction={"hor" || "ver"}

            // inverse align default is "start"
            align
            // set align directly
            align={"start" || "end" || "center"}

            // inverse justify default is "end"
            justify
            // set justify directly
            justify={"end" || "start"}

            // stretch to min default is "none"
            stretch
            // set stretch directly
            // "min" stretches it to the size of the root if list is smaller
            // "strict" sets the size of the root to the list
            stretch={"none" | "min" | "strict"}

            // recalculate position on, optional
            // default values shown below
            rearrange={{
                // {any[] | undefined}
                deps: undefined,
                // rearrange when list is opened
                open: true,
                // rearrange on scroll
                scroll: true,
                // rearrange on window resize
                resize: true,
                // rearrange every animation frame
                // if you need to use it - you are probably doing something wrong
                polling: false,
            }}

            // when visibility changes
            event_visibility_chage={(visible: boolean) => {}}
            // when transition status changes
            event_transition_change={(property: string, active: true) => {}}
        >
            {/* Used to measure content. Required for any list */}
            <ddn.Content
                clmap={clmap}
                className={""}
                render_view={props => <div {...props} />}

                // dont render focusguards, false by default
                focus_noguards={false}
            >
                Your Content Here
            </ddn.Content>

            {() => {
                return <ddn.Content>
                    Your Content Here
                </ddn.Content>
            }}
        </ddn.CmpListPortal>
    </ddn.CmpContainer>
}