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 %}
|
{% if ha_enabled %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="edit-ha-entity">Home Assistant Entity ID</label>
|
<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"
|
<input type="text" id="edit-ha-entity" name="ha_entity_id"
|
||||||
value="{{ device.ha_entity_id or '' }}"
|
value="{{ device.ha_entity_id or '' }}"
|
||||||
placeholder="e.g. sensor.tv_remote_battery"
|
placeholder="e.g. sensor.tv_remote_battery"
|
||||||
list="ha-entities-list" autocomplete="off">
|
autocomplete="off">
|
||||||
<datalist id="ha-entities-list"></datalist>
|
<div id="ha-entity-dropdown"
|
||||||
<small class="text-muted" id="ha-entities-status" style="display:block;margin-top:0.25rem;font-size:0.8rem;"></small>
|
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>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button class="btn btn-primary" type="submit">Save Changes</button>
|
<button class="btn btn-primary" type="submit">Save Changes</button>
|
||||||
@@ -237,24 +243,65 @@ function addInstallRow() {
|
|||||||
{% if ha_enabled %}
|
{% if ha_enabled %}
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(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');
|
var status = document.getElementById('ha-entities-status');
|
||||||
if (!datalist) return;
|
if (!input || !dropdown) return;
|
||||||
|
|
||||||
function populate(entities) {
|
var allEntities = [];
|
||||||
datalist.innerHTML = '';
|
|
||||||
entities.forEach(function(e) {
|
function renderDropdown(query) {
|
||||||
var opt = document.createElement('option');
|
var q = query.toLowerCase();
|
||||||
opt.value = e.entity_id;
|
var matches = q
|
||||||
if (e.friendly_name && e.friendly_name !== e.entity_id) opt.label = e.friendly_name;
|
? allEntities.filter(function(e) {
|
||||||
datalist.appendChild(opt);
|
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' : '';
|
if (status) status.textContent = entities.length ? entities.length + ' battery entities available' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var cached = sessionStorage.getItem('ha_battery_entities');
|
var cached = sessionStorage.getItem('ha_battery_entities');
|
||||||
if (cached) { populate(JSON.parse(cached)); return; }
|
if (cached) { onEntities(JSON.parse(cached)); return; }
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
|
|
||||||
if (status) status.textContent = 'Loading HA entities\u2026';
|
if (status) status.textContent = 'Loading HA entities\u2026';
|
||||||
@@ -262,7 +309,7 @@ function addInstallRow() {
|
|||||||
.then(function(r) { return r.json(); })
|
.then(function(r) { return r.json(); })
|
||||||
.then(function(entities) {
|
.then(function(entities) {
|
||||||
try { sessionStorage.setItem('ha_battery_entities', JSON.stringify(entities)); } catch(e) {}
|
try { sessionStorage.setItem('ha_battery_entities', JSON.stringify(entities)); } catch(e) {}
|
||||||
populate(entities);
|
onEntities(entities);
|
||||||
})
|
})
|
||||||
.catch(function() { if (status) status.textContent = ''; });
|
.catch(function() { if (status) status.textContent = ''; });
|
||||||
}());
|
}());
|
||||||
|
|||||||
Reference in New Issue
Block a user