logo dile-components dile-components

dile-select-ajax-overlay

Same ability as dile-select-ajax to search a configurable JSON API resource, but results appear directly in a floating popup as soon as they load, instead of populating a native <select> that then needs a second click to open. It covers the exact same properties, methods and events as dile-select-ajax, so the two are interchangeable — swap the tag name to change the UI without changing how you wire it up.

Internally it's built by composing dile-select-overlay for the popup/picking UI (embedded with its trigger hidden), the same way dile-select-ajax composes dile-select.

Alternative: dile-ajax-select-crud-overlay

If you need more advanced configuration options for your API requests, such as adding authentication tokens or custom headers, consider using dile-ajax-select-crud-overlay instead. Same relationship as dile-select-ajax → dile-ajax-select-crud, but for the overlay variant.

Installation

npm i @dile/ui

dile-select-ajax-overlay Usage

Import the component.

import '@dile/ui/components/select/select-ajax-overlay.js';

Use the component.

<dile-select-ajax-overlay
  id="select1"
  name="post_id"
  label="Post"
  displayProperty="title"
  endpoint="https://jsonplaceholder.typicode.com/posts"
  delay="500"
></dile-select-ajax-overlay>

Properties

Same set as dile-select-ajax, except selectDefaultPlaceholder: that property doesn't apply here — it existed to label the empty/default option inside the native <select> you had to click to open, but this component never shows an empty placeholder row, so there's nothing for it to label.

Custom option rendering

By default each result shows the displayProperty text. Set optionRenderer to render richer results with any field of the original data object:

productSelect.optionRenderer = (item, index) => html`
  <span part="product-name">${item.name}</span>
  <span part="product-meta">${item.price} € · IVA ${item.vat} %</span>
`;
<dile-select-ajax-overlay
  class="products"
  endpoint="/api/products"
  displayProperty="name"
  optionParts="product-name product-meta"
></dile-select-ajax-overlay>
.products::part(option-content) { display: flex; flex-direction: column; }
.products::part(product-name) { font-weight: 700; }
.products::part(product-meta) { align-self: flex-end; font-size: 0.8em; color: #666; }

Methods

Events

Accessibility

The search field has role="combobox", aria-haspopup="listbox" and aria-expanded. Keyboard navigation (ArrowUp/ArrowDown/Home/End/Enter/Escape) is forwarded to the internal dile-select-overlay popup, which owns the role="listbox"/role="option" markup and highlight state — see dile-select-overlay's accessibility section for the full keyboard pattern.

Because the popup lives in a different shadow root than the search field (the trigger it's embedded under is hidden, not the field driving it), aria-activedescendant can't reliably point across that boundary. Instead, a visually-hidden aria-live="polite" region next to the field announces the currently highlighted option's text as you navigate with the keyboard, so screen reader users still get real-time feedback.

dile-select-ajax-overlay demo

<script type="module">
import { LitElement, html, css } from 'lit';
import '@dile/ui/components/select/select-ajax-overlay.js';

class MyComponentOverlay extends LitElement {
  static get styles() {
    return css`
      :host {
        position: relative;
      }
      dile-select-ajax-overlay {
        z-index: 1;
      }
    `
  }

  render() {
    return html`
      <dile-select-ajax-overlay
        id="select1"
        name="country_id"
        label="Country"
        displayProperty="name"
        endpoint="https://timer.escuelait.com/api/countries"
        delay="500"
        queryStringVariable="keyword"
        resultDataProperty="data"
      ></dile-select-ajax-overlay>

      <p id="msg1">Select a value</p>
      <p style="font-size: 0.875rem;">Note that the API only has 30 countries, so the query results may be shorter than actual list of countries. Notice how the results appear as soon as they load — no extra click needed.</p>
    `
  }
  firstUpdated() {
    this.shadowRoot.getElementById('select1').addEventListener('element-changed', (e) => {
      let textElement = this.shadowRoot.getElementById('msg1');
      textElement.innerText = "The value selected is: " + e.detail.value;
    });
  }
}
customElements.define('my-component-overlay', MyComponentOverlay);
</script>
<my-component-overlay></my-component-overlay>