ControlTile

Molecule

Async controls: a frame (`ControlTile`) plus `ControlButton`, `ControlSwitch`, `ControlSlider` and `ControlSetpoint`. Each takes the bound `value` and a write handler that returns a Promise, and shows idle -> pending -> confirmed / mismatch / failed with the previous value restored on failure. The slider writes once on release. Hand the write to the control with `root.__kuiControl.onCommit = fn`, the `onCommit` global-function name, or the `kui:control-commit` DOM event. `ControlButton` takes an optional `pressed` for a toggle-like button: `true` draws it pressed and announces `aria-pressed="true"`, `false` announces `aria-pressed="false"`, absent is an ordinary button. Same extended props as kui-react: `framed`, `disabledReason`, `confirm`; switch `label` / `icon` / `layout` / `colors` / `size` / `renderLayout`; slider `layout` / `tickMarks` / `orientation` / `thickness` / `thumbSize` / `colors` / `commitMode`; set point `layout` (`stepper`, `dial`) and colours; button `layout` (`icon`, `fab`, `tile`) / `icon` / `variant` / `size` / `align`. No transport knowledge; all text is overridable through `messages`.

Switch: pending then confirmed

Preview

Heater

Code
<%- include('modules/ui/ControlTile/ControlSwitch', { id: 'heater', title: 'Heater', value: false }) %>
<script>
  document.getElementById('heater').__kuiControl.onCommit = (next) => api.setHeater(next);
</script>

Switch: failure rolls back (with Retry)

Preview

Heater

Code
<%- include('modules/ui/ControlTile/ControlSwitch', { id: 'heater', title: 'Heater', value: false }) %>
<script>
  document.getElementById('heater').__kuiControl.onCommit = () => Promise.reject(new Error('Device offline'));
</script>

Switch: the device reports a different value (mismatch)

Preview

Heater

Code
<%- include('modules/ui/ControlTile/ControlSwitch', { id: 'heater', title: 'Heater', value: false, reported: false, timeoutMs: 2500 }) %>
<script>
  const heater = document.getElementById('heater').__kuiControl;
  heater.onCommit = write;
  deviceEvents.on('heater', (state) => heater.setReported(state));
</script>

Slider: commit on release

Preview

Fan speed

0 %40 %100 %
Code
<%- include('modules/ui/ControlTile/ControlSlider', { title: 'Fan speed', value: 40, min: 0, max: 100, step: 5, unit: '%' }) %>

Set point: validated entry

Preview

Target temperature

°C

Current: 21.0 °C

Code
<%- include('modules/ui/ControlTile/ControlSetpoint', { title: 'Target temperature', value: 21, min: 5, max: 30, step: 0.5, unit: '°C', decimals: 1 }) %>

Button: none / confirm / typed confirmation

Preview

Gateway

Gateway

Gateway

Code
<%- include('modules/ui/ControlTile/ControlButton', { title: 'Gateway', label: 'Reboot', confirm: 'typed', confirmText: 'reboot' }) %>

Button: toggle (pressed)

Preview

Fan

Fan

Code
<%- include('modules/ui/ControlTile/ControlButton', { title: 'Fan', label: 'Fan', pressed: fanOn }) %>

Switch layouts, colours and a confirmation

Preview

Pump

Fan

Code
<%- include('modules/ui/ControlTile/ControlSwitch', { title: 'Pump', value: true, label: 'Main pump', icon: '<i class="fa-solid fa-water"></i>', layout: 'right', colors: { on: 'var(--success)' } }) %>
<%- include('modules/ui/ControlTile/ControlSwitch', { title: 'Fan', value: false, label: 'Fan', layout: 'centered', confirm: 'confirm' }) %>

Slider: extended layout with marks, compact layout

Preview

Brightness

60 %
0 %100 %

Volume

30
Code
<%- include('modules/ui/ControlTile/ControlSlider', { title: 'Brightness', value: 60, step: 10, unit: '%', layout: 'extended', tickMarks: 5, tickLabels: true }) %>

Set point: stepper and dial

Preview

Target

21.0 °C

Target

21 °C
Code
<%- include('modules/ui/ControlTile/ControlSetpoint', { title: 'Target', value: 21, min: 5, max: 30, unit: '°C', layout: 'dial' }) %>

Button layouts: icon, fab, tile; a disabled reason

Preview

Power

Gate

Alarm

Pump

The device is offline.
Code
<%- include('modules/ui/ControlTile/ControlButton', { title: 'Power', label: 'Power', layout: 'icon', icon: '<i class="fa-solid fa-power-off"></i>' }) %>
<%- include('modules/ui/ControlTile/ControlButton', { title: 'Pump', label: 'Start', disabledReason: 'The device is offline.' }) %>

Read-only (no permission)

Preview

Heater

Any control goes in the slot.

You do not have permission to use this control.
Code
<%- include('modules/ui/ControlTile/ControlSwitch', { title: 'Heater', value: false, readOnly: true }) %>
Source modules/ui/ControlTile/ControlTile.ejs