bc926b1bf9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
122 lines
4.3 KiB
JavaScript
122 lines
4.3 KiB
JavaScript
/**
|
|
* Patch: add datalist autocomplete to the <tag-input-field> web component.
|
|
*
|
|
* Fetches /micropub?q=category&filter=<value> as the user types (debounced
|
|
* 300ms) and populates a native <datalist>. Selecting a suggestion fills the
|
|
* text field; the user confirms with comma or blur as usual.
|
|
*
|
|
* Datalist is explicitly cleared in keydown(Comma) and blur handlers because
|
|
* the existing handlers clear the field via programmatic .value = "" which
|
|
* does NOT fire the input event (HTML Living Standard).
|
|
*/
|
|
|
|
import { access, readFile, writeFile } from "node:fs/promises";
|
|
|
|
const MARKER = "// [patch] tag-input-autocomplete";
|
|
|
|
const candidates = [
|
|
"node_modules/@indiekit/frontend/components/tag-input/index.js",
|
|
"node_modules/@indiekit/indiekit/node_modules/@indiekit/frontend/components/tag-input/index.js",
|
|
];
|
|
|
|
const OLD_SNIPPET = ` // Capture any value in input not converted to tag (for example, by clicking
|
|
// outside component before pressing tab key) and add to list of tags.
|
|
$tagInputInput.addEventListener("blur", () => {
|
|
if ($tagInputInput.value) {
|
|
tagInput.addTag($tagInputInput.value, false);
|
|
$tagInputInput.value = "";
|
|
}
|
|
});
|
|
|
|
return tagInput;`;
|
|
|
|
const NEW_SNIPPET = ` // Capture any value in input not converted to tag (for example, by clicking
|
|
// outside component before pressing tab key) and add to list of tags.
|
|
$tagInputInput.addEventListener("blur", () => {
|
|
if ($tagInputInput.value) {
|
|
tagInput.addTag($tagInputInput.value, false);
|
|
$tagInputInput.value = "";
|
|
}
|
|
});
|
|
|
|
// Autocomplete: wire datalist to Micropub q=category endpoint ${MARKER}
|
|
const _micropubHref =
|
|
document.querySelector('link[rel="micropub"]')?.href ?? "/micropub";
|
|
const _datalistId =
|
|
"tag-suggestions-" + (this.$replacedInput?.getAttribute("name") ?? Math.random().toString(36).slice(2));
|
|
const _$datalist = document.createElement("datalist");
|
|
_$datalist.id = _datalistId;
|
|
this.appendChild(_$datalist);
|
|
$tagInputInput.setAttribute("list", _datalistId);
|
|
|
|
let _autocompleteTimer;
|
|
$tagInputInput.addEventListener("input", () => {
|
|
clearTimeout(_autocompleteTimer);
|
|
const _val = $tagInputInput.value.trim();
|
|
if (!_val) { _$datalist.replaceChildren(); return; }
|
|
_autocompleteTimer = setTimeout(async () => {
|
|
try {
|
|
const _res = await fetch(
|
|
\`\${_micropubHref}?q=category&filter=\${encodeURIComponent(_val)}\`
|
|
);
|
|
if (!_res.ok) return;
|
|
const _data = await _res.json();
|
|
_$datalist.replaceChildren(
|
|
...(_data.categories ?? []).map((_cat) => {
|
|
const _opt = document.createElement("option");
|
|
_opt.value = _cat;
|
|
return _opt;
|
|
})
|
|
);
|
|
} catch {}
|
|
}, 300);
|
|
});
|
|
|
|
// Clear datalist after tag confirmed via comma (programmatic clear doesn't fire input)
|
|
$tagInputInput.addEventListener("keydown", (_e) => {
|
|
if (_e.code === "Comma") _$datalist.replaceChildren();
|
|
});
|
|
|
|
// Clear datalist after tag confirmed via blur
|
|
$tagInputInput.addEventListener("blur", () => {
|
|
_$datalist.replaceChildren();
|
|
});
|
|
|
|
return tagInput;`;
|
|
|
|
async function exists(p) {
|
|
try { await access(p); return true; } catch { return false; }
|
|
}
|
|
|
|
let totalPatched = 0;
|
|
let totalChecked = 0;
|
|
|
|
for (const filePath of candidates) {
|
|
if (!(await exists(filePath))) continue;
|
|
totalChecked++;
|
|
|
|
const source = await readFile(filePath, "utf8");
|
|
if (source.includes(MARKER)) {
|
|
console.log(`[postinstall] patch-tag-input-autocomplete: already applied to ${filePath}`);
|
|
continue;
|
|
}
|
|
|
|
if (!source.includes(OLD_SNIPPET)) {
|
|
console.warn(`[postinstall] patch-tag-input-autocomplete: snippet not found in ${filePath} (upstream changed?)`);
|
|
continue;
|
|
}
|
|
|
|
const updated = source.replace(OLD_SNIPPET, NEW_SNIPPET);
|
|
await writeFile(filePath, updated, "utf8");
|
|
console.log(`[postinstall] Applied patch-tag-input-autocomplete to ${filePath}`);
|
|
totalPatched++;
|
|
}
|
|
|
|
if (totalChecked === 0) {
|
|
console.log("[postinstall] patch-tag-input-autocomplete: no target files found");
|
|
} else if (totalPatched === 0) {
|
|
console.log("[postinstall] patch-tag-input-autocomplete: already up to date");
|
|
} else {
|
|
console.log(`[postinstall] patch-tag-input-autocomplete: patched ${totalPatched} file(s)`);
|
|
}
|