Skip to content

Commit 940f72b

Browse files
committed
Input, bind, disposal
1 parent bc3d350 commit 940f72b

File tree

8 files changed

+99
-1
lines changed

8 files changed

+99
-1
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,21 @@ User interface components for Observable notebooks.
2525
<a name="Table" href="#Table">#</a> <b>Table</b>(<i>data</i>, <i>options</i>) · [Source](./src/table.js)
2626

2727
28+
29+
## Input
30+
31+
<a name="Input" href="#Input">#</a> <b>Input</b>(<i>value</i>) · [Source](./src/input.js)
32+
33+
34+
35+
## bind
36+
37+
<a name="bind" href="#bind">#</a> <b>bind</b>(<i>input</i>, <i>view</i>, <i>invalidation</i>) · [Source](./src/bind.js)
38+
39+
40+
41+
## disposal
42+
43+
<a name="disposal" href="#disposal">#</a> <b>disposal</b>(<i>element</i>) · [Source](./src/disposal.js)
44+
45+

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
"dist/**/*.js",
1919
"src/**/*.js"
2020
],
21+
"engines": {
22+
"node": ">=14.5.0"
23+
},
2124
"scripts": {
2225
"test": "mkdir -p test/output && tape -r esm -r module-alias/register 'test/**/*-test.js' | tap-dot && node -r esm -r module-alias/register test/input.js | tap-dot && eslint src test",
2326
"prepublishOnly": "rm -rf dist && rollup -c",

src/bind.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {disposal} from "./disposal.js";
2+
3+
export function bind(target, source, invalidation = disposal(target)) {
4+
const onsource = () => set(target, source);
5+
const ontarget = () => (set(source, target), source.dispatchEvent(new CustomEvent("input")));
6+
const sourceEvent = eventof(source);
7+
const targetEvent = eventof(target);
8+
onsource();
9+
target.addEventListener(targetEvent, ontarget);
10+
source.addEventListener(sourceEvent, onsource);
11+
invalidation.then(() => {
12+
target.removeEventListener(targetEvent, ontarget);
13+
source.removeEventListener(sourceEvent, onsource);
14+
});
15+
return target;
16+
}
17+
18+
function get(input) {
19+
switch (input.type) {
20+
case "range":
21+
case "number": return input.valueAsNumber;
22+
case "date": return input.valueAsDate;
23+
case "checkbox": return input.checked;
24+
case "file": return input.multiple ? input.files : input.files[0];
25+
default: return input.value;
26+
}
27+
}
28+
29+
function set(target, source) {
30+
const value = get(source);
31+
switch (target.type) {
32+
case "range":
33+
case "number": target.valueAsNumber = value; break;
34+
case "date": target.valueAsDate = value; break;
35+
case "checkbox": target.checked = value; break;
36+
case "file": target.multiple ? (target.files = value) : (target.files = [value]); break;
37+
default: target.value = value; break;
38+
}
39+
}
40+
41+
function eventof(input) {
42+
switch (input.type) {
43+
case "button":
44+
case "submit": return "click";
45+
case "file": return "change";
46+
default: return "input";
47+
}
48+
}

src/disposal.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export function disposal(element) {
2+
return new Promise(resolve => {
3+
requestAnimationFrame(() => {
4+
const target = element.closest(".observablehq");
5+
if (!target) return resolve();
6+
const observer = new MutationObserver(() => {
7+
if (target.contains(element)) return;
8+
observer.disconnect(), resolve();
9+
});
10+
observer.observe(target, {childList: true});
11+
});
12+
});
13+
}

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ export {Range} from "./range.js";
22
export {Search, searchFilter} from "./search.js";
33
export {Select} from "./select.js";
44
export {Table} from "./table.js";
5+
export {Input} from "./input.js";
6+
export {bind} from "./bind.js";
7+
export {disposal} from "./disposal.js";

src/input.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {bind} from "./bind.js";
2+
3+
export class Input extends EventTarget {
4+
constructor(value) {
5+
super();
6+
this.value = value;
7+
}
8+
bind(input, invalidation) {
9+
return bind(input, this, invalidation);
10+
}
11+
}

test/jsdom.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export async function withJsdom(run) {
2020
const jsdom = new JSDOM("");
2121
global.window = jsdom.window;
2222
global.document = jsdom.window.document;
23+
global.CustomEvent = jsdom.window.CustomEvent;
2324
global.Node = jsdom.window.Node;
2425
global.NodeList = jsdom.window.NodeList;
2526
global.HTMLCollection = jsdom.window.HTMLCollection;
@@ -29,6 +30,7 @@ export async function withJsdom(run) {
2930
} finally {
3031
delete global.window;
3132
delete global.document;
33+
delete global.CustomEvent;
3234
delete global.Node;
3335
delete global.NodeList;
3436
delete global.HTMLCollection;

test/scratch.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@
190190

191191
</style>
192192
</head>
193-
<body>
193+
<body class="observablehq">
194194
<script type="module">
195195

196196
import {Range} from "@observablehq/inputs";

0 commit comments

Comments
 (0)