1
0
Fork 0
firefox/toolkit/content/widgets/panel-list
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00
..
panel-item.css Adding upstream version 140.0. 2025-06-25 09:37:52 +02:00
panel-list.css Adding upstream version 140.0. 2025-06-25 09:37:52 +02:00
panel-list.js Adding upstream version 140.0. 2025-06-25 09:37:52 +02:00
panel-list.stories.mjs Adding upstream version 140.0. 2025-06-25 09:37:52 +02:00
README.stories.md Adding upstream version 140.0. 2025-06-25 09:37:52 +02:00

Panel Menu

The panel-list and panel-item components work together to create a menu for in-content contexts. The basic structure is a panel-list with panel-item children and optional hr elements as separators. The panel-list will anchor itself to the target of the initiating event when opened with panelList.toggle(event).

Note: XUL is currently required to support accesskey underlining (although using moz-label could change that). Shortcuts are not displayed automatically in the panel-item. This XUL requirement affects the "With Accesskeys" story, enabling or disabling accesskeys will not show any visual change in the story.

<panel-list stay-open open>
    <panel-item action="new" accesskey="N">New</panel-item>
    <panel-item accesskey="O">Open</panel-item>
    <hr />
    <panel-item action="save" accesskey="S">Save</panel-item>
    <hr />
    <panel-item accesskey="Q">Quit</panel-item>
</panel-list>

Status

Current status is listed as in-development since this is only intended for use within in-content contexts. XUL is still required for accesskey underlining, but could be migrated to use the moz-label component. This is a useful but historical element that could likely use some attention at the API level and to be brought up to our design systems standards.

When to use

  • When there are multiple options for something that would take too much space with individual buttons.
  • When the actions are not frequently needed.
  • When you are within an in-content context.

When not to use

  • When there is only one action.
  • When the actions are frequently needed.
  • In the browser chrome, you probably want to use menupopup or panel instead.

Basic usage

The source for panel-list can be found under toolkit/content/widgets/panel-list/panel-list.js. You can find an examples of panel-list in use in the Firefox codebase in both about:addons and the migration-wizard.

panel-list will automatically be imported in chrome documents, both through markup and through JS with document.createElement("panel-list") or by cloning a template.

<!-- This will import `panel-list` if needed in most cases. -->
<panel-list></panel-list>

In non-chrome documents it can be imported into .html/.xhtml files:

<script src="chrome://global/content/elements/panel-list.js"></script>

And used as follows:

<panel-list>
  <panel-item accesskey="N">New</panel-item>
  <panel-item accesskey="O">Open</panel-item>
  <hr />
  <panel-item accesskey="S">Save</panel-item>
  <hr />
  <panel-item accesskey="Q">Quit</panel-item>
</panel-list>

The toggle method takes the event you received on your anchor button and opens the menu attached to that element.

anchorButton.addEventListener("mousedown", e => panelList.toggle(e));

Accesskeys are activated with the bare accesskey letter when the menu is opened. So for this example after opening the menu pressing s will fire a click event on the Save panel-item.

Note: XUL is currently required for accesskey underlining, but can be replaced with moz-label later.

Fluent usage

The panel-item expects to have text content set by fluent.

<panel-list>
  <panel-item data-l10n-id="menu-new"></panel-item>
  <panel-item data-l10n-id="menu-save"></panel-item>
</panel-list>

In which case your Fluent messages will look something like this:

menu-new = New
    .accesskey = N
menu-save = Save
    .accesskey = S

Advanced usage

Showing the menu

By default the menu will be hidden. It is shown when the open attribute is set, but that won't position the menu by default.

To trigger the auto-positioning of the menu, it should be opened or closed using the toggle(event) method.

function onMenuButton(event) {
  document.querySelector("panel-list").toggle(event);
}

The toggle(event) method will use event.target as the anchor for the menu.

To achieve the expected behaviour, the menu should open on mousedown for mouse events, and click for keyboard events. This can be accomplished by checking the event.inputSource property in chrome contexts or event.detail in non-chrome contexts (event.detail will be the click count which is 0 when a click is from the keyboard).

function openMenu(event) {
  if (
    event.type == "mousedown" ||
    event.inputSource == MouseEvent.MOZ_SOURCE_KEYBOARD ||
    !event.detail
  ) {
    document.querySelector("panel-list").toggle(event);
  }
}

let menuButton = document.getElementById("open-menu-button");
menuButton.addEventListener("mousedown", openMenu);
menuButton.addEventListener("click", openMenu);

Checkbox

Items may be used as a checkbox by setting the type attribute. This allows checkmark icon to be automatically rendered and removed via the item's checked attribute. Note: this item does not have its own click handler for adding or removing the checkmark, you must handle that.

<panel-list stay-open open>
  <panel-item action="toggle-setting" type="checkbox" checked="">Toggle Setting</panel-item>
  <panel-item action="toggle-panel" type="checkbox">Toggle Panel</panel-item>
</panel-list>
<panel-list>
  <panel-item action="toggle-setting" type="checkbox" checked="">Toggle Setting</panel-item>
  <panel-item action="toggle-panel" type="checkbox">Toggle Panel</panel-item>
</panel-list>

Icons

Icons can be added to the panel-items by setting a background-image on panel-item::part(button).

panel-item[action="new"]::part(button) {
  background-image: url("./new.svg");
}

panel-item[action="save"]::part(button) {
  background-image: url("./save.svg");
}

Badging

Icons may be badged by setting the badged attribute. This adds a dot next to the icon.

<panel-list>
  <panel-item action="new">New</panel-item>
  <panel-item action="save" badged>Save</panel-item>
</panel-list>
<panel-list stay-open open>
  <panel-item action="new">New</panel-item>
  <panel-item action="save" badged>Save</panel-item>
</panel-list>

Matching anchor width

When using the panel-list like a select dropdown, it's nice to have it match the size of the anchor button. You can see this in practice in the Wide variant and the migration-wizard. Setting the min-width-from-anchor attribute will cause the menu to match its anchor's width when it is opened.

<button class="current-selection">Apples</button>
<panel-list min-width-from-anchor>
  <panel-item>Apples</panel-list>
  <panel-item>Bananas</panel-list>
</panel-list>

Usage in a XUL panel

The "new" (as of early 2023) migration wizard uses the panel-list inside of a XUL panel element to let its contents escape its container dialog by creating an OS-level window. This can be useful if the menu could be larger than its container, however in chrome contexts you are likely better off using menupopup.

By placing a panel-list inside of a XUL panel it will automatically defer its positioning responsibilities to the XUL panel and it will then be able to grow larger than its containing window if needed.

<!-- Assuming we're in a XUL document. -->
<panel>
  <html:panel-list>
    <html:panel-item>Apples</html:panel-item>
    <html:panel-item>Apples</html:panel-item>
    <html:panel-item>Apples</html:panel-item>
  </html:panel-list>
</panel>

Submenus

panel-list supports nested submenus. Submenus can be created by nesting a second panel-list in a panel-item's submenu slot and specifying a submenu attribute on that panel-item that points to the nested list's ID. For example:

<panel-list>
  <panel-item>No submenu</panel-item>
  <panel-item>No submenu</panel-item>
  <panel-item submenu="example-submenu">
    Has a submenu
    <panel-list slot="submenu" id="example-submenu">
      <panel-item>I'm a submenu item!</panel-item>
      <panel-item>I'm also a submenu item!</panel-item>
    </panel-list>
  </panel-item>
</panel-list>

As of February 2024 submenus are only in use in Firefox View and support for nesting beyond one submenu may be limited.