Skip to content

About

Collection of different control UI widgets used in the Edirom and the Control Bar web component to wrap these widgets

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

This repository is work in progress and highly experimental at the moment.

This repository collects web components that are used to control views in the Edirom. It consists of:

  • edirom-control-bar — a wrapper web component for different constellations of control elements.
  • different control elements in the form of small web components:
    • edirom-button-widget — a square icon button widget for use inside a control bar.
    • edirom-spin-box-widget — a step navigator with previous/next buttons and a text input.
    • edirom-spacer-widget — a layout helper that fills available space between control widgets.
    • edirom-select-widget — a single-choice selector built on a native <select>.
    • edirom-multi-select-widget — an icon button that opens a checkbox list (dropdown or bottom sheet) for choosing several values.

All widgets follow an external-state-only pattern: a user action dispatches a …-requested event, and the host application confirms it by setting the corresponding attribute. A demo page for manual testing lives in demo/index.html (serve the parent Web-Components directory, because the widgets import edirom-icon from ../edirom-core-web-components).


edirom-button-widget

A square icon button for use inside an edirom-control-bar. It renders an edirom-icon (from edirom-core-web-components) centred inside a rounded container. The element maintains a 1:1 aspect ratio and fills the full height of its parent.

The button operates as a state carousel: it holds a list of named states (each with an associated icon), displays the icon of the current state, and on each click dispatches a request for the next state without advancing itself. The calling application is responsible for setting the new current-state attribute in response to that event.

Attributes

Attribute Type Default Description
states-data JSON string (array) '[]' Stringified array of state objects. Each object must have a name (string) and an iconName (string) key. The array order defines the carousel sequence.
current-state string — The name of the active state. When set, the button renders the corresponding icon. If the name is not found in states-data, an error is logged and the button does not update. If absent, the button renders with an empty icon.

State object shape:

{ "name": "unlocked", "iconName": "lock_open" }

Events

Event bubbles composed detail Description
button-state-requested ✅ ✅ { requestedState: string } Fired on click. requestedState is the name of the next state in carousel order. The component does not change state on its own — set current-state externally to confirm the transition.

CSS Custom Properties

Property Default Description
--secondary-color #ffffff00 (transparent) Background colour of the button inner container.
--button-widget-padding 2px Padding inside the button inner container.

Usage

<script type="module" src="edirom-button-widget.js"></script>

<!-- Two-state lock toggle -->
<edirom-button-widget
  states-data='[{"name":"unlocked","iconName":"lock_open"},{"name":"locked","iconName":"lock_closed"}]'
  current-state="unlocked">
</edirom-button-widget>

<!-- Handling the state-change request externally -->
<script>
  const btn = document.querySelector('edirom-button-widget');
  btn.addEventListener('button-state-requested', (e) => {
      btn.setAttribute('current-state', e.detail.requestedState);
  });
</script>

<!-- Inside a control bar -->
<edirom-control-bar gap="8px">
  <edirom-button-widget
    states-data='[{"name":"play","iconName":"play"},{"name":"pause","iconName":"pause"}]'
    current-state="play">
  </edirom-button-widget>
  <edirom-spacer-widget></edirom-spacer-widget>
  <edirom-button-widget
    states-data='[{"name":"unlocked","iconName":"lock_open"},{"name":"locked","iconName":"lock_closed"}]'
    current-state="unlocked">
  </edirom-button-widget>
</edirom-control-bar>

edirom-control-bar

A horizontal flex-row container for control widgets. It fills the full width and height of its parent and centers all child widgets vertically. Widgets are inserted via the default slot — no slot="..." attribute needed on children.

Attributes

Attribute Type Default Description
layout-mode "desktop" | "mobile" "desktop" Switches the template. Desktop uses 8px horizontal padding, mobile uses 4px.
gap CSS length string "0" Gap between child widgets, passed directly to the CSS gap property (e.g. "8px", "0.5rem").

Usage

<script src="edirom-control-bar.js"></script>
<script src="edirom-spacer-widget.js"></script>

<!-- Basic bar -->
<edirom-control-bar gap="8px">
  <edirom-button>Back</edirom-button>
  <edirom-button>Forward</edirom-button>
</edirom-control-bar>

<!-- Left group / right group separated by a spacer -->
<edirom-control-bar gap="8px">
  <edirom-button>Left</edirom-button>
  <edirom-spacer-widget></edirom-spacer-widget>
  <edirom-slider></edirom-slider>
  <edirom-menu></edirom-menu>
</edirom-control-bar>

