forked from observablehq/inputs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathselect.js
77 lines (74 loc) · 2.74 KB
/
select.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import {html} from "htl";
import {arrayify} from "./array.js";
import {preventDefault} from "./event.js";
import {stringify} from "./format.js";
import {boxSizing, defaultStyle, mr2} from "./style.js";
const ns = "observablehq-select";
let nextId = 0;
export function Select(data, {
format = data instanceof Map ? ([key]) => key : d => d,
valueof = data instanceof Map ? ([, value]) => value : d => d,
label,
value,
multiple,
style = {}
} = {}) {
if (typeof format !== "function") throw new TypeError("format is not a function");
if (typeof valueof !== "function") throw new TypeError("valueof is not a function");
data = arrayify(data);
if (multiple === true) multiple = Math.max(1, Math.min(data.length, 10));
const {width = "180px", ...formStyle} = style;
const form = html`<form
onchange=${() => form.dispatchEvent(new CustomEvent("input"))}
oninput=${oninput}
style=${{...defaultStyle, ...formStyle}}>
<select
style=${{...mr2, ...boxSizing, width}}
multiple=${!!multiple}
size=${!!multiple && +multiple}
name=input>
${data.map((d, i) => html`<option selected=${value !== undefined && value === valueof(d, i, data)}>${stringify(format(d, i, data))}`)}
</select>${label}
</form>`;
const {input} = form.elements;
function oninput(event) {
if (event && event.isTrusted) form.onchange = null;
if (multiple) {
form.value = Array.from(input.selectedOptions, ({index: i}) => valueof(data[i], i, data));
} else {
const i = input.selectedIndex;
form.value = valueof(data[i], i, data);
}
}
oninput();
return form;
}
export function AutoSelect(data, {
format = data instanceof Map ? ([key]) => key : d => d,
valueof = data instanceof Map ? ([, value]) => value : d => d,
label,
value,
style = {}
} = {}) {
if (typeof format !== "function") throw new TypeError("format is not a function");
if (typeof valueof !== "function") throw new TypeError("valueof is not a function");
data = arrayify(data);
const {width = "180px", ...formStyle} = style;
const index = new Map(data.map((d, i) => [stringify(format(d, i, data)), i]).reverse());
const id = `${ns}-${++nextId}`;
const form = html`<form
onsubmit=${preventDefault}
oninput=${oninput}
style=${{...defaultStyle, ...formStyle}}>
<input name=input autocomplete=off list=${id} style=${{...mr2, ...boxSizing, width}}>${label}
<datalist id=${id}>${Array.from(index, ([key]) => html`<option>${key}`).reverse()}</datalist>
</form>`;
const {input} = form.elements;
if (value !== undefined) input.value = stringify(format(value));
function oninput() {
const i = index.get(input.value);
form.value = i === undefined ? null : valueof(data[i], i, data);
}
oninput();
return form;
}