Replace datalist with custom autocomplete dropdown for HA entity field (mobile fix)
This commit is contained in:
@@ -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 = ''; });
|
||||
}());
|
||||
|
||||
Reference in New Issue
Block a user