This commit is contained in:
Mateusz Gruszczyński
2026-09-16 15:28:06 +02:00
parent 3f9acb1675
commit 143ff0d272
16 changed files with 104 additions and 63 deletions
+37 -15
View File
@@ -139,8 +139,8 @@ function haEntityPickerResults(kind, query = '') {
.slice(0, 12);
}
function renderHaEntityPickerResults(input, kind) {
const picker = input?.closest?.('[data-ha-entity-picker]');
function renderHaEntityPickerResults(queryInput, kind) {
const picker = queryInput?.closest?.('[data-ha-entity-picker]');
const host = picker?.querySelector?.('[data-ha-entity-results]');
const note = picker?.querySelector?.('[data-ha-entity-note]');
if (!host || !note) return;
@@ -156,12 +156,23 @@ function renderHaEntityPickerResults(input, kind) {
return;
}
const items = haEntityPickerResults(kind, input.value);
note.textContent = tr('flow.haEntitySearchCount', { count: app.haEntityCatalog.length });
const query = String(queryInput.value || '').trim();
const matchingCount = (app.haEntityCatalog || [])
.filter(entity => haEntityMatchesSharedKind(entity, kind))
.filter(entity => {
if (!query) return true;
const needle = query.toLocaleLowerCase();
return [entity.entity_id, entity.name, entity.state, entity.unit, entity.device_class]
.some(value => String(value || '').toLocaleLowerCase().includes(needle));
}).length;
const items = haEntityPickerResults(kind, query);
note.textContent = query
? tr('flow.haEntitySearchMatches', { count: matchingCount, total: app.haEntityCatalog.length })
: tr('flow.haEntitySearchCount', { count: app.haEntityCatalog.length });
host.innerHTML = items.length ? items.map(entity => {
const title = entity.name || entity.entity_id;
const state = `${entity.state || '—'}${entity.unit ? ` ${entity.unit}` : ''}`;
return `<button type="button" class="ha-entity-option" data-ha-entity-value="${esc(entity.entity_id)}"><span><strong>${esc(title)}</strong><code>${esc(entity.entity_id)}</code></span><small>${esc(state)}</small></button>`;
return `<button type="button" class="ha-entity-option" data-ha-entity-value="${esc(entity.entity_id)}" data-ha-entity-label="${esc(title)}"><span><strong>${esc(title)}</strong><code>${esc(entity.entity_id)}</code></span><small>${esc(state)}</small></button>`;
}).join('') : `<div class="ha-entity-empty">${esc(tr('flow.haEntitySearchEmpty'))}</div>`;
host.hidden = false;
}
@@ -171,7 +182,7 @@ async function loadHaEntityCatalog(force = false) {
if (!force && fresh) return app.haEntityCatalog;
if (app.haEntityCatalogLoading) return app.haEntityCatalog;
app.haEntityCatalogLoading = true;
const activeInput = $('#flowSharedInputFields [data-ha-entity-search]');
const activeInput = $('#flowSharedInputFields [data-ha-entity-query]');
if (activeInput) renderHaEntityPickerResults(activeInput, $('#flowSharedInputKind')?.value || 'ha_state');
try {
const response = await api('/api/integrations/home-assistant/entities');
@@ -185,28 +196,39 @@ async function loadHaEntityCatalog(force = false) {
app.haEntityCatalogLoadedAt = Date.now();
} finally {
app.haEntityCatalogLoading = false;
const input = $('#flowSharedInputFields [data-ha-entity-search]');
const input = $('#flowSharedInputFields [data-ha-entity-query]');
if (input) renderHaEntityPickerResults(input, $('#flowSharedInputKind')?.value || 'ha_state');
}
return app.haEntityCatalog;
}
function haEntityPickerMarkup(kind, value, placeholder) {
return `<div class="ha-entity-picker" data-ha-entity-picker><label><span>entity_id</span><input type="search" autocomplete="off" data-shared-config="entity_id" data-ha-entity-search value="${esc(value || '')}" placeholder="${esc(placeholder)}"></label><small class="field-note" data-ha-entity-note>${esc(tr('flow.haEntitySearchHint'))}</small><div class="ha-entity-results" data-ha-entity-results hidden></div></div>`;
return `<div class="ha-entity-picker" data-ha-entity-picker>
<label class="ha-entity-search-field"><span>${esc(tr('flow.haEntitySearchLabel'))}</span><input type="search" autocomplete="off" data-ha-entity-query placeholder="${esc(tr('flow.haEntitySearchPlaceholder'))}"></label>
<small class="field-note" data-ha-entity-note>${esc(tr('flow.haEntitySearchHint'))}</small>
<div class="ha-entity-results" data-ha-entity-results hidden></div>
<label class="ha-entity-selected-field"><span>${esc(tr('flow.haEntitySelectedLabel'))}</span><input autocomplete="off" data-shared-config="entity_id" data-ha-entity-selected value="${esc(value || '')}" placeholder="${esc(placeholder)}"></label>
</div>`;
}
function bindHaEntityPicker(kind) {
const input = $('#flowSharedInputFields [data-ha-entity-search]');
const queryInput = $('#flowSharedInputFields [data-ha-entity-query]');
const selectedInput = $('#flowSharedInputFields [data-ha-entity-selected]');
const results = $('#flowSharedInputFields [data-ha-entity-results]');
if (!input || !results) return;
const refresh = () => renderHaEntityPickerResults(input, kind);
input.addEventListener('input', refresh);
input.addEventListener('focus', refresh);
if (!queryInput || !selectedInput || !results) return;
const refresh = () => renderHaEntityPickerResults(queryInput, kind);
queryInput.addEventListener('input', refresh);
queryInput.addEventListener('focus', refresh);
queryInput.addEventListener('keydown', event => {
if (event.key === 'Escape') results.hidden = true;
});
results.addEventListener('click', event => {
const option = event.target.closest?.('[data-ha-entity-value]');
if (!option) return;
input.value = option.dataset.haEntityValue || '';
input.dispatchEvent(new Event('change', { bubbles: true }));
selectedInput.value = option.dataset.haEntityValue || '';
selectedInput.dispatchEvent(new Event('input', { bubbles: true }));
selectedInput.dispatchEvent(new Event('change', { bubbles: true }));
queryInput.value = option.dataset.haEntityLabel || option.dataset.haEntityValue || '';
results.hidden = true;
});
loadHaEntityCatalog().then(refresh);