Replace datalist with custom autocomplete dropdown for HA entity field (mobile fix)

This commit is contained in:
2026-04-14 01:38:42 -05:00
parent b6a3533fed
commit a9d0b3fc63
+61 -14
View File
@@ -210,12 +210,18 @@ function addInstallRow() {
{% if ha_enabled %}
<div class="form-group">
<label for="edit-ha-entity">Home Assistant Entity ID</label>
<div style="position:relative;">
<input type="text" id="edit-ha-entity" name="ha_entity_id"
value="{{ device.ha_entity_id or '' }}"
placeholder="e.g. sensor.tv_remote_battery"
list="ha-entities-list" autocomplete="off">
<datalist id="ha-entities-list"></datalist>
<small class="text-muted" id="ha-entities-status" style="display:block;margin-top:0.25rem;font-size:0.8rem;"></small>
autocomplete="off">
<div id="ha-entity-dropdown"
style="display:none;position:absolute;left:0;right:0;top:100%;z-index:200;
background:var(--bg-card);border:1px solid var(--border);border-radius:4px;
max-height:220px;overflow-y:auto;box-shadow:0 4px 12px rgba(0,0,0,0.15);"></div>
</div>
<small class="text-muted" id="ha-entities-status"
style="display:block;margin-top:0.25rem;font-size:0.8rem;"></small>
</div>
{% endif %}
<button class="btn btn-primary" type="submit">Save Changes</button>
@@ -237,24 +243,65 @@ function addInstallRow() {
{% if ha_enabled %}
<script>
(function() {
var datalist = document.getElementById('ha-entities-list');
var input = document.getElementById('edit-ha-entity');
var dropdown = document.getElementById('ha-entity-dropdown');
var status = document.getElementById('ha-entities-status');
if (!datalist) return;
if (!input || !dropdown) return;
function populate(entities) {
datalist.innerHTML = '';
entities.forEach(function(e) {
var opt = document.createElement('option');
opt.value = e.entity_id;
if (e.friendly_name && e.friendly_name !== e.entity_id) opt.label = e.friendly_name;
datalist.appendChild(opt);
var allEntities = [];
function renderDropdown(query) {
var q = query.toLowerCase();
var matches = q
? allEntities.filter(function(e) {
return e.entity_id.toLowerCase().indexOf(q) !== -1
|| (e.friendly_name && e.friendly_name.toLowerCase().indexOf(q) !== -1);
})
: allEntities;
if (!matches.length) { dropdown.style.display = 'none'; return; }
dropdown.innerHTML = '';
matches.slice(0, 60).forEach(function(e) {
var item = document.createElement('div');
item.style.cssText = 'padding:0.5rem 0.75rem;cursor:pointer;border-bottom:1px solid var(--border);';
var label = document.createElement('div');
label.style.cssText = 'font-size:0.875rem;word-break:break-all;';
label.textContent = e.entity_id;
item.appendChild(label);
if (e.friendly_name && e.friendly_name !== e.entity_id) {
var sub = document.createElement('div');
sub.style.cssText = 'font-size:0.75rem;color:var(--text-muted);';
sub.textContent = e.friendly_name;
item.appendChild(sub);
}
item.addEventListener('mousedown', function(ev) { ev.preventDefault(); });
item.addEventListener('click', function() {
input.value = e.entity_id;
dropdown.style.display = 'none';
input.focus();
});
item.addEventListener('mouseover', function() { this.style.background = 'var(--bg-hover)'; });
item.addEventListener('mouseout', function() { this.style.background = ''; });
dropdown.appendChild(item);
});
dropdown.style.display = 'block';
}
input.addEventListener('input', function() { renderDropdown(this.value); });
input.addEventListener('focus', function() { if (allEntities.length) renderDropdown(this.value); });
input.addEventListener('blur', function() { setTimeout(function() { dropdown.style.display = 'none'; }, 150); });
input.addEventListener('keydown', function(e) { if (e.key === 'Escape') dropdown.style.display = 'none'; });
document.addEventListener('click', function(e) {
if (!input.contains(e.target) && !dropdown.contains(e.target)) dropdown.style.display = 'none';
});
function onEntities(entities) {
allEntities = entities;
if (status) status.textContent = entities.length ? entities.length + ' battery entities available' : '';
}
try {
var cached = sessionStorage.getItem('ha_battery_entities');
if (cached) { populate(JSON.parse(cached)); return; }
if (cached) { onEntities(JSON.parse(cached)); return; }
} catch(e) {}
if (status) status.textContent = 'Loading HA entities\u2026';
@@ -262,7 +309,7 @@ function addInstallRow() {
.then(function(r) { return r.json(); })
.then(function(entities) {
try { sessionStorage.setItem('ha_battery_entities', JSON.stringify(entities)); } catch(e) {}
populate(entities);
onEntities(entities);
})
.catch(function() { if (status) status.textContent = ''; });
}());