Public
Edited
Mar 8, 2023
1 fork
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
DynamicSelector = (config = {}) => {
const {
fn,
label,
type = "one",
choicesConfig = {}
} = typeof config == "function" ? { fn: config } : config;
const defaultChoicesConfig = {
renderChoiceLimit: 3,
removeItemButton: true
};
// create a single or multi select node
const select = html`<select ${type}></select>`;
document.body.appendChild(select);
// initialise choices object with select node and config
const choicesObject = new Choices(select, {
...defaultChoicesConfig,
...choicesConfig
});

// listener for search typing to fire API call and populate choices
select.addEventListener("search", async function (event) {
choicesObject.clearChoices();
choicesObject.setChoices(await fn(event.detail.value));
// TODO:
// The right way is to send setChoices() a promise so that it renders "Loading..."
// But trying this line:
// choicesObject.setChoices(async () => await fn(event.detail.value));
// Causes the text box to unfocus and prevents additional typing after search
});

// Wrapping container to capture change events
const container = html`
<form>
<label style="font:13px/1.2 var(--sans-serif);">${label ?? ""}</label>
${select.closest(".choices")}
</form>`;
container.addEventListener("change", () =>
container.dispatchEvent(new CustomEvent("input"))
);
// On input event, set the value to the selected item(s)
container.addEventListener(
"input",
() => (container.value = Array.from(select, (d) => d.value))
);
container.value = [];
return container;
}
Insert cell
Insert cell
viewof select = Inputs.select(["A", "B"], {label: "Select one"})
Insert cell
Insert cell
searchWikipediaPages = async (search, limit = 10) => {
const res = await d3.json(
`https://en.wikipedia.org/w/api.php?&origin=*&action=opensearch&search=${search}&limit=${limit}`
);
return res[1].map((d, i) => ({ label: d, value: res[3][i] }));
}
Insert cell
Choices = require("choices.js@9.0.1/public/assets/scripts/choices.js")
Insert cell
// https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css with a couple of tweaks
html`<style>

.choices {
width: 300px;
outline: none;
position: relative;
z-index: 1;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
opacity: 1;
border: 0.5px solid white;
border-radius: 5px;
color: #262626;
font-family: var(--sans-serif);
font-size: 12px;
font-weight: 600;
cursor: pointer;
}

.choices:focus {
outline: 0
}

.choices:last-child {
margin-bottom: 0
}

.choices.is-disabled .choices__inner, .choices.is-disabled .choices__input {
background-color: #eaeaea;
cursor: not-allowed;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none
}

.choices.is-disabled .choices__item {
cursor: not-allowed
}

.choices [hidden] {
display: none !important
}

.choices[data-type*=select-one] {
cursor: pointer
}

.choices[data-type*=select-one] .choices__inner {
padding-bottom: 7.5px
}

.choices[data-type*=select-one] .choices__input {
display: block;
width: 100%;
padding: 10px;
border-bottom: 1px solid #ddd;
background-color: #fff;
margin: 0
}

.choices[data-type*=select-one] .choices__button {
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);
padding: 0;
background-size: 8px;
position: absolute;
top: 50%;
right: 0;
margin-top: -10px;
margin-right: 25px;
height: 20px;
width: 20px;
border-radius: 10em;
opacity: .5
}

.choices[data-type*=select-one] .choices__button:focus, .choices[data-type*=select-one] .choices__button:hover {
opacity: 1
}

.choices[data-type*=select-one] .choices__button:focus {
box-shadow: 0 0 0 2px #00bcd4
}

.choices[data-type*=select-one] .choices__item[data-value=''] .choices__button {
display: none
}

.choices[data-type*=select-one]:after {
content: '';
height: 0;
width: 0;
border-style: solid;
border-color: #333 transparent transparent;
border-width: 5px;
position: absolute;
right: 11.5px;
top: 50%;
margin-top: -2.5px;
pointer-events: none
}

.choices[data-type*=select-one].is-open:after {
border-color: transparent transparent #333;
margin-top: -7.5px
}

.choices[data-type*=select-one][dir=rtl]:after {
left: 11.5px;
right: auto
}

.choices[data-type*=select-one][dir=rtl] .choices__button {
right: auto;
left: 0;
margin-left: 25px;
margin-right: 0
}

.choices[data-type*=select-multiple] .choices__inner, .choices[data-type*=text] .choices__inner {
cursor: text
}

.choices[data-type*=select-multiple] .choices__button, .choices[data-type*=text] .choices__button {
position: relative;
display: inline-block;
margin: 0 -4px 0 8px;
padding-left: 16px;
border-left: 1px solid #008fa1;
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);
background-size: 8px;
width: 8px;
line-height: 1;
opacity: .75;
border-radius: 0
}

.choices[data-type*=select-multiple] .choices__button:focus, .choices[data-type*=select-multiple] .choices__button:hover, .choices[data-type*=text] .choices__button:focus, .choices[data-type*=text] .choices__button:hover {
opacity: 1
}

.choices__inner {
display: inline-block;
vertical-align: top;
width: calc(100% - 20px);
background-color: #f9f9f9;
padding: 5px 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
/* min-height: 44px; */
overflow: hidden;
// This line causes multiple carets to appear so i've replaced it with the simpler version
// background: #EFEFEF url("data:image/svg+xml;utf8,<svg viewBox='0 0 140 140' width='20' height='20' xmlns='http://www.w3.org/2000/svg'><g><path d='m121.3,34.6c-1.6-1.6-4.2-1.6-5.8,0l-51,51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l53.9,53.9c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2l53.9-53.9c1.7-1.6 1.7-4.2 0.1-5.8z' fill='black'/></g></svg>") no-repeat;
background: #EFEFEF no-repeat;
background-position: right 7.5px top 50%;
}

.is-focused .choices__inner, .is-open .choices__inner {
border-color: #b7b7b7
}

.is-open .choices__inner {
border-radius: 5px 5px 0 0
}

.is-flipped.is-open .choices__inner {
border-radius: 0 0 5px 5px
}

.choices__list {
margin: 0;
padding-left: 0;
list-style: none
}

.choices__list--single {
display: inline-block;
padding: 4px 16px 4px 4px;
width: 100%
}

[dir=rtl] .choices__list--single {
padding-right: 4px;
padding-left: 16px
}

.choices__list--single .choices__item {
width: 100%
}

.choices__list--multiple {
display: inline
}

.choices__list--multiple .choices__item {
display: inline-block;
vertical-align: middle;
border-radius: 5px;
padding: 4px 10px;
font-size: 12px;
font-weight: 500;
margin-right: 3.75px;
margin-bottom: 3.75px;
background-color: #33A5F4;
border: 1px solid #00a5bb;
color: #fff;
word-break: break-all;
box-sizing: border-box
}

.choices__list--multiple .choices__item[data-deletable] {
padding-right: 5px
}

[dir=rtl] .choices__list--multiple .choices__item {
margin-right: 0;
margin-left: 3.75px
}

.choices__list--multiple .choices__item.is-highlighted {
background-color: #00a5bb;
border: 1px solid #008fa1
}

.is-disabled .choices__list--multiple .choices__item {
background-color: #aaa;
border: 1px solid #919191
}

.choices__list--dropdown {
visibility: hidden;;
position: absolute;
z-index: 1;
width: 100%;
background-color: #fff;
border: 1px solid #ddd;
/* top: 100%; */
margin-top: -1px;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
overflow: hidden;
word-break: break-all;
will-change: visibility
}

.choices__list--dropdown.is-active {
visibility: visible
}

.is-open .choices__list--dropdown {
border-color: #b7b7b7
}

.is-flipped .choices__list--dropdown {
top: auto;
bottom: 100%;
margin-top: 0;
margin-bottom: -1px;
border-radius: .25rem .25rem 0 0
}

.choices__list--dropdown .choices__list {
position: relative;
max-height: 300px;
overflow: auto;
-webkit-overflow-scrolling: touch;
will-change: scroll-position
}

.choices__list--dropdown .choices__item {
position: relative;
padding: 10px;
font-size: 14px
}

[dir=rtl] .choices__list--dropdown .choices__item {
text-align: right
}

@media (min-width:640px) {
.choices__list--dropdown .choices__item--selectable {
padding-right: 100px
}

.choices__list--dropdown .choices__item--selectable:after {
content: attr(data-select-text);
font-size: 12px;
opacity: 0;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%)
}

[dir=rtl] .choices__list--dropdown .choices__item--selectable {
text-align: right;
padding-left: 100px;
padding-right: 10px
}

[dir=rtl] .choices__list--dropdown .choices__item--selectable:after {
right: auto;
left: 10px
}
}

.choices__list--dropdown .choices__item--selectable.is-highlighted {
background-color: #f2f2f2
}

.choices__list--dropdown .choices__item--selectable.is-highlighted:after {
opacity: .5
}

.choices__item {
position: relative;
z-index: 1;
cursor: default
}

.choices__item--selectable {
cursor: pointer
}

.choices__item--disabled {
cursor: not-allowed;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
opacity: .5
}

.choices__heading {
font-weight: 600;
font-size: 12px;
padding: 10px;
border-bottom: 1px solid #f7f7f7;
color: gray
}

.choices__button {
text-indent: -9999px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
border: 0;
background-color: transparent;
background-repeat: no-repeat;
background-position: center;
cursor: pointer
}

.choices__button:focus, .choices__input:focus {
outline: 0
}

.choices__input {
display: inline-block;
vertical-align: baseline;
background-color: #EFEFEF;
font-size: 14px;
margin-bottom: 5px;
border: 0;
border-radius: 0;
max-width: 100%;
padding: 4px 0 4px 2px
}

[dir=rtl] .choices__input {
padding-right: 2px;
padding-left: 0
}

.choices__placeholder {
opacity: 0.5
}

</style>`
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