<!-- Three groups with unequal spacing between them -->
<edirom-control-bar gap="8px">
  <edirom-button>Left</edirom-button>
  <edirom-spacer-widget></edirom-spacer-widget>
  <edirom-button>Center</edirom-button>
  <edirom-spacer-widget grow="2"></edirom-spacer-widget>
  <edirom-button>Right</edirom-button>
</edirom-control-bar>

<!-- Mobile layout -->
<edirom-control-bar layout-mode="mobile" gap="4px">
  <edirom-button>A</edirom-button>
  <edirom-spacer-widget></edirom-spacer-widget>
  <edirom-button>B</edirom-button>
</edirom-control-bar>

edirom-spacer-widget

A layout helper that expands to fill available space in the flex row of an edirom-control-bar. Inserting a spacer between two widgets pushes the widgets on either side to opposite ends of the bar. Multiple spacers with different grow values create proportional spacing groups.

Attributes

Attribute Type Default Description
grow Number 1 Maps to CSS flex-grow. A spacer-widget with grow="2" consumes twice the available space compared to a default spacer-widget.

Usage

<!-- Equal spacing on both sides of a centered group -->
<edirom-control-bar gap="8px">
  <edirom-spacer-widget></edirom-spacer-widget>
  <edirom-button>Centered</edirom-button>
  <edirom-spacer-widget></edirom-spacer-widget>
</edirom-control-bar>

<!-- Weighted: center item closer to the right -->
<edirom-control-bar gap="8px">
  <edirom-spacer-widget grow="2"></edirom-spacer-widget>
  <edirom-button>Skewed right</edirom-button>
  <edirom-spacer-widget></edirom-spacer-widget>
</edirom-control-bar>

edirom-spin-box-widget

A step navigator for use inside an edirom-control-bar. It displays a text input flanked by a previous (eo_previous) and a next (eo_next) icon button. The user can step through a predefined list of values by clicking the icons or by typing a value into the text field and pressing Enter.

Like edirom-button-widget, this component follows an external-state-only pattern: clicking a button or submitting a text value dispatches a request event but does not change the current step. The host application must set the current-step attribute in response to validate and confirm the transition.

Attributes

Attribute Type Default Description
steps-data JSON string (array) '[]' Stringified array of step values. Each element can be a string or an integer (e.g. '["Step 1", 2, "5", 100]'). The array order defines the navigation sequence. All values are compared as strings internally.
current-step string — The value of the active step. Must match a value in steps-data when compared as a string (e.g. set "2" to match the integer 2). If the value is not found, a warning is logged and the display is not updated. When steps-data contains duplicate values, the occurrence nearest to the previously resolved index is selected (first occurrence on initial load).
carrousel "true" | "false" "false" When "true", navigation wraps around: stepping past the last value continues at the first, and vice versa. When "false", the previous button is hidden at the first step and the next button is hidden at the last step.
label string — Optional short label rendered above the text input in a small font (~60% of the host font size). The label is left-aligned and truncated with an ellipsis (…) when it is wider than the input field. When set to a non-empty string, the input shrinks to 75% of the component height and its font size is reduced to 75% to leave room for the label. When absent or empty, the input occupies the full height and font size (no visual change).

Events

Event bubbles composed detail Description
spin-box-step-requested ✅ ✅ { requestedStep: string } Fired when the user clicks a navigation icon or submits a valid value via the text input (Enter key). requestedStep is the string value of the target step. The component does not change step on its own — set current-step externally to confirm the transition. If the user types a value not present in steps-data, no event is fired and the input reverts to the current step.

CSS Custom Properties

Property Default Description
--spin-box-input-width 4ch Width of the text input field.
--spin-box-gap 0 Gap between the previous icon, text input, and next icon.
--spin-box-border-color #ccc Border colour of the text input.
--spin-box-input-color fieldtext (browser default) Text colour of the text input.
--spin-box-input-background field (browser default) Background colour of the text input.
--spin-box-border-radius 4px Border radius of the text input.
--spin-box-input-padding 0 2px Padding inside the text input.

Usage

<script type="module" src="edirom-spin-box-widget.js"></script>

<!-- Basic step navigation -->
<edirom-spin-box-widget
  steps-data='["Schritt 1", 2, "5", 100, "another string"]'
  current-step="Schritt 1">
</edirom-spin-box-widget>

<!-- Handling the step-change request externally -->
<script>
  const spin = document.querySelector('edirom-spin-box-widget');
  spin.addEventListener('spin-box-step-requested', (e) => {
      spin.setAttribute('current-step', e.detail.requestedStep);
  });
</script>

