Element Framework

Overlay Menu

  • HTML
  • JS
  • CSS
<section>
  <ef-button cta id="button">Choose Item</ef-button>
</section>
<ef-overlay-menu id="menu" opened>
  <ef-item type="header">Regions</ef-item>
  <ef-item for="emea">EMEA</ef-item>
  <ef-item for="n-america">N. America</ef-item>
  <ef-item for="apac">APAC</ef-item>
  <ef-item for="latin-america">Latin America</ef-item>
  <ef-item type="header">Favorites</ef-item>
  <ef-item icon="flame">Thailand</ef-item>
  <ef-item type="divider"></ef-item>
  <ef-item icon="dislike-empty" disabled>Unspecified</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="emea">
  <ef-item>Spain</ef-item>
  <ef-item>France</ef-item>
  <ef-item for="united-kingdom">United Kingdom</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="united-kingdom">
  <ef-item>Cardiff</ef-item>
  <ef-item>Edinburgh</ef-item>
  <ef-item>London</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="n-america">
  <ef-item>Canada</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="apac">
  <ef-item>China</ef-item>
  <ef-item>Australia</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="latin-america">
  <ef-item>Mexico</ef-item>
  <ef-item>Brazil</ef-item>
</ef-overlay-menu>
var button = document.getElementById('button');
var menu = document.getElementById('menu');
menu.positionTarget = button;

button.addEventListener('click', function() {
  if (!menu.fullyOpened && !menu.transitioning) {
    menu.opened = true;
  }
});
section {
  height: 235px;
  padding: 0 3px;
}

ef-overlay-menu is an overlay window that supports single-level and multi-level menus. It can be positioned by attaching to other elements, or its vertical and horizontal offset can be adjusted, if needed.

Basic Menu

Create ef-overlay-menu with ef-item elements as menu items. Listen for the item-trigger event to identify a clicked item.

As the overlay menu is designed to support several use cases (multi-selection, toggle, etc.), the menu will not close when an item is clicked. To open or close the menu, simply set the opened property to true or false, respectively.

  • HTML
  • JS
  • CSS
<section>
  <ef-button cta id="button">Choose Item</ef-button>
</section>
<ef-overlay-menu id="menu">
  <ef-item type="header">EMEA</ef-item>
  <ef-item value="Spain">Spain</ef-item>
  <ef-item value="France" disabled>France</ef-item>
  <ef-item value="United Kingdom">United Kingdom</ef-item>
</ef-overlay-menu>
var button = document.getElementById('button');
var menu = document.getElementById('menu');
var menuController = menu.parentElement;
menu.positionTarget = button;

button.addEventListener('click', function() {
  if (!menu.fullyOpened && !menu.transitioning) {
    menu.opened = true;
  }
});

menuController.addEventListener('item-trigger', function(e) {
  var value = e.detail.value;
  console.log('You have clicked on: ' + value);
  button.innerHTML = value;
  menu.opened = false;
});
section {
  height: 135px;
  padding: 0 3px;
}
<ef-button cta id="button">Choose Item</ef-button>
<ef-overlay-menu id="menu">
  <ef-item type="header">EMEA</ef-item>
  <ef-item value="Spain">Spain</ef-item>
  <ef-item value="France" disabled>France</ef-item>
  <ef-item value="United Kingdom">United Kingdom</ef-item>
</ef-overlay-menu>
var button = document.getElementById('button');
var menu = document.getElementById('menu');
var menuController = menu.parentElement;
menu.positionTarget = button;

button.addEventListener('click', function() {
  if (!menu.fullyOpened && !menu.transitioning) {
    menu.opened = true;
  }
});

menuController.addEventListener('item-trigger', function(e) {
  var value = e.detail.value;
  console.log('You have clicked on: ' + value);
  button.innerHTML = value;
  menu.opened = false;
});

See Item API document for more detail about ef-item properties

Nested menus

Menu and sub-menus are bound together using the for and id attributes of ef-item and the sub-menu. The for attribute must be equal to the id attribute of the related sub-menu in order to bind the menu and submenu together.

  • HTML
  • JS
  • CSS
<section>
  <ef-button cta id="button">Nested menus</ef-button>
</section>
<ef-overlay-menu id="menu">
  <ef-item type="header">Regions</ef-item>
  <ef-item for="emea">EMEA</ef-item>
  <ef-item for="apac">APAC</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="emea">
  <ef-item>Spain</ef-item>
  <ef-item disabled>France</ef-item>
  <ef-item for="united-kingdom">United Kingdom</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="united-kingdom">
  <ef-item>Cardiff</ef-item>
  <ef-item>Edinburgh</ef-item>
  <ef-item for="london">London</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="london">
  <ef-item>London Bridge</ef-item>
  <ef-item>Westminster Bridge</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="apac">
  <ef-item>China</ef-item>
  <ef-item>Australia</ef-item>
