UNPKG

preline

Version:

Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.

367 lines (268 loc) 15.7 kB
# Dropdown A dropdown menu displays a list of actions and more with JavaScript dropdown plugin. [![npm](https://img.shields.io/badge/npm-v4.2.0-blue)](https://www.npmjs.com/package/@preline/dropdown) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://preline.co/plugins/dropdown.html) ## Contents - [Overview](#overview) - [Installation](#installation) - [Basic usage](#basic-usage) - [Accessibility notes](#accessibility-notes) - [Configuration Options](#configuration-options) - [JavaScript API](#javascript-api) - [Events](#events) - [Common Patterns](#common-patterns) - [License](#license) ## Overview The Dropdown component provides a menu that appears when triggered, displaying a list of actions or options. It supports multiple placement options, customizable triggers, keyboard navigation, and integrates with Floating UI for advanced positioning. **Key Features:** - Multiple placement options (top, bottom, left, right, auto) - Customizable trigger events (click, hover, contextmenu) - Floating UI integration for smart positioning - Keyboard navigation support (Arrow keys, Home, End, Esc, A-Z) - Programmatic control via JavaScript API - Event system for lifecycle hooks - Accessibility attributes (ARIA) built-in ## Installation To get started, install Dropdown plugin via npm, else you can skip this step if you are already using Preline UI as a package. ```bash npm i @preline/dropdown ``` ### CSS Use [`@source`](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file. ```css @import "tailwindcss"; /* @preline/dropdown */ @source "../node_modules/@preline/dropdown/*.js"; @import "./node_modules/@preline/dropdown/variants.css"; @import "./node_modules/@preline/dropdown/theme.css"; ``` ### JavaScript Include the JavaScript that powers the interactive elements near the end of your `</body>` tag: ```html <script src="./node_modules/@preline/dropdown/index.js"></script> ``` ### Manual Initialization Use the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly. ```html <script type="module"> import HSDropdown from "@preline/dropdown/non-auto.mjs"; new HSDropdown(document.querySelector("#dropdown")); </script> ``` ### Via Bundler When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module. `@preline/dropdown` is the auto-init entry: it scans the DOM and initializes matching elements automatically. ```js import "@preline/dropdown"; ``` `@preline/dropdown/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself. ```js import HSDropdown from "@preline/dropdown/non-auto"; HSDropdown.autoInit(); // Or initialize a specific element manually const el = document.querySelector("#dropdown"); if (el) new HSDropdown(el); ``` ### TypeScript This package ships with TypeScript type definitions. No additional `@types/` package is needed. ## Basic usage The following example demonstrates the minimal HTML structure required for a dropdown component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The dropdown menu appears when clicking the button. ```html <div class="hs-dropdown relative inline-flex"> <button id="hs-dropdown-unstyled" type="button" class="hs-dropdown-toggle inline-flex justify-center items-center gap-x-2" aria-expanded="false" aria-label="Menu"> Actions </button> <div class="hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 w-56 hidden z-10 mt-2 min-w-60 bg-white" role="menu" aria-labelledby="hs-dropdown-unstyled"> <a class="block" href="#">Newsletter</a> <a class="block" href="#">Purchases</a> <a class="block" href="#">Downloads</a> <a class="block" href="#">Team Account</a> </div> </div> ``` **Structure Requirements:** - `hs-dropdown`: Required container wrapper - `hs-dropdown-toggle`: Required button element that triggers the dropdown - `hs-dropdown-menu`: Required container for dropdown menu items - Proper ARIA attributes (`aria-expanded`, `aria-label`, `role="menu"`, `aria-labelledby`) **Initial State:** - Set `aria-expanded="false"` on the toggle button initially - Add `hidden` class to the dropdown menu initially - Menu visibility is controlled by `hs-dropdown-open:*` modifier classes ## Accessibility notes ### Keyboard interactions | Command | Description | | --- | --- | | Enter | Activates the selected menu item. | | ArrowUp / ArrowDown | Focuses previous/next non-disabled item. | | Home / End | Focuses first/last non-disabled item. | | Esc | Closes any open dropdown menus. | | A-Z / a-z | Focuses first item that matches keyboard input. | ## Configuration Options ### Data Attributes Data attributes are added directly to HTML elements to configure dropdown behavior. | Attribute | Target Element | Type | Default | Description | | --- | --- | --- | --- | --- | | `data-hs-dropdown-transition` | `hs-dropdown` (container) | - | - | Data attribute to designate the container to be animated. When present, enables transition animations for the dropdown menu. | ### CSS Classes (Modifiers) CSS class modifiers use Tailwind-style syntax with `--` prefix to control dropdown behavior. | Class Modifier | Target Element | Values | Default | Description | | --- | --- | --- | --- | --- | | `[--placement:*]` | `hs-dropdown` (container) | `"auto"` \| `"auto-start"` \| `"auto-end"` \| `"top"` \| `"top-left"` \| `"top-right"` \| `"bottom"` \| `"bottom-left"` \| `"bottom-right"` \| `"right"` \| `"right-start"` \| `"right-end"` \| `"left"` \| `"left-start"` \| `"left-end"` | `"bottom"` | Specifies the position of the menu when opened relative to the toggle button. | | `[--scope:*]` | `hs-dropdown` (container) | `"window"` \| `"parent"` | `"parent"` | Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the `Floating UI` plugin. | | `[--auto-close:*]` | `hs-dropdown` (container) | `"inside"` \| `"outside"` \| `"false"` \| `"true"` | `"true"` | Specifies the zone, when clicked, which will close the menu. `inside` closes only when clicking inside, `outside` closes only when clicking outside, `true` closes on any click, `false` prevents auto-close. | | `[--has-autofocus:*]` | `hs-dropdown` (container) | `"true"` \| `"false"` | `"true"` | Disables autofocus on the first focusable element when opening a dropdown. Set to `"false"` to disable autofocus. | | `[--strategy:*]` | `hs-dropdown` (container) | `"fixed"` \| `"absolute"` | `"fixed"` | Sets the position strategy. `fixed` positions relative to viewport, `absolute` positions relative to the nearest positioned ancestor. | | `[--adaptive:*]` | `hs-dropdown` (container) | `"none"` \| `"adaptive"` | `"adaptive"` | Used to disable horizontal position calculations (useful for dropdown menus that span the full width of the screen) while still calculating the vertical position. | | `[--gpu-acceleration:*]` | `hs-dropdown` (container) | `"true"` \| `"false"` | `"false"` | Disable/enable position calculation using the transform property. Set to `"true"` to enable GPU acceleration. | | `[--offset:*]` | `hs-dropdown` (container) | number | `10` | Sets the dropdown's bottom or top offset in pixels. | | `[--flip:*]` | `hs-dropdown` (container) | `"true"` \| `"false"` | `"true"` | Flips the menu's placement when it starts to overlap its reference element. Set to `"false"` to disable flipping. | | `[--trigger:*]` | `hs-dropdown` (container) | `"hover"` \| `"click"` \| `"contextmenu"` | `"click"` | Event to trigger a dropdown. `hover` shows on mouse enter, `click` shows on click, `contextmenu` shows on right-click. | **Example:** ```html <div class="hs-dropdown --trigger:hover --placement:top --auto-close:false"> <button class="hs-dropdown-toggle">Hover me</button> <div class="hs-dropdown-menu">Menu content</div> </div> ``` ### Required CSS Classes These classes define the structure and must be present for the dropdown to function. | Class | Required On | Purpose | | --- | --- | --- | | `hs-dropdown` | Container wrapper | Groups the toggle and menu elements together | | `hs-dropdown-toggle` | Button element | The clickable element that activates the dropdown | | `hs-dropdown-menu` | Menu container | The dropdown menu that appears on trigger | ### Optional CSS Classes | Class | Required On | Purpose | | --- | --- | --- | | `hs-dropdown-toggle-wrapper` | Wrapper element | A wrapper for a Dropdown toggle, useful when some other element is placed in the Dropdown toggle. For example, if you want to place a "+" button inside an existing Dropdown toggle button that opens a modal. | | `hs-dropdown-close` | Close element | Dropdown close element (can be multiple). Elements with this class will close the dropdown when clicked. | ### Tailwind Modifiers | Name | Description | | --- | --- | | `hs-dropdown-open:*` | The modifier that allows you to set Tailwind classes when the dropdown menu is open. | ## JavaScript API The `HSDropdown` object is available in the global `window` object after the plugin is loaded. ### Instance Methods These methods are called on a dropdown instance. | Method | Parameters | Return Type | Description | | --- | --- | --- | --- | | `open(target, openedViaKeyboard)` | `target`: `VirtualElement \| HTMLElement` (optional)<br>`openedViaKeyboard`: `boolean` (optional) | `boolean` | Forces the dropdown menu to open programmatically. Returns `true` if opened successfully, `false` otherwise. | | `close(isAnimated)` | `isAnimated`: `boolean` (optional) | `boolean` | Forces the dropdown menu to close programmatically. `isAnimated` indicates whether the dropdown menu is closed with animation. Returns `true` if closed successfully, `false` otherwise. | | `forceClearState()` | None | `void` | Destroys dropdown state without removing the instance. Use to reset dropdown to initial state. | | `destroy()` | None | `void` | Destroys the dropdown instance, removes all generated markup, classes, and event listeners. Use when removing dropdown from DOM. | ### Static Methods These methods are called directly on the `HSDropdown` class. | Method | Parameters | Return Type | Description | | --- | --- | --- | --- | | `HSDropdown.getInstance(target, isInstance)` | `target`: `HTMLElement \| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `HSDropdown \| { id: string \| number, element: HSDropdown } \| null` | Returns the dropdown instance associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSDropdown` instance. If `isInstance` is `false` or omitted, returns the `HSDropdown` instance directly. Returns `null` if dropdown instance is not found. | | `HSDropdown.open(target, openedViaKeyboard)` | `target`: `HSDropdown \| HTMLElement \| string` (CSS selector)<br>`openedViaKeyboard`: `boolean` (optional) | `void` | Opens the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance `open()` method. | | `HSDropdown.close(target)` | `target`: `HSDropdown \| HTMLElement \| string` (CSS selector) | `void` | Closes the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance `close()` method. | ### Usage Examples **Example 1: Using instance methods (public API)** ```javascript // Create a new dropdown instance const dropdown = new HSDropdown(document.querySelector('#hs-dropdown')); const openBtn = document.querySelector('#hs-open-btn'); // Open dropdown programmatically openBtn.addEventListener('click', () => { dropdown.open(); }); ``` **Example 2: Using static methods** ```javascript const openBtn = document.querySelector('#hs-open-btn'); // Open dropdown using static method (no instance needed) openBtn.addEventListener('click', () => { HSDropdown.open('#hs-dropdown'); }); ``` **Example 3: Getting instance and using methods (recommended pattern)** ```javascript // Get the dropdown instance const instance = HSDropdown.getInstance('#hs-dropdown', true); if (instance) { const { element } = instance; // element is the HSDropdown instance const openBtn = document.querySelector('#hs-open-btn'); // Use instance methods openBtn.addEventListener('click', () => { element.open(); }); // Clean up when removing from DOM function removeDropdown() { element.destroy(); } } ``` **Example 4: Destroying dropdown instance** ```javascript const instance = HSDropdown.getInstance('#hs-dropdown', true); if (instance) { const { element } = instance; const removeBtn = document.querySelector('#hs-remove-btn'); removeBtn.addEventListener('click', () => { // Clean up before removing from DOM element.destroy(); // Now safe to remove the element document.querySelector('#hs-dropdown').remove(); }); } ``` ## Events Dropdown instances emit events that can be listened to for lifecycle hooks and custom behavior. | Event Name | When Fired | Callback Parameter | Description | | --- | --- | --- | --- | | `on:open` | After dropdown menu is opened | `HSDropdown` (dropdown instance) | Fires after the dropdown menu has been displayed. Use for post-open actions like loading content or tracking analytics. | | `on:close` | After dropdown menu is closed | `HSDropdown` (dropdown instance) | Fires after the dropdown menu has been hidden. Use for cleanup or state updates. | ### Event Usage Example ```javascript // Get dropdown instance const instance = HSDropdown.getInstance('#hs-dropdown', true); if (instance) { const { element } = instance; // Listen to open event element.on('open', (dropdownInstance) => { console.log('Dropdown opened:', dropdownInstance); // Perform actions after dropdown opens // e.g., load content, update UI, track analytics }); // Listen to close event element.on('close', (dropdownInstance) => { console.log('Dropdown closed:', dropdownInstance); // Perform cleanup or state updates }); } ``` ## Common Patterns ### Pattern 1: Hover Trigger Show dropdown on hover instead of click. ```html <div class="hs-dropdown --trigger:hover"> <button class="hs-dropdown-toggle">Hover me</button> <div class="hs-dropdown-menu">Menu content</div> </div> ``` ### Pattern 2: Programmatic Control Control dropdown from external buttons. ```html <div class="hs-dropdown" id="hs-dropdown-first"> <button class="hs-dropdown-toggle">Actions</button> <div class="hs-dropdown-menu">Menu content</div> </div> <button id="hs-open-dropdown">Open Dropdown</button> <button id="hs-close-dropdown">Close Dropdown</button> <script> const instance = HSDropdown.getInstance('#hs-dropdown-first', true); if (instance) { const { element } = instance; document.querySelector('#hs-open-dropdown').addEventListener('click', () => { element.open(); }); document.querySelector('#hs-close-dropdown').addEventListener('click', () => { element.close(); }); } </script> ``` ## License Copyright (c) 2026 Preline Labs. Licensed under the [MIT License](https://opensource.org/licenses/MIT).