Skip to content

Dropdowns ​

Dropdown menus are Bootstrap 6 menus, opened by Bootstrap's own data-bs-toggle="menu" data API, so the Bootstrap JavaScript module must be on the page. The dropdown control is CSS only: it gives the menus the library look. Ui::dropdownButton() renders the toggle and the menu together, or you write the menu markup yourself.

Ui::dropdownButton() is slot-based: open it with ->slot(), print the items, and close it with Ui::end(), which renders the button around everything printed in between. Because the items are captured rather than returned, the opening and closing calls go in <?php ?> tags, while the items print with {{ }} as usual.

Ui::dropdownItem() runs a handler when it has a handler, or follows a link when it has an href, and Ui::dropdownDivider() draws a line between groups of items. Items take any attribute, so here dataRequestData tells the handler which item was clicked.

blade
<?php Ui::dropdownButton(label: 'Actions', primary: true)->slot() ?>
    {{ Ui::dropdownItem(
        label: 'Duplicate',
        handler: 'onDropdownAction',
        icon: 'ph ph-copy',
        dataRequestData: "action: 'duplicate'",
        dataRequestFlash: true
    ) }}
    {{ Ui::dropdownItem(
        label: 'Archive',
        handler: 'onDropdownAction',
        icon: 'ph ph-archive',
        dataRequestData: "action: 'archive'",
        dataRequestFlash: true
    ) }}
    {{ Ui::dropdownDivider() }}
    {{ Ui::dropdownItem(label: 'Open the reference', href: route('reference.index'), icon: 'ph ph-book-open') }}
<?php Ui::end() ?>
php
<?php

namespace App\Controllers\Reference\UiController;

use Larajax\Classes\AjaxResponse;

/**
 * DropdownHandlers holds the AJAX handlers behind the Dropdowns page.
 */
trait DropdownHandlers
{
    /**
     * onDropdownAction runs the dropdown item named in the request data.
     */
    public function onDropdownAction(): AjaxResponse
    {
        $actions = [
            'duplicate' => 'Duplicated the project.',
            'archive' => 'Archived the project.',
            'rename' => 'Renamed the project.',
            'delete' => 'Deleted the project.',
        ];

        return ajax()->flash('success', $actions[input('action')] ?? 'Nothing to do.');
    }
}

The output is a btn-group holding the toggle and a menu control-dropdown panel. The toggle sets data-bs-strategy="fixed", so the menu opens against the viewport and is not clipped by a scrolling or overflow: hidden container around it.

The icon argument shows an icon before the label, and caret: false hides the arrow after it. A button with an icon and no caret suits a "more actions" menu. The caret is the icon pack's dropdown.caret key.

blade
<?php Ui::dropdownButton(label: 'More', icon: 'ph ph-dots-three-outline', secondary: true, caret: false)->slot() ?>
    {{ Ui::dropdownItem(label: 'Popups', href: route('reference.ui.popups')) }}
    {{ Ui::dropdownItem(label: 'Larajax docs', href: 'https://larajax.org', target: '_blank') }}
<?php Ui::end() ?>

Items take any attribute, as buttons do, so they can ask for confirmation with dataRequestConfirm or send values with dataRequestData. The class argument merges into the item's menu-item class:

  • disabled greys an item out. Add ariaDisabled: 'true' so screen readers know it too.
  • A theme class, such as theme-danger, colors an item, hover included, for destructive actions.
blade
<?php Ui::dropdownButton(label: 'Project', secondary: true)->slot() ?>
    {{ Ui::dropdownItem(
        label: 'Rename',
        handler: 'onDropdownAction',
        icon: 'ph ph-pencil-simple',
        dataRequestData: "action: 'rename'",
        dataRequestFlash: true
    ) }}
    {{ Ui::dropdownItem(label: 'Export', icon: 'ph ph-download-simple', class: 'disabled', ariaDisabled: 'true') }}
    {{ Ui::dropdownDivider() }}
    {{ Ui::dropdownItem(
        label: 'Delete',
        handler: 'onDropdownAction',
        icon: 'ph ph-trash',
        class: 'theme-danger',
        dataRequestData: "action: 'delete'",
        dataRequestConfirm: 'Delete the project?',
        dataRequestFlash: true
    ) }}