</ef-overlay-menu>
var button = document.getElementById('button');
var menu = document.getElementById('menu');
menu.positionTarget = button;

button.addEventListener('click', function() {
  if (!menu.fullyOpened && !menu.transitioning) {
    menu.opened = true;
  }
});
section {
  height: 225px;
  padding: 0 3px;
}
<ef-overlay-menu id="menu">
  <ef-item type="header">Regions</ef-item>
  <ef-item for="emea">EMEA</ef-item>
  <ef-item for="apac">APAC</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="emea">
  <ef-item>Spain</ef-item>
  <ef-item disabled>France</ef-item>
  <ef-item for="united-kingdom">United Kingdom</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="united-kingdom">
  <ef-item>Cardiff</ef-item>
  <ef-item>Edinburgh</ef-item>
  <ef-item for="london">London</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="london">
  <ef-item>London Bridge</ef-item>
  <ef-item>Westminster Bridge</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="apac">
  <ef-item>China</ef-item>
  <ef-item>Australia</ef-item>
</ef-overlay-menu>

Compact menu

If there is not enough space to fit sub-menus, add the compact attribute. In this mode, sub-menus will be opened on top of the parent menu.

  • HTML
  • JS
  • CSS
<section>
  <ef-button cta id="button">Compact menu</ef-button>
</section>
<ef-overlay-menu id="menu" compact>
  <ef-item type="header">Regions</ef-item>
  <ef-item for="emea">EMEA</ef-item>
  <ef-item for="apac">APAC</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="emea" compact>
  <ef-item>Spain</ef-item>
  <ef-item disabled>France</ef-item>
  <ef-item for="united-kingdom">United Kingdom</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="united-kingdom" compact>
  <ef-item>Cardiff</ef-item>
  <ef-item>Edinburgh</ef-item>
  <ef-item for="london">London</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="london" compact>
  <ef-item>London Bridge</ef-item>
  <ef-item>Westminster Bridge</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="apac" compact>
  <ef-item>China</ef-item>
  <ef-item>Australia</ef-item>
</ef-overlay-menu>
var button = document.getElementById('button');
var menu = document.getElementById('menu');
menu.positionTarget = button;

button.addEventListener('click', function() {
  if (!menu.fullyOpened && !menu.transitioning) {
    menu.opened = true;
  }
});
section {
  height: 135px;
  padding: 0 3px;
}
<ef-overlay-menu compact>
  ...
</ef-overlay-menu>

Managing position and user interaction

ef-overlay-menu inherits properties from ef-overlay and thus supports the same positioning strategies.

The developer may specify with-backdrop together with no-cancel-on-outside-click so users must interact with the menu before they can return to the application.

  • HTML
  • JS
  • CSS
<section>
  <ef-button cta id="button">Choose Item</ef-button>
</section>
<ef-overlay-menu id="menu" with-backdrop no-cancel-on-outside-click>
  <ef-item type="header">EMEA</ef-item>
  <ef-item value="Spain">Spain</ef-item>
  <ef-item value="France" disabled>France</ef-item>
  <ef-item value="United Kingdom">United Kingdom</ef-item>
</ef-overlay-menu>
var button = document.getElementById('button');
var menu = document.getElementById('menu');
var menuController = menu.parentElement;

button.addEventListener('click', function() {
  if (!menu.fullyOpened && !menu.transitioning) {
    // position at the top right corner of the button
    menu.positionTarget = button;
    menu.position = ['right-start'];
    menu.opened = true;
  }
});

menuController.addEventListener('item-trigger', function(e) {
  menu.opened = false;
});
section {
  height: 115px;
  padding: 0 3px;
}
<ef-button cta id="button">Choose Item</ef-button>
<ef-overlay-menu id="menu" with-backdrop no-cancel-on-outside-click>...</ef-overlay-menu>
var button = document.getElementById('button');
var menu = document.getElementById('menu');

button.addEventListener('click', function() {
  if (!menu.fullyOpened && !menu.transitioning) {
    // position at the top right corner of the button
    menu.positionTarget = button;
    menu.position = ['right-start'];
    menu.opened = true;
  }
});

Loading from data

ef-overlay-menu can be populated using the data property. data fields have the same names as properties in ef-item. Use the items collection to create sub-menus.

Alternatively, you can set data using a CollectionComposer, which is useful when you need a rich API to manage data externally.

  • HTML
  • JS
  • CSS
<section>
  <ef-button cta id="button">Choose Item</ef-button>