<!-- Carrousel mode -->
<edirom-spin-box-widget
  steps-data='["A", "B", "C"]'
  current-step="A"
  carrousel="true">
</edirom-spin-box-widget>

<!-- Custom input width -->
<style>
  .wide-spin-box {
    --spin-box-input-width: 10ch;
  }
</style>
<edirom-spin-box-widget
  class="wide-spin-box"
  steps-data='["Largo", "Andante", "Allegro", "Presto"]'
  current-step="Andante">
</edirom-spin-box-widget>

<!-- With a label above the input -->
<edirom-spin-box-widget
  steps-data='[1, 2, 3, 4, 5]'
  current-step="1"
  label="Annotationen">
</edirom-spin-box-widget>

<!-- Inside a control bar -->
<edirom-control-bar gap="8px">
  <edirom-spacer-widget></edirom-spacer-widget>
  <edirom-spin-box-widget
    steps-data='[1, 2, 3, 4, 5]'
    current-step="1">
  </edirom-spin-box-widget>
  <edirom-spacer-widget></edirom-spacer-widget>
</edirom-control-bar>

edirom-select-widget

A single-choice selector for use inside an edirom-control-bar, built on a native <select>. Phones show their own picker (wheel or sheet) with the full option labels, while the closed field can be kept compact and is truncated with an ellipsis. Keyboard and screen-reader support come from the native element.

The component knows nothing about its content: options are a generic list of values and labels (movements, works, languages, view types, …). Choosing an option dispatches a request event and does not change the value on its own — set value externally to confirm it. Until then the field shows the confirmed value again.

Attributes

Attribute Type Default Description
options-data JSON string (array) '[]' Stringified array of options. Each element is either an object { "value": string, "label"?: string, "group"?: string, "disabled"?: boolean } or a plain string/number (used as value and label). Options sharing a group are rendered in an <optgroup> with that label.
value string — The confirmed value. If it is absent or not found in options-data, the placeholder is shown (and a warning is logged for an unknown value).
label string — Optional short caption rendered above the field in a small font, like the label of edirom-spin-box-widget. With a label the field shrinks to 75% of the height and font size.
placeholder string '' Text shown while no valid value is confirmed. It cannot be chosen by the user.
accessible-label string value of label Accessible name of the field (aria-label). Use it when there is no visible label.
disabled boolean — Disables the field.
custom-picker boolean — Opt-in: in browsers that support customizable selects (appearance: base-select) the open list is styled with the widget's theme instead of the platform picker. Leave it off to keep the native picker on phones.

Events

Event bubbles composed detail Description
select-value-requested ✅ ✅ { requestedValue: string } Fired when the user chooses an option. Set value to confirm it.

CSS Custom Properties

Property Default Description
--select-widget-max-width none Maximum width of the widget (e.g. 10ch for a compact field in a mobile control bar).
--select-widget-color fieldtext Text colour of the field.
--select-widget-background field Background colour of the field.
--select-widget-border-color #ccc Border colour of the field (and of the custom picker).
--select-widget-border-radius 4px Border radius of the field.
--select-widget-padding 0 2px Padding inside the field.
--select-widget-picker-background var(--tertiary-color, Canvas) Background of the open list (only with custom-picker).
--select-widget-picker-color var(--primary-color, CanvasText) Text colour of the open list (only with custom-picker).

Parts and states

Selector Description
::part(inner), ::part(label), ::part(select) The wrapper, the caption and the native <select>.
:state(empty) No valid value is confirmed (the placeholder is shown).

Usage

<script type="module" src="edirom-select-widget.js"></script>

<edirom-select-widget
  label="Movement"
  options-data='[{"value":"m1","label":"Allegro"},{"value":"m2","label":"Adagio"}]'
  value="m1">
</edirom-select-widget>

<script>
  const select = document.querySelector('edirom-select-widget');
  select.addEventListener('select-value-requested', (e) => {
      select.setAttribute('value', e.detail.requestedValue);
  });
</script>

<!-- Compact inside a mobile control bar -->
<style>
  #movement-select { --select-widget-max-width: 10ch; }
</style>

edirom-multi-select-widget

A multiple-choice selector for use inside an edirom-control-bar: a square icon button (same look as edirom-button-widget) that opens a panel with a checkbox list. The panel uses the native Popover API, so it lives in the top layer and closes on Escape or a click outside without extra script.

  • layout-mode="desktop" (default): a dropdown anchored to the button (CSS anchor positioning, which flips above the button when there is no room below; a script fallback places it in browsers without anchor positioning).
  • layout-mode="mobile": a bottom sheet over a dimmed page, with touch-sized rows.

