tactile-odometer 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +15 -1
  2. package/element.mjs +60 -0
  3. package/package.json +7 -3
package/README.md CHANGED
@@ -11,7 +11,7 @@ Vanilla ES module and CSS, no dependencies, no build step. MIT.
11
11
 
12
12
  **[Play with it →](https://tactile-ui.me/objects/odometer)**
13
13
 
14
- <!-- demo gif: 1,099 → 1,100, then +37 -->
14
+ ![1,099 rolling to 1,100: the last three drums carry together](demo.gif)
15
15
 
16
16
  ## Install
17
17
 
@@ -51,6 +51,20 @@ Or straight from a CDN:
51
51
  It works in React, Vue, Svelte or anything else that can hand it an element:
52
52
  create it in an effect or `onMount`, call `destroy()` on unmount.
53
53
 
54
+ ### As a custom element
55
+
56
+ ```html
57
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tactile-odometer/odometer.css">
58
+ <script type="module" src="https://cdn.jsdelivr.net/npm/tactile-odometer/element.mjs"></script>
59
+
60
+ <tactile-odometer value="1099" digits="4" finish="brass" style="width:480px;height:160px"></tactile-odometer>
61
+ ```
62
+
63
+ Every option below is an attribute of the same name (`maxSpin` is `max-spin`).
64
+ Set `el.value = 1100` or the `value` attribute to roll it; `el.odometer` is the
65
+ Odometer itself. It's made when the element joins the page and destroyed when
66
+ it leaves.
67
+
54
68
  ### Options
55
69
 
56
70
  | Option | Default | What it does |
package/element.mjs ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * <tactile-odometer>: the Odometer as a custom element, for plain HTML,
3
+ * Angular, Astro, Rails or anything else that renders elements.
4
+ *
5
+ * <script type="module" src="tactile-odometer/element.mjs"></script>
6
+ * <tactile-odometer value="1099" digits="4" style="width:480px;height:160px"></tactile-odometer>
7
+ *
8
+ * el.value = 1100; // or el.setAttribute('value', 1100): rolls, and carries
9
+ * el.odometer.count(1); // the Odometer itself, for anything else
10
+ *
11
+ * Every option is an attribute of the same name (see the README). Numbers are
12
+ * read as numbers; grouping, zeros, sound and announce are true unless set to
13
+ * "false". The counter is made when the element joins the page and destroyed
14
+ * when it leaves. The stylesheet is still yours to load.
15
+ */
16
+ import Odometer from './odometer.mjs';
17
+
18
+ const NUMBERS = ['decimals', 'digits', 'stiffness', 'damping', 'maxSpin', 'count', 'wear', 'light', 'size'];
19
+ const STRINGS = ['prefix', 'suffix', 'locale', 'finish', 'red', 'bezel', 'label'];
20
+ const BOOLEANS = ['grouping', 'zeros', 'sound', 'announce'];
21
+ // Attributes are lower-cased by HTML, so maxSpin is written max-spin.
22
+ const attr = (name) => name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
23
+
24
+ export class TactileOdometer extends HTMLElement {
25
+ static observedAttributes = ['value', ...[...NUMBERS, ...STRINGS, ...BOOLEANS].map(attr)];
26
+ #odometer = null;
27
+
28
+ /** The Odometer inside, once the element is on the page. */
29
+ get odometer() { return this.#odometer; }
30
+ get value() { return this.#odometer?.value ?? Number(this.getAttribute('value') ?? 0); }
31
+ set value(v) { this.setAttribute('value', String(v)); }
32
+
33
+ #options() {
34
+ const o = {};
35
+ for (const k of NUMBERS) if (this.hasAttribute(attr(k))) o[k] = k === 'size' && this.getAttribute('size') === 'fit' ? 'fit' : Number(this.getAttribute(attr(k)));
36
+ for (const k of STRINGS) if (this.hasAttribute(attr(k))) o[k] = this.getAttribute(attr(k));
37
+ for (const k of BOOLEANS) if (this.hasAttribute(attr(k))) o[k] = this.getAttribute(attr(k)) !== 'false';
38
+ return o;
39
+ }
40
+
41
+ connectedCallback() {
42
+ if (this.#odometer) return;
43
+ if (!this.style.display) this.style.display = 'block';
44
+ this.#odometer = new Odometer(this, { ...this.#options(), value: Number(this.getAttribute('value') ?? 0) });
45
+ }
46
+
47
+ disconnectedCallback() {
48
+ this.#odometer?.destroy();
49
+ this.#odometer = null;
50
+ }
51
+
52
+ attributeChangedCallback(name, old, value) {
53
+ if (!this.#odometer || old === value) return;
54
+ if (name === 'value') this.#odometer.setValue(Number(value));
55
+ else this.#odometer.update(this.#options());
56
+ }
57
+ }
58
+
59
+ if (!customElements.get('tactile-odometer')) customElements.define('tactile-odometer', TactileOdometer);
60
+ export default TactileOdometer;
package/package.json CHANGED
@@ -1,16 +1,18 @@
1
1
  {
2
2
  "name": "tactile-odometer",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "A mechanical drum counter for the web that carries like the real thing: 1,099 to 1,100 rolls three drums together. Vanilla JS, no dependencies.",
5
5
  "type": "module",
6
6
  "main": "./odometer.mjs",
7
7
  "exports": {
8
8
  ".": "./odometer.mjs",
9
9
  "./odometer.css": "./odometer.css",
10
- "./big-shoulders-bold.woff2": "./big-shoulders-bold.woff2"
10
+ "./big-shoulders-bold.woff2": "./big-shoulders-bold.woff2",
11
+ "./element": "./element.mjs"
11
12
  },
12
13
  "files": [
13
14
  "odometer.mjs",
15
+ "element.mjs",
14
16
  "odometer.css",
15
17
  "big-shoulders-bold.woff2",
16
18
  "BigShoulders-OFL.txt",
@@ -30,7 +32,9 @@
30
32
  "ticker",
31
33
  "drum-counter",
32
34
  "vanilla-js",
33
- "no-dependencies"
35
+ "no-dependencies",
36
+ "custom-element",
37
+ "web-component"
34
38
  ],
35
39
  "license": "MIT",
36
40
  "author": "Ciprian Titire (https://titi.re)",