</section>
<ef-overlay-menu id="menu"></ef-overlay-menu>
var button = document.getElementById('button');
var menu = document.getElementById('menu');

menu.data = [{
  type: 'header',
  label: 'Regions'
}, {
  icon: 'flame',
  label: 'EMEA',
  items: [{
    label: 'Spain'
  }, {
    label: 'France',
    disabled: true
  }, {
    label: 'Italy'
  }, {
    label: 'United Kingdom'
  }]
}, {
  type: 'divider'
}, {
  label: 'APAC'
}];

menu.positionTarget = button;

button.addEventListener('click', function() {
  if (!menu.fullyOpened && !menu.transitioning) {
    menu.opened = true;
  }
});
section {
  height: 165px;
  padding: 0 3px;
}
<ef-overlay-menu id="menu"></ef-overlay-menu>
var menu = document.getElementById('menu');

menu.data = [{
  type: 'header',
  label: 'Regions'
}, {
  icon: 'flame',
  label: 'EMEA',
  items: [{
    label: 'Spain'
  }, {
    label: 'France',
    disabled: true
  }, {
    label: 'Italy'
  }, {
    label: 'United Kingdom'
  }]
}, {
  type: 'divider'
}, {
  label: 'APAC'
}];

Data property interface

The data property of the ef-overlay-menu use the OverlayMenuData interface for its data items which is described below.

Name Type Description
label string Item's label
value string Value of an item
type string Type of item. Value can be text, header, divider
icon string Set the icon name from the coral-icon list
items array Child items collection
readonly boolean Sets the item to be readonly
disabled boolean Sets the item to be disabled

Managing selection

ef-overlay-menu does not manage the selected state. Instead, the developer decides the selection model by changing the selected attribute on menu items. Furthermore, the developer may use the values getter and setter to manipulate selected items across a menu and all of its sub-menus.

  • HTML
  • JS
  • CSS
<section>
  <ef-button cta id="button">Choose Item</ef-button>
</section>
<ef-overlay-menu id="menu">
  <ef-item type="header">Regions</ef-item>
  <ef-item value="emea" for="emea" selected>EMEA</ef-item>
  <ef-item value="apac" for="apac">APAC</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="emea">
  <ef-item value="spain">Spain</ef-item>
  <ef-item value="france" disabled>France</ef-item>
  <ef-item value="united-kingdom" for="united-kingdom" selected>United Kingdom</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="united-kingdom">
  <ef-item value="cardiff">Cardiff</ef-item>
  <ef-item value="edinburgh">Edinburgh</ef-item>
  <ef-item value="london" selected>London</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="apac">
  <ef-item value="china">China</ef-item>
  <ef-item value="australia">Australia</ef-item>
</ef-overlay-menu>
var button = document.getElementById('button');
var menu = document.getElementById('menu');
var menuController = menu.parentElement;
menu.positionTarget = button;

var getItemDescendants = function(item) {
  var descendants = [];
  while (item) {
    descendants.unshift(item);
    item = item.parentElement && item.parentElement.id ?
      menuController.querySelector('ef-item[for=' + item.parentElement.id + ']') :
      null;
  }

  return descendants;
};

menuController.addEventListener('item-trigger', function(e) {
  var selectedPath = getItemDescendants(e.target);
  menu.values = selectedPath.map(function(item) {
    return item.value;
  });
});

button.addEventListener('click', function() {
  if (!menu.fullyOpened && !menu.transitioning) {
    menu.opened = true;
  }
});
section {
  height: 200px;
  padding: 0 3px;
}
var menuController = menu.parentElement;

var getItemDescendants = function (item) {
  var descendants = [];
  while (item) {
    descendants.unshift(item);
    item = item.parentElement && item.parentElement.id
      ? menuController.querySelector('ef-item[for=' + item.parentElement.id + ']')
      : null;
    }

  return descendants;
};

menuController.addEventListener('item-trigger', function(e) {
  var selectedPath = getItemDescendants(e.target);
  menu.values = selectedPath.map(function (item) {
    return item.value;
  });
});

Overlay Transitions

ef-overlay-menu supports a number of built-in transitions. To set the transition, use the transition-style attribute.

  • HTML
  • JS
  • CSS
<section>
  <ef-button cta id="button">With Transitions</ef-button>
</section>
<ef-overlay-menu id="menu" transition-style="slide">
  <ef-item type="header">Regions</ef-item>
  <ef-item for="emea">EMEA</ef-item>
  <ef-item for="apac">APAC</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="emea" transition-style="slide">
  <ef-item>Spain</ef-item>
  <ef-item disabled>France</ef-item>
  <ef-item for="united-kingdom">United Kingdom</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="united-kingdom" transition-style="slide">
  <ef-item>Cardiff</ef-item>
  <ef-item>Edinburgh</ef-item>
  <ef-item>London</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="apac" transition-style="slide">
  <ef-item>China</ef-item>
  <ef-item>Australia</ef-item>
