|
||
---|---|---|
.. | ||
panel-item.css | ||
panel-list.css | ||
panel-list.js | ||
panel-list.stories.mjs | ||
README.stories.md |
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-item
s 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.