v0.11.0 #50
@@ -275,7 +275,13 @@ async function _fetchTypeahead(input, fieldKind) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Args go in data-* attributes (not inline onclick) to avoid the quote
|
||||||
|
// collision when location names contain characters JSON.stringify quotes
|
||||||
|
// (e.g. anything with spaces/punctuation — basically every real name).
|
||||||
|
// _esc() escapes for HTML attribute context (entities for <>&"), then
|
||||||
|
// the browser decodes them when reading the dataset value.
|
||||||
dropdown.innerHTML = items.map((it, idx) => {
|
dropdown.innerHTML = items.map((it, idx) => {
|
||||||
|
const cid = _esc(input.dataset.clusterId);
|
||||||
if (it.kind === 'match') {
|
if (it.kind === 'match') {
|
||||||
const m = it.payload;
|
const m = it.payload;
|
||||||
const scoreBadge = m.score >= 0.99
|
const scoreBadge = m.score >= 0.99
|
||||||
@@ -291,16 +297,24 @@ async function _fetchTypeahead(input, fieldKind) {
|
|||||||
}
|
}
|
||||||
const metaLine = meta.length ? `<div class="text-xs text-gray-500 dark:text-gray-400">${meta.join(' · ')}</div>` : '';
|
const metaLine = meta.length ? `<div class="text-xs text-gray-500 dark:text-gray-400">${meta.join(' · ')}</div>` : '';
|
||||||
return `<button type="button"
|
return `<button type="button"
|
||||||
|
data-cid="${cid}"
|
||||||
|
data-field-kind="${fieldKind}"
|
||||||
|
data-entity-id="${_esc(m.id)}"
|
||||||
|
data-entity-name="${_esc(m.name)}"
|
||||||
onmousedown="event.preventDefault()"
|
onmousedown="event.preventDefault()"
|
||||||
onclick="onTypeaheadPick(event, '${_esc(input.dataset.clusterId)}', '${fieldKind}', '${_esc(m.id)}', ${JSON.stringify(m.name)})"
|
onclick="_typeaheadPickFromButton(this)"
|
||||||
class="w-full text-left px-3 py-2 hover:bg-gray-50 dark:hover:bg-slate-700 border-b border-gray-100 dark:border-gray-700 last:border-b-0">
|
class="w-full text-left px-3 py-2 hover:bg-gray-50 dark:hover:bg-slate-700 border-b border-gray-100 dark:border-gray-700 last:border-b-0">
|
||||||
<div class="text-sm font-medium text-gray-900 dark:text-white">${_esc(m.name)}${scoreBadge}</div>
|
<div class="text-sm font-medium text-gray-900 dark:text-white">${_esc(m.name)}${scoreBadge}</div>
|
||||||
${metaLine}
|
${metaLine}
|
||||||
</button>`;
|
</button>`;
|
||||||
}
|
}
|
||||||
return `<button type="button"
|
return `<button type="button"
|
||||||
|
data-cid="${cid}"
|
||||||
|
data-field-kind="${fieldKind}"
|
||||||
|
data-entity-id=""
|
||||||
|
data-entity-name="${_esc(it.name)}"
|
||||||
onmousedown="event.preventDefault()"
|
onmousedown="event.preventDefault()"
|
||||||
onclick="onTypeaheadPick(event, '${_esc(input.dataset.clusterId)}', '${fieldKind}', '', ${JSON.stringify(it.name)})"
|
onclick="_typeaheadPickFromButton(this)"
|
||||||
class="w-full text-left px-3 py-2 hover:bg-orange-50 dark:hover:bg-orange-900/20 border-t border-gray-200 dark:border-gray-700 text-seismo-orange font-medium text-sm">
|
class="w-full text-left px-3 py-2 hover:bg-orange-50 dark:hover:bg-orange-900/20 border-t border-gray-200 dark:border-gray-700 text-seismo-orange font-medium text-sm">
|
||||||
+ ${_esc(it.label)}
|
+ ${_esc(it.label)}
|
||||||
</button>`;
|
</button>`;
|
||||||
@@ -308,6 +322,19 @@ async function _fetchTypeahead(input, fieldKind) {
|
|||||||
dropdown.classList.remove('hidden');
|
dropdown.classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trampoline — reads the click target's data-* attributes and forwards
|
||||||
|
// to onTypeaheadPick. Keeps the inline onclick attribute free of any
|
||||||
|
// string interpolation that could collide with HTML quoting.
|
||||||
|
function _typeaheadPickFromButton(btn) {
|
||||||
|
onTypeaheadPick(
|
||||||
|
null,
|
||||||
|
btn.dataset.cid,
|
||||||
|
btn.dataset.fieldKind,
|
||||||
|
btn.dataset.entityId || '',
|
||||||
|
btn.dataset.entityName || ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function onTypeaheadPick(event, clusterId, fieldKind, entityId, name) {
|
function onTypeaheadPick(event, clusterId, fieldKind, entityId, name) {
|
||||||
// entityId is empty string for "create new", or a UUID for matched existing.
|
// entityId is empty string for "create new", or a UUID for matched existing.
|
||||||
const inputs = document.querySelectorAll(`input[data-cluster-id="${clusterId}"]`);
|
const inputs = document.querySelectorAll(`input[data-cluster-id="${clusterId}"]`);
|
||||||
|
|||||||
Reference in New Issue
Block a user