</ef-overlay-menu>
var button = document.getElementById('button');
var menu = document.getElementById('menu');
menu.positionTarget = button;

button.addEventListener('click', function() {
  if (!menu.fullyOpened && !menu.transitioning) {
    menu.opened = true;
  }
});
section {
  height: 200px;
  padding: 0 3px;
}
<ef-overlay-menu id="menu" transition-style="slide">
  <ef-item type="header">Regions</ef-item>
  <ef-item for="emea">EMEA</ef-item>
  <ef-item for="apac">APAC</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="emea" transition-style="slide">
  <ef-item>Spain</ef-item>
  <ef-item disabled>France</ef-item>
  <ef-item for="united-kingdom">United Kingdom</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="united-kingdom" transition-style="slide">
  <ef-item>Cardiff</ef-item>
  <ef-item>Edinburgh</ef-item>
  <ef-item>London</ef-item>
</ef-overlay-menu>
<ef-overlay-menu id="apac" transition-style="slide">
  <ef-item>China</ef-item>
  <ef-item>Australia</ef-item>
</ef-overlay-menu>

Position against target

Position may contain a single word or a comma separated list to set the priority. Position is not applied if attachTarget is not HTML Element. For instance:

  • [bottom-middle, top-middle] default position is bottom-middle, if cannot fit position top-middle.
  • [left, right] the align is not set, set best position on the left or right.
menu.position = ['bottom-end', 'bottom-start', 'right-end', 'center-middle'];

The first part defines position. The optional second part defines align. For instance: bottom, top-start, right-middle.

Position Description
top Above target (same as top-start)
right After target (same as right-middle)
bottom Below target (same as bottom-start)
left Before target (same as left-middle)
center At the center of target (same as center-middle)
Align Description
start Target is aligned at the start of popup
middle Target is aligned at the middle of popup
end Target is aligned at the end of popup

API Reference

Attributes

boolean
compact
Switch to compact style menu
boolean
opened
True if the menu is currently displayed
string
value
Returns the first selected item value.
number
x
Set a specific x coordinate
number
y
Set a specific y coordinate
number
offset
A pixel value that will be added to the position calculated on the vertical or horizontal axis. The offset is applied dynamically depending on the `positionTarget`
{} | undefined
position
Set position and align against the attach target.
boolean
with-backdrop
True to show backdrop
boolean
no-cancel-on-esc-key
Set to true to disable canceling the overlay with the ESC key
boolean
no-cancel-on-outside-click
Set to true to disable canceling the overlay by clicking outside it
boolean
lock-position-target
Set to true to lock position target
string
transition-style
Set the transition style
number
horizontal-offset
A pixel value that will be added to the position calculated on the horizontal axis. The offset will be applied either to the `left` or `right` depending on the `positionTarget`
number
vertical-offset
A pixel value that will be added to the position calculated on the vertical axis. The offset will be applied either to the `top` or `bottom` depending on the `positionTarget`

Properties

boolean
compact
Switch to compact style menu
false
string[]
values
Array of item's values
data
Construct the menu from data object. Cannot be used with internal content
boolean
opened
True if the menu is currently displayed
false
boolean
withBackdrop
True to show backdrop
false
boolean
noCancelOnEscKey
Set to true to disable canceling the overlay with the ESC key
false
boolean
noCancelOnOutsideClick
Set to true to disable canceling the overlay by clicking outside it
false
boolean
lockPositionTarget
Set to true to lock position target
false
HTMLElement|null
positionTarget
Position next to the HTML element
"null"
string|null
transitionStyle
Set the transition style
"null"
string
value
Returns the first selected item value.
""
number
x
Set a specific x coordinate
number
y
Set a specific y coordinate
number
horizontalOffset
A pixel value that will be added to the position calculated on the horizontal axis. The offset will be applied either to the `left` or `right` depending on the `positionTarget`
number
verticalOffset
A pixel value that will be added to the position calculated on the vertical axis. The offset will be applied either to the `top` or `bottom` depending on the `positionTarget`
number
offset
A pixel value that will be added to the position calculated on the vertical or horizontal axis. The offset is applied dynamically depending on the `positionTarget`
{} | undefined
position
Set position and align against the attach target.

Events

item-trigger
Dispatched when user clicks on item
opened-changed
Dispatched when when opened attribute changes internally. Prevent default to stop opening/closing pipeline