Unlisted
Edited
Mar 13, 2023
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof wiki = SearchForm({
label: "Wikipedia Search",
placeholder: "Enter page name...",
debounce: debounce1,
suggestion: async (text) => {
mutable searchCount += 1;
const res = await d3.json(
`https://en.wikipedia.org/w/api.php?action=opensearch&format=json&search=${text}&namespace=0&limit=10&origin=*`
);
return res[1];
}
})
Insert cell
wiki
Insert cell
mutable searchCount = 0 // increments when api is hit, run to reset
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof wikiObject = SearchForm({
label: "Wikipedia Search",
placeholder: "Enter page name...",
debounce: debounce2,
format: (d) => d.entry, // what you want to search and display
suggestion: async (text) => {
const res = await d3.json(
`https://en.wikipedia.org/w/api.php?action=opensearch&format=json&search=${text}&namespace=0&limit=10&origin=*`
);
return res[1].map((d, i) => ({
entry: d,
url: res[3][i]
}));
}
})
Insert cell
wikiObject
Insert cell
Insert cell
Insert cell
function SearchForm({
uid = DOM.uid("autosuggestion").id,
placeholder = "",
label = "",
showLoading = true,
showResultCount = true,
format = (d) => d,
dataListFormat = null,
suggestion = () => [],
debounce = false,
debounceTime = 250,
debounceOptions = {}
} = {}) {
const input = htl.html`<input
type="search"
placeholder="${placeholder}"
list="${uid}"
autocomplete="off"
>`;

const form = htl.html`<form class="oi-ec050e">
${label ? htl.html`<label>${label}</label>` : null}
<div class="oi-ec050e-input">
${input}
<output id="${uid}-status"></output>
<datalist id="${uid}"></datalist>
</div>
</form>`;

let results = [];

form.value = ""; // starting value
form.onsubmit = (event) => event.preventDefault();

form.onchange = (event) => {
clearStatus();
const value = event.target.value;
form.value = results.find((d) => format(d) == value) || "";
input.blur(); // removes keyboard focus from the element upon submission
form.dispatchEvent(new CustomEvent("input", { bubbles: true })); // bubbles needed for Inputs.form
};

// "cache"
const options = new Map();
options.set("", []);

const clearStatus = () => {
d3.select(`#${uid}-status`).text("");
};

const setLoadingStatus = () => {
d3.select(`#${uid}-status`).text("Loading...");
};

const setResultStatus = (resultCount) => {
d3.select(`#${uid}-status`).text(`${resultCount} results`);
};

const updateDatalist = (r) => {
showResultCount ? setResultStatus(r.length) : clearStatus();
results = r;
d3.select(`#${uid}`)
.selectAll("option")
.data(r)
.join("option")
.attr("value", format);
};

const searchAndUpdate = async (text) => {
if (showLoading) setLoadingStatus();
options.set(text, await suggestion(text));
// if it takes a while for the suggestion to come back, the typed text
// might no longer be the same. In this case we want to cache the results
// but not update the datalist, which would overwrite more recent searches
if (text != input.value) {
return;
}
updateDatalist(options.get(text));
};

const searchBouncer = _.debounce(
(value) => searchAndUpdate(value),
debounceTime,
debounceOptions
);

const handleInput = async (event) => {
if (event.inputType === undefined) {
// Prevents additional searching on enter press
return;
}
event.stopPropagation();
let value = event.target.value;

if (options.has(value)) {
searchBouncer.cancel(); // Prevents overwriting of cache results
updateDatalist(options.get(value));
if (value == "") clearStatus();
} else if (debounce) {
searchBouncer(value);
} else {
searchAndUpdate(value);
}
};

input.oninput = handleInput;
return form;
}
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more