<?php Ui::end() ?>

Any toggle with data-bs-toggle="menu" opens the menu element placed right after it, so no data-bs-target or wrapper is needed. Add control-dropdown to the menu for the library look, and give the toggle aria-expanded="false", which Bootstrap updates as the menu opens and closes.

Keep the items, <a class="menu-item"> elements, as direct children of the menu: Bootstrap's arrow key navigation only looks there. An active class marks the current choice, and <hr class="menu-divider"> separates groups.

blade
<button type="button" class="btn-solid theme-secondary" data-bs-toggle="menu" aria-expanded="false">
    Sort by
</button>
<div class="menu control-dropdown" role="menu">
    <a class="menu-item active" role="menuitem" href="javascript:;">
        Newest first
    </a>
    <a class="menu-item" role="menuitem" href="javascript:;">
        Oldest first
    </a>
    <hr class="menu-divider">
    <a class="menu-item" role="menuitem" href="javascript:;">
        Title
    </a>
</div>

data-bs-placement on the toggle picks the side and alignment of the menu. The menu opens below and aligned to the toggle's left edge by default. top-start opens it above, and bottom-end lines it up with the toggle's right edge, which suits a toggle at the end of a row.

blade
<button type="button" class="btn-solid theme-secondary" data-bs-toggle="menu" data-bs-placement="top-start" aria-expanded="false">
    Opens above
</button>
<div class="menu control-dropdown" role="menu">
    <a class="menu-item" role="menuitem" href="javascript:;">
        First
    </a>
    <a class="menu-item" role="menuitem" href="javascript:;">
        Second
    </a>
</div>

<button
    type="button"
    class="btn-icon-link ph ph-dots-three-vertical"
    title="Row options"
    data-bs-toggle="menu"
    data-bs-placement="bottom-end"
    aria-expanded="false"></button>
<div class="menu control-dropdown" role="menu">
    <a class="menu-item" role="menuitem" href="javascript:;">
        Aligned to the right edge
    </a>
    <a class="menu-item" role="menuitem" href="javascript:;">
        Of the toggle
    </a>
</div>

Toggle attributes ​

These are Bootstrap's menu attributes, set on the toggle.

AttributeDescription
data-bs-toggle="menu"opens the menu element that follows the toggle.
data-bs-placementthe side and alignment, such as bottom-start, bottom-end, top-start or top-end. Default: bottom-start.
data-bs-strategy="fixed"positions the menu against the viewport, for a toggle inside a scrolling or overflow: hidden container. Ui::dropdownButton() sets it.
data-bs-container="body"moves the menu to <body> while it is open. Avoid it inside a popup: the moved menu renders beneath the dialog, while a menu left in place works.

Bootstrap dispatches show.bs.menu, shown.bs.menu, hide.bs.menu and hidden.bs.menu on the toggle. shown and hidden fire after the menu's transition, so change a menu's items in a show.bs.menu listener, before it opens.

Arguments ​

ArgumentDescription
labelthe toggle text, escaped.
iconicon classes, rendered before the label.
primaryprimary styling, btn-solid theme-primary. Default: false.
secondarysecondary styling, btn-solid theme-secondary. Default: false.
caretshows the caret after the label. Default: true.

Any other argument becomes an attribute on the toggle button, as on buttons.

ArgumentDescription
labelthe item text, escaped.
handleran AJAX handler to run when the item is clicked.
hrefan address to follow. Default: javascript:;.
iconicon classes, rendered before the label.

Any other argument becomes an attribute on the item, with class merged into menu-item.

Takes no arguments, and renders <hr class="menu-divider">.