Like the other widgets it is content-agnostic (voices, annotation categories, layers, overlays, …) and follows the external-state pattern: a change dispatches multi-select-values-requested, and the checkboxes show the confirmed selected-values until the host sets them.

Attributes

Attribute Type Default Description
options-data JSON string (array) '[]' Stringified array of options, in display order. Each element is either an object { "value": string, "label"?: string, "group"?: string, "disabled"?: boolean } or a plain string/number. Options sharing a group are rendered in a <fieldset> with that legend. Duplicate values are ignored.
selected-values JSON string (array) '[]' The confirmed selection, as an array of values.
icon string — Name of the edirom-icon shown in the button (e.g. eo_voice_filter).
label string — Heading of the panel, also the button's tooltip and accessible name.
accessible-label string value of label Overrides the accessible name and tooltip of the button.
select-all-label string — When set, a "select all" checkbox with this text is shown above the options. It is mixed (indeterminate) when only some options are selected; unchecking it keeps the first min-selected options.
min-selected integer 0 Minimum number of selected options. When it is reached, the remaining selected checkboxes are disabled.
max-selected integer — Maximum number of selected options. When it is reached, the unselected checkboxes are disabled.
commit-mode "immediate" | "confirm" "immediate" immediate: every checkbox change dispatches a request. confirm: changes are collected and dispatched on the apply button; cancel, Escape or a click outside discard them.
apply-label / cancel-label string "OK" / "Cancel" Texts of the buttons in confirm mode.
close-label string "Close" Accessible name and tooltip of the close button (✕) in the top right corner of the panel. It closes the panel and, in confirm mode, discards unapplied changes.
layout-mode "desktop" | "mobile" "desktop" Dropdown or bottom sheet (see above).
disabled boolean — Disables the button (and closes an open panel).

Events

Event bubbles composed detail Description
multi-select-values-requested ✅ ✅ { requestedValues: string[] } Fired on a change (immediate mode) or on apply (confirm mode). The values are in options order. Set selected-values to confirm them.
multi-select-toggle ✅ ✅ { open: boolean } Fired when the panel opens or closes.

Properties and methods

Member Description
open (read-only) Whether the panel is open.
show() / hide() Opens / closes the panel (e.g. to close it on a "back" gesture).
selectedValues (read-only) The confirmed values, in options order.

CSS Custom Properties

Property Default Description
--secondary-color #ffffff00 (transparent) Background colour of the button.
--button-widget-padding 2px Padding inside the button.
--multi-select-accent var(--primary-color, AccentColor) Checkbox colour (accent-color), focus ring and apply button.
--multi-select-panel-background var(--tertiary-color, Canvas) Background of the panel.
--multi-select-panel-color var(--primary-color, CanvasText) Text colour of the panel.
--multi-select-panel-border-color 20% of the text colour Border of the panel.
--multi-select-panel-width max-content Width of the dropdown (desktop).
--multi-select-panel-max-height min(70vh, 30rem) (desktop), 60dvh (mobile) Maximum height of the panel; the list scrolls inside it.

Parts and states

Selector Description
::part(trigger) The icon button.
::part(panel), ::part(header), ::part(heading), ::part(close-button), ::part(body), ::part(footer) The panel and its sections; the header holds the heading and the close button.
::part(group), ::part(group-label) A <fieldset> of grouped options and its legend.
::part(option), ::part(option-label), ::part(select-all) An option row, its text, and the select-all row.
::part(apply-button), ::part(cancel-button) The buttons in confirm mode.
:state(open) The panel is open.
:state(partial) Some, but not all, options are selected (e.g. to show that a filter is active).
:state(none) No option is selected.

Usage

<script type="module" src="edirom-multi-select-widget.js"></script>

<edirom-multi-select-widget
  icon="eo_voice_filter"
  label="Select parts"
  select-all-label="All parts"
  min-selected="1"
  layout-mode="mobile"
  options-data='[{"value":"cl","label":"Clarinet"},{"value":"vl1","label":"Violin I"},{"value":"vc","label":"Violoncello"}]'
  selected-values='["cl","vl1","vc"]'>
</edirom-multi-select-widget>

<script>
  const voices = document.querySelector('edirom-multi-select-widget');
  voices.addEventListener('multi-select-values-requested', (e) => {
      voices.setAttribute('selected-values', JSON.stringify(e.detail.requestedValues));
  });
</script>

<!-- Highlight the button while a filter is active -->
<style>
  edirom-multi-select-widget:state(partial) { --secondary-color: #aaa; }
</style>

About

Collection of different control UI widgets used in the Edirom and the Control Bar web component to wrap these widgets

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages