@profpowell/blink-wc 1.5.0 → 1.5.2
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.
- package/README.md +6 -0
- package/custom-elements.json +8 -9
- package/dist/blink-wc.css +21 -5
- package/dist/blink-wc.js +144 -120
- package/dist/blink-wc.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -289,6 +289,12 @@ Relies on CSS relative colour syntax and `light-dark()` (Baseline 2024).
|
|
|
289
289
|
`:root[data-motion-reduced]` — halts all animation (including the terminal and
|
|
290
290
|
typewriter carets) and shows the text fully, unless you explicitly set
|
|
291
291
|
`reduced-motion="ignore"`. Changes apply live.
|
|
292
|
+
- Screen readers hear the text once, as written. Letter and word modes split
|
|
293
|
+
the text into animated spans, so those spans are hidden from assistive tech
|
|
294
|
+
and a visually hidden copy of the text is exposed instead (unless the content
|
|
295
|
+
holds links or other interactive elements, which are left as authored). The
|
|
296
|
+
terminal cursor, typewriter caret and morse dot/dash caption are decorative
|
|
297
|
+
and are not announced.
|
|
292
298
|
- The blink pauses when off-screen and when the tab is hidden.
|
|
293
299
|
- `pause-on-hover` also pauses on keyboard focus (`:focus-within`).
|
|
294
300
|
- Avoid fast, large-area blinking, which can trigger photosensitive seizures
|
package/custom-elements.json
CHANGED
|
@@ -61,6 +61,10 @@
|
|
|
61
61
|
"name": "unit",
|
|
62
62
|
"readonly": true
|
|
63
63
|
},
|
|
64
|
+
{
|
|
65
|
+
"kind": "method",
|
|
66
|
+
"name": "connectedMoveCallback"
|
|
67
|
+
},
|
|
64
68
|
{
|
|
65
69
|
"kind": "method",
|
|
66
70
|
"name": "start"
|
|
@@ -87,20 +91,15 @@
|
|
|
87
91
|
},
|
|
88
92
|
{
|
|
89
93
|
"kind": "method",
|
|
90
|
-
"name": "
|
|
91
|
-
"parameters": [
|
|
92
|
-
{
|
|
93
|
-
"name": "byWord",
|
|
94
|
-
"default": "this.unit === 'word'"
|
|
95
|
-
}
|
|
96
|
-
]
|
|
94
|
+
"name": "_syncSpeech"
|
|
97
95
|
},
|
|
98
96
|
{
|
|
99
97
|
"kind": "method",
|
|
100
|
-
"name": "
|
|
98
|
+
"name": "_splitLetters",
|
|
101
99
|
"parameters": [
|
|
102
100
|
{
|
|
103
|
-
"name": "
|
|
101
|
+
"name": "byWord",
|
|
102
|
+
"default": "this.unit === 'word'"
|
|
104
103
|
}
|
|
105
104
|
]
|
|
106
105
|
},
|
package/dist/blink-wc.css
CHANGED
|
@@ -50,6 +50,16 @@
|
|
|
50
50
|
display: inline-block;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/* Screen-reader copy of split text (the animated spans are aria-hidden). */
|
|
54
|
+
blink-wc .blink-sr {
|
|
55
|
+
position: absolute;
|
|
56
|
+
inline-size: 1px;
|
|
57
|
+
block-size: 1px;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
clip-path: inset(50%);
|
|
60
|
+
white-space: nowrap;
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
/* ─── Keyframes ──────────────────────────────────────────────────────── */
|
|
54
64
|
|
|
55
65
|
/* blink — the classic: hard on/off, no easing */
|
|
@@ -119,12 +129,18 @@
|
|
|
119
129
|
|
|
120
130
|
/* !important: the per-behavior `animation` shorthand resets play-state to
|
|
121
131
|
running, so the pause overrides must beat it regardless of specificity. */
|
|
132
|
+
/* .blink-content::after is the terminal cursor / typewriter caret. It needs
|
|
133
|
+
its own selector: :is() cannot match pseudo-elements. */
|
|
122
134
|
blink-wc[data-state='paused'] :is(.blink-content, .blink-char),
|
|
135
|
+
blink-wc[data-state='paused'] .blink-content::after,
|
|
123
136
|
blink-wc[data-visible='false'] :is(.blink-content, .blink-char),
|
|
124
|
-
blink-wc[data-
|
|
137
|
+
blink-wc[data-visible='false'] .blink-content::after,
|
|
138
|
+
blink-wc[data-tab-visible='false'] :is(.blink-content, .blink-char),
|
|
139
|
+
blink-wc[data-tab-visible='false'] .blink-content::after {
|
|
125
140
|
animation-play-state: paused !important;
|
|
126
141
|
}
|
|
127
|
-
blink-wc[pause-on-hover]:where(:hover, :focus-within) :is(.blink-content, .blink-char)
|
|
142
|
+
blink-wc[pause-on-hover]:where(:hover, :focus-within) :is(.blink-content, .blink-char),
|
|
143
|
+
blink-wc[pause-on-hover]:where(:hover, :focus-within) .blink-content::after {
|
|
128
144
|
animation-play-state: paused !important;
|
|
129
145
|
}
|
|
130
146
|
blink-wc[pause-on-hover] {
|
|
@@ -248,7 +264,7 @@
|
|
|
248
264
|
animation: none;
|
|
249
265
|
}
|
|
250
266
|
blink-wc[mode='terminal'] .blink-content::after {
|
|
251
|
-
content: '▋';
|
|
267
|
+
content: '▋' / ''; /* decorative: not announced */
|
|
252
268
|
margin-inline-start: 0.1em;
|
|
253
269
|
color: var(--color-accent, var(--color-success, oklch(80% 0.2 145)));
|
|
254
270
|
animation: blink-wc-blink var(--blink-rate, 1s) step-end infinite;
|
|
@@ -348,7 +364,7 @@
|
|
|
348
364
|
0 0 0.8em currentColor;
|
|
349
365
|
}
|
|
350
366
|
blink-wc[mode='morse']::after {
|
|
351
|
-
content: attr(data-morse);
|
|
367
|
+
content: attr(data-morse) / ''; /* duplicates the text: not announced */
|
|
352
368
|
display: block;
|
|
353
369
|
margin-block-start: 0.4rem;
|
|
354
370
|
font-size: 0.75em;
|
|
@@ -474,7 +490,7 @@
|
|
|
474
490
|
}
|
|
475
491
|
}
|
|
476
492
|
blink-wc[mode='typewriter'] .blink-content::after {
|
|
477
|
-
content: '▋';
|
|
493
|
+
content: '▋' / ''; /* decorative: not announced */
|
|
478
494
|
margin-inline-start: 0.05em;
|
|
479
495
|
color: var(--color-accent, currentColor);
|
|
480
496
|
animation: blink-wc-blink var(--blink-rate, 1s) step-end infinite;
|
package/dist/blink-wc.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
var L = Object.defineProperty;
|
|
2
|
+
var R = (r, t, e) => t in r ? L(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
|
|
3
|
+
var C = (r, t, e) => R(r, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
const P = [
|
|
2
5
|
"wave",
|
|
3
6
|
"twinkle",
|
|
4
7
|
"sparkle",
|
|
@@ -13,7 +16,7 @@ const M = [
|
|
|
13
16
|
"shake",
|
|
14
17
|
"heartbeat",
|
|
15
18
|
"decode"
|
|
16
|
-
],
|
|
19
|
+
], N = ["extrude", "collapse", "outline", "morph", "depth", "revolve"], O = { morph: 4, revolve: 4 }, w = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%&*+=?<>/~^|", S = {
|
|
17
20
|
a: ".-",
|
|
18
21
|
b: "-...",
|
|
19
22
|
c: "-.-.",
|
|
@@ -64,35 +67,37 @@ const M = [
|
|
|
64
67
|
"+": ".-.-.",
|
|
65
68
|
"-": "-....-",
|
|
66
69
|
"@": ".--.-."
|
|
67
|
-
},
|
|
70
|
+
}, M = matchMedia("(prefers-reduced-motion: reduce)"), v = /* @__PURE__ */ new Set();
|
|
68
71
|
let y = null;
|
|
69
|
-
const
|
|
70
|
-
function
|
|
71
|
-
v.add(
|
|
72
|
+
const D = () => M.matches || document.documentElement.hasAttribute("data-motion-reduced"), E = () => v.forEach((r) => r._update());
|
|
73
|
+
function W(r) {
|
|
74
|
+
v.add(r), !y && (M.addEventListener("change", E), y = new MutationObserver(E), y.observe(document.documentElement, {
|
|
72
75
|
attributes: !0,
|
|
73
76
|
attributeFilter: ["data-motion-reduced"]
|
|
74
77
|
}));
|
|
75
78
|
}
|
|
76
|
-
function
|
|
77
|
-
v.delete(
|
|
79
|
+
function I(r) {
|
|
80
|
+
v.delete(r), !(v.size || !y) && (M.removeEventListener("change", E), y.disconnect(), y = null);
|
|
78
81
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"unit"
|
|
94
|
-
];
|
|
82
|
+
const k = /* @__PURE__ */ new Map();
|
|
83
|
+
function T(r, t) {
|
|
84
|
+
const e = `${r || ""}|${t}`;
|
|
85
|
+
return k.has(e) || k.set(e, new Intl.Segmenter(r, { granularity: t })), k.get(e);
|
|
86
|
+
}
|
|
87
|
+
const F = (r) => r.trim() === "";
|
|
88
|
+
function q(r, t) {
|
|
89
|
+
return [...T(t, "grapheme").segment(r)].map((e) => e.segment);
|
|
90
|
+
}
|
|
91
|
+
function B(r, t) {
|
|
92
|
+
const e = [];
|
|
93
|
+
for (const { segment: s, isWordLike: a } of T(t, "word").segment(r)) {
|
|
94
|
+
const m = F(s), l = e.length ? e[e.length - 1] : null, c = l != null && F(l);
|
|
95
|
+
l != null && (m && c || !m && !a && !c) ? e[e.length - 1] += s : e.push(s);
|
|
95
96
|
}
|
|
97
|
+
return e;
|
|
98
|
+
}
|
|
99
|
+
const H = "a[href], button, input, select, textarea, summary, [tabindex], [contenteditable]";
|
|
100
|
+
class x extends HTMLElement {
|
|
96
101
|
// ── Functional core: pure getters ──────────────────────────────────────
|
|
97
102
|
get rate() {
|
|
98
103
|
return this.getAttribute("rate") || "1s";
|
|
@@ -110,7 +115,7 @@ class R extends HTMLElement {
|
|
|
110
115
|
}
|
|
111
116
|
get steps() {
|
|
112
117
|
const t = parseInt(this.getAttribute("steps"), 10);
|
|
113
|
-
return Number.isFinite(t) && t >= 2 ? t :
|
|
118
|
+
return Number.isFinite(t) && t >= 2 ? t : O[this.mode] || 2;
|
|
114
119
|
}
|
|
115
120
|
get stepDurations() {
|
|
116
121
|
return this.getAttribute("step-durations") || null;
|
|
@@ -130,15 +135,20 @@ class R extends HTMLElement {
|
|
|
130
135
|
}
|
|
131
136
|
// ── Imperative shell ───────────────────────────────────────────────────
|
|
132
137
|
connectedCallback() {
|
|
133
|
-
if (
|
|
138
|
+
if (W(this), this._built) {
|
|
134
139
|
this._observe(), this._update();
|
|
135
140
|
return;
|
|
136
141
|
}
|
|
137
142
|
this._build(), this._observe(), this._update(), this._built = !0, this._dispatch("blink-start");
|
|
138
143
|
}
|
|
144
|
+
// Moved with Node.moveBefore(): the browser skips disconnected/connected and
|
|
145
|
+
// keeps animation state, so there is nothing to tear down or rebuild. (Where
|
|
146
|
+
// moveBefore() is unsupported, a move falls back to the reconnect path above.)
|
|
147
|
+
connectedMoveCallback() {
|
|
148
|
+
}
|
|
139
149
|
disconnectedCallback() {
|
|
140
150
|
var t, e;
|
|
141
|
-
|
|
151
|
+
I(this), (t = this._intersectionObserver) == null || t.disconnect(), (e = this._content) == null || e.removeEventListener("animationiteration", this._onIteration), document.removeEventListener("visibilitychange", this._onVisibility), this._stopMorse(), this._stopSteps(), this._stopDecode();
|
|
142
152
|
}
|
|
143
153
|
attributeChangedCallback(t, e, s) {
|
|
144
154
|
e === s || !this._built || ((t === "mode" || t === "unit" || t === "step-by" || t === "behavior") && this._renderContent(), this._update(), t === "play-state" && this._dispatch(s === "paused" ? "blink-pause" : "blink-start"));
|
|
@@ -161,7 +171,7 @@ class R extends HTMLElement {
|
|
|
161
171
|
const t = [...this.childNodes];
|
|
162
172
|
this.textContent = "";
|
|
163
173
|
const e = document.createElement("span");
|
|
164
|
-
e.className = "blink-content", t.forEach((s) => e.appendChild(s)), this.appendChild(e), this._content = e, this._sourceHTML = e.innerHTML, this._renderContent();
|
|
174
|
+
e.className = "blink-content", t.forEach((s) => e.appendChild(s)), this.appendChild(e), this._content = e, this._sourceHTML = e.innerHTML, this._sourceText = e.textContent.replace(/\s+/g, " ").trim(), this._renderContent();
|
|
165
175
|
}
|
|
166
176
|
// Restore authored markup, then split into units if the mode needs it. Per-unit
|
|
167
177
|
// motion modes split by `unit`; per-unit step sequences split by `step-by`.
|
|
@@ -169,45 +179,45 @@ class R extends HTMLElement {
|
|
|
169
179
|
if (this._sourceHTML == null) return;
|
|
170
180
|
this._content.innerHTML = this._sourceHTML;
|
|
171
181
|
const t = this._usesSteps() && this.stepBy !== "element";
|
|
172
|
-
if (
|
|
182
|
+
if (P.includes(this.mode) || t) {
|
|
173
183
|
const e = t ? this.stepBy === "word" : this.unit === "word";
|
|
174
184
|
this._splitLetters(e), this.dataset.split = "";
|
|
175
185
|
} else
|
|
176
186
|
delete this.dataset.split;
|
|
187
|
+
this._syncSpeech();
|
|
188
|
+
}
|
|
189
|
+
// Per-unit spans make screen readers spell the text out letter by letter (and
|
|
190
|
+
// decode would expose its scrambled glyphs). When split, hide the animated
|
|
191
|
+
// spans and expose one visually-hidden copy of the original text instead.
|
|
192
|
+
_syncSpeech() {
|
|
193
|
+
var s;
|
|
194
|
+
const t = this.hasAttribute("data-split"), e = t && this._content.querySelector(H);
|
|
195
|
+
t && !e ? (this._speech || (this._speech = document.createElement("span"), this._speech.className = "blink-sr"), this._speech.textContent = this._sourceText, this._content.setAttribute("aria-hidden", "true"), this._speech.isConnected || this._content.after(this._speech)) : (this._content.removeAttribute("aria-hidden"), (s = this._speech) == null || s.remove());
|
|
177
196
|
}
|
|
178
197
|
// Wrap each unit in a <span class="blink-char"> with a stagger index, so CSS
|
|
179
198
|
// can animate units individually (wave, twinkle, …) and the step engine can
|
|
180
199
|
// advance each unit's data-step on its own staggered clock.
|
|
181
200
|
_splitLetters(t = this.unit === "word") {
|
|
201
|
+
var h;
|
|
182
202
|
const e = document.createTreeWalker(this._content, NodeFilter.SHOW_TEXT), s = [];
|
|
183
203
|
for (; e.nextNode(); ) s.push(e.currentNode);
|
|
184
|
-
const
|
|
185
|
-
let
|
|
186
|
-
for (const
|
|
187
|
-
const
|
|
188
|
-
for (const
|
|
189
|
-
if (
|
|
190
|
-
const
|
|
191
|
-
|
|
204
|
+
const a = this.mode === "twinkle" || this.mode === "sparkle", m = this.mode === "decode", l = ((h = this.closest("[lang]")) == null ? void 0 : h.lang) || void 0;
|
|
205
|
+
let c = 0;
|
|
206
|
+
for (const _ of s) {
|
|
207
|
+
const u = document.createDocumentFragment(), d = t ? B(_.textContent, l) : q(_.textContent, l);
|
|
208
|
+
for (const p of d) {
|
|
209
|
+
if (p === "") continue;
|
|
210
|
+
const f = F(p), i = document.createElement("span");
|
|
211
|
+
i.className = "blink-char", i.style.setProperty("--i", c++), a && (i.style.setProperty("--rate", `${(0.4 + Math.random() * 1.6).toFixed(2)}s`), i.style.setProperty("--delay", `${Math.random().toFixed(2)}s`)), f ? (i.classList.add("blink-space"), i.textContent = " ") : (i.textContent = p, m && (i.dataset.ch = p)), u.appendChild(i);
|
|
192
212
|
}
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
this._content.style.setProperty("--n", p);
|
|
196
|
-
}
|
|
197
|
-
// Split text into word and whitespace-run tokens (no regex escapes needed).
|
|
198
|
-
_tokenizeWords(t) {
|
|
199
|
-
const e = [];
|
|
200
|
-
let s = "", r = null;
|
|
201
|
-
for (const u of t) {
|
|
202
|
-
const d = u.trim() === "";
|
|
203
|
-
s !== "" && d !== r && (e.push(s), s = ""), s += u, r = d;
|
|
213
|
+
_.replaceWith(u);
|
|
204
214
|
}
|
|
205
|
-
|
|
215
|
+
this._content.style.setProperty("--n", c);
|
|
206
216
|
}
|
|
207
217
|
// Whether motion should be suppressed: OS preference or VB's data-motion-reduced,
|
|
208
218
|
// unless this instance opts out with reduced-motion="ignore".
|
|
209
219
|
_reduceMotion() {
|
|
210
|
-
return this.getAttribute("reduced-motion") !== "ignore" &&
|
|
220
|
+
return this.getAttribute("reduced-motion") !== "ignore" && D();
|
|
211
221
|
}
|
|
212
222
|
// Whether playback is currently paused (state, off-screen, tab-hidden, or hover).
|
|
213
223
|
_isPaused() {
|
|
@@ -219,12 +229,12 @@ class R extends HTMLElement {
|
|
|
219
229
|
// morse mode: blink the text out as Morse code. CSS keys off data-lit to light
|
|
220
230
|
// the text; we also expose the dot/dash string via data-morse for a caption.
|
|
221
231
|
_syncMorse() {
|
|
222
|
-
var
|
|
232
|
+
var u;
|
|
223
233
|
if (this._stopMorse(), this.mode !== "morse") {
|
|
224
234
|
delete this.dataset.lit, delete this.dataset.morse;
|
|
225
235
|
return;
|
|
226
236
|
}
|
|
227
|
-
const t = (((
|
|
237
|
+
const t = (((u = this._content) == null ? void 0 : u.textContent) || "").trim();
|
|
228
238
|
if (this.dataset.morse = this._toMorse(t), this._reduceMotion()) {
|
|
229
239
|
this.dataset.lit = "true";
|
|
230
240
|
return;
|
|
@@ -234,38 +244,38 @@ class R extends HTMLElement {
|
|
|
234
244
|
this.dataset.lit = "true";
|
|
235
245
|
return;
|
|
236
246
|
}
|
|
237
|
-
const
|
|
238
|
-
let
|
|
239
|
-
const _ = (
|
|
240
|
-
this._morseRAF = requestAnimationFrame(_),
|
|
241
|
-
const
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
let
|
|
246
|
-
for (const
|
|
247
|
-
if (
|
|
248
|
-
|
|
247
|
+
const a = parseFloat(getComputedStyle(this).getPropertyValue("--blink-dot")), m = Number.isFinite(a) && a > 0 ? a : 170, l = s.reduce((d, p) => d + p.dur, 0) + 7;
|
|
248
|
+
let c = 0, h = null;
|
|
249
|
+
const _ = (d) => {
|
|
250
|
+
this._morseRAF = requestAnimationFrame(_), h == null && (h = d);
|
|
251
|
+
const p = d - h;
|
|
252
|
+
if (h = d, this._isPaused()) return;
|
|
253
|
+
c += p;
|
|
254
|
+
const f = c / m % l;
|
|
255
|
+
let i = 0, b = !1;
|
|
256
|
+
for (const o of s) {
|
|
257
|
+
if (f < i + o.dur) {
|
|
258
|
+
b = o.lit;
|
|
249
259
|
break;
|
|
250
260
|
}
|
|
251
|
-
|
|
261
|
+
i += o.dur;
|
|
252
262
|
}
|
|
253
|
-
const
|
|
254
|
-
this.dataset.lit !==
|
|
263
|
+
const n = b ? "true" : "false";
|
|
264
|
+
this.dataset.lit !== n && (this.dataset.lit = n);
|
|
255
265
|
};
|
|
256
266
|
this.dataset.lit = "false", this._morseRAF = requestAnimationFrame(_);
|
|
257
267
|
}
|
|
258
268
|
// Build an on/off timeline (durations in Morse "units") for a string.
|
|
259
269
|
_morseSegments(t) {
|
|
260
270
|
const e = [], s = t.toLowerCase().split(/\s+/).filter(Boolean);
|
|
261
|
-
return s.forEach((
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
const _ = S[
|
|
265
|
-
[..._].forEach((
|
|
266
|
-
e.push({ lit: !0, dur:
|
|
267
|
-
}),
|
|
268
|
-
}),
|
|
271
|
+
return s.forEach((a, m) => {
|
|
272
|
+
const l = [...a].filter((c) => S[c]);
|
|
273
|
+
l.forEach((c, h) => {
|
|
274
|
+
const _ = S[c];
|
|
275
|
+
[..._].forEach((u, d) => {
|
|
276
|
+
e.push({ lit: !0, dur: u === "-" ? 3 : 1 }), d < _.length - 1 && e.push({ lit: !1, dur: 1 });
|
|
277
|
+
}), h < l.length - 1 && e.push({ lit: !1, dur: 3 });
|
|
278
|
+
}), m < s.length - 1 && e.push({ lit: !1, dur: 7 });
|
|
269
279
|
}), e;
|
|
270
280
|
}
|
|
271
281
|
// Human-readable dot/dash string, words separated by " / ".
|
|
@@ -297,36 +307,36 @@ class R extends HTMLElement {
|
|
|
297
307
|
_syncDecode() {
|
|
298
308
|
if (this._stopDecode(), this.mode !== "decode") return;
|
|
299
309
|
const t = [...this.querySelectorAll(".blink-char")].filter(
|
|
300
|
-
(
|
|
310
|
+
(i) => !i.classList.contains("blink-space") && i.dataset.ch != null
|
|
301
311
|
);
|
|
302
312
|
if (t.length === 0) return;
|
|
303
313
|
if (this._reduceMotion()) {
|
|
304
|
-
for (const
|
|
314
|
+
for (const i of t) i.textContent = i.dataset.ch;
|
|
305
315
|
return;
|
|
306
316
|
}
|
|
307
|
-
const s = () =>
|
|
308
|
-
let
|
|
309
|
-
const _ = t.map((
|
|
310
|
-
const
|
|
311
|
-
return
|
|
317
|
+
const s = () => w[Math.random() * w.length | 0], a = 650, m = 45, l = 1800, c = 55;
|
|
318
|
+
let h = 0;
|
|
319
|
+
const _ = t.map((i) => {
|
|
320
|
+
const b = Number(i.style.getPropertyValue("--i")) || 0, n = a + b * m;
|
|
321
|
+
return n > h && (h = n), { s: i, settleAt: n, target: i.dataset.ch };
|
|
312
322
|
});
|
|
313
|
-
let
|
|
314
|
-
const
|
|
315
|
-
this._decodeRAF = requestAnimationFrame(
|
|
316
|
-
const
|
|
317
|
-
if (
|
|
318
|
-
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
for (const
|
|
322
|
-
|
|
323
|
-
|
|
323
|
+
let u = 0, d = null, p = 0;
|
|
324
|
+
const f = (i) => {
|
|
325
|
+
this._decodeRAF = requestAnimationFrame(f), d == null && (d = i);
|
|
326
|
+
const b = i - d;
|
|
327
|
+
if (d = i, this._isPaused()) return;
|
|
328
|
+
u += b;
|
|
329
|
+
const n = u - p >= c;
|
|
330
|
+
n && (p = u);
|
|
331
|
+
for (const o of _)
|
|
332
|
+
u >= o.settleAt ? o.s.textContent !== o.target && (o.s.textContent = o.target) : n && (o.s.textContent = s());
|
|
333
|
+
u > h + l && (u = 0, p = 0);
|
|
324
334
|
};
|
|
325
|
-
this._decodeRAF = requestAnimationFrame(
|
|
335
|
+
this._decodeRAF = requestAnimationFrame(f);
|
|
326
336
|
}
|
|
327
337
|
// Whether the multi-step engine should drive this element.
|
|
328
338
|
_usesSteps() {
|
|
329
|
-
return this.behavior === "steps" ||
|
|
339
|
+
return this.behavior === "steps" || N.includes(this.mode);
|
|
330
340
|
}
|
|
331
341
|
// Parse a CSS time (e.g. "1s", "800ms", or a bare number of seconds) to ms.
|
|
332
342
|
_parseTime(t) {
|
|
@@ -343,8 +353,8 @@ class R extends HTMLElement {
|
|
|
343
353
|
_stepWeights(t) {
|
|
344
354
|
const e = this.stepDurations;
|
|
345
355
|
if (!e) return Array(t).fill(1);
|
|
346
|
-
const s = e.split(/[\s,]+/).map(Number).filter((
|
|
347
|
-
return s.length === 0 ? Array(t).fill(1) : Array.from({ length: t }, (
|
|
356
|
+
const s = e.split(/[\s,]+/).map(Number).filter((a) => Number.isFinite(a) && a > 0);
|
|
357
|
+
return s.length === 0 ? Array(t).fill(1) : Array.from({ length: t }, (a, m) => s[m % s.length]);
|
|
348
358
|
}
|
|
349
359
|
// The element(s) that carry data-step: the whole content for step-by="element",
|
|
350
360
|
// or each unit span for step-by="letter"/"word".
|
|
@@ -373,39 +383,53 @@ class R extends HTMLElement {
|
|
|
373
383
|
const t = this.steps, e = this._stepTargets();
|
|
374
384
|
if (e.length === 0) return;
|
|
375
385
|
if (this._reduceMotion()) {
|
|
376
|
-
for (const
|
|
386
|
+
for (const n of e) n.dataset.step = "0";
|
|
377
387
|
return;
|
|
378
388
|
}
|
|
379
|
-
const
|
|
380
|
-
let
|
|
381
|
-
for (const
|
|
382
|
-
|
|
383
|
-
const
|
|
384
|
-
let
|
|
385
|
-
for (;
|
|
386
|
-
return
|
|
387
|
-
},
|
|
388
|
-
e.forEach((
|
|
389
|
-
let
|
|
390
|
-
const
|
|
391
|
-
this._stepRAF = requestAnimationFrame(
|
|
392
|
-
const
|
|
393
|
-
if (
|
|
394
|
-
|
|
395
|
-
for (let
|
|
396
|
-
const A =
|
|
397
|
-
A !==
|
|
389
|
+
const a = this._stepWeights(t), m = a.reduce((n, o) => n + o, 0), l = this._parseTime(this.rate), c = [];
|
|
390
|
+
let h = 0;
|
|
391
|
+
for (const n of a)
|
|
392
|
+
h += n, c.push(h / m * l);
|
|
393
|
+
const u = e.length > 1 ? (parseFloat(getComputedStyle(this).getPropertyValue("--blink-stagger")) || 0.08) * 1e3 : 0, d = (n) => {
|
|
394
|
+
let o = 0;
|
|
395
|
+
for (; o < t - 1 && n >= c[o]; ) o++;
|
|
396
|
+
return o;
|
|
397
|
+
}, p = new Array(e.length).fill(0);
|
|
398
|
+
e.forEach((n) => n.dataset.step = "0");
|
|
399
|
+
let f = 0, i = null;
|
|
400
|
+
const b = (n) => {
|
|
401
|
+
this._stepRAF = requestAnimationFrame(b), i == null && (i = n);
|
|
402
|
+
const o = n - i;
|
|
403
|
+
if (i = n, !this._isPaused()) {
|
|
404
|
+
f += o;
|
|
405
|
+
for (let g = 0; g < e.length; g++) {
|
|
406
|
+
const A = d((f + g * u) % l);
|
|
407
|
+
A !== p[g] && (e[g].dataset.step = String(A), g === 0 && A < p[g] && this._dispatch("blink-cycle"), p[g] = A);
|
|
398
408
|
}
|
|
399
409
|
}
|
|
400
410
|
};
|
|
401
|
-
this._stepRAF = requestAnimationFrame(
|
|
411
|
+
this._stepRAF = requestAnimationFrame(b);
|
|
402
412
|
}
|
|
403
413
|
_dispatch(t) {
|
|
404
414
|
this.dispatchEvent(new CustomEvent(t, { bubbles: !0 }));
|
|
405
415
|
}
|
|
406
416
|
}
|
|
407
|
-
|
|
417
|
+
C(x, "observedAttributes", [
|
|
418
|
+
"rate",
|
|
419
|
+
"behavior",
|
|
420
|
+
"min-opacity",
|
|
421
|
+
"count",
|
|
422
|
+
"steps",
|
|
423
|
+
"step-durations",
|
|
424
|
+
"step-by",
|
|
425
|
+
"play-state",
|
|
426
|
+
"pause-on-hover",
|
|
427
|
+
"reduced-motion",
|
|
428
|
+
"mode",
|
|
429
|
+
"unit"
|
|
430
|
+
]);
|
|
431
|
+
customElements.define("blink-wc", x);
|
|
408
432
|
export {
|
|
409
|
-
|
|
433
|
+
x as BlinkWc
|
|
410
434
|
};
|
|
411
435
|
//# sourceMappingURL=blink-wc.js.map
|
package/dist/blink-wc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blink-wc.js","sources":["../src/blink-wc.js"],"sourcesContent":["/**\n * @class BlinkWc\n * @extends HTMLElement\n * @description Vanilla Breeze blink component. The deprecated <blink> element,\n * reborn for 2026 — light DOM, CSS-driven, themeable, accessible, and it\n * pauses when offscreen or tab-hidden. Blinking content is a real\n * accessibility hazard, so it stops entirely under prefers-reduced-motion\n * (unless you explicitly opt out) and is always pausable.\n *\n * @attr rate - CSS time for one blink cycle (default: 1s)\n * @attr behavior - blink | pulse | flicker (default: blink)\n * @attr min-opacity - opacity of the \"off\" phase, 0–1 (default per behavior)\n * @attr count - number of blinks, then stop, or `infinite` (default: infinite)\n * @attr pause-on-hover - boolean; also pauses on focus-within\n * @attr play-state - running | paused\n * @attr reduced-motion - respect | ignore (default: respect)\n * @attr mode - visual/motion preset. Surface themes: neon | crt | terminal |\n * warning | alert | police | vegas. Per-unit motion: wave | twinkle |\n * sparkle | rainbow | chase | glitch | cascade | typewriter. Special: morse.\n * @attr unit - letter | word (default: letter) — granularity for the motion modes\n *\n * @fires blink-start\n * @fires blink-pause\n * @fires blink-cycle - fires on each blink iteration\n */\n\n// Modes that split text into per-unit spans for letter/word-level effects.\nconst LETTER_MODES = [\n 'wave',\n 'twinkle',\n 'sparkle',\n 'rainbow',\n 'chase',\n 'glitch',\n 'cascade',\n 'typewriter',\n 'zoom',\n 'flip3d',\n 'swing',\n 'shake',\n 'heartbeat',\n 'decode',\n];\n\n// Modes driven by the multi-step engine: JS advances data-step=\"0..N-1\" across\n// the cycle and CSS styles each step. A plain blink is just the 2-step case.\nconst STEP_MODES = ['extrude', 'collapse', 'outline', 'morph', 'depth', 'revolve'];\n\n// Default step count per step-mode (overridable with the `steps` attribute).\nconst STEP_COUNTS = { morph: 4, revolve: 4 };\n\n// Glyphs the `decode` scramble cycles through before locking onto the text.\nconst DECODE_GLYPHS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%&*+=?<>/~^|';\n\n// International Morse code, plus digits and a little punctuation.\nconst MORSE = {\n a: '.-',\n b: '-...',\n c: '-.-.',\n d: '-..',\n e: '.',\n f: '..-.',\n g: '--.',\n h: '....',\n i: '..',\n j: '.---',\n k: '-.-',\n l: '.-..',\n m: '--',\n n: '-.',\n o: '---',\n p: '.--.',\n q: '--.-',\n r: '.-.',\n s: '...',\n t: '-',\n u: '..-',\n v: '...-',\n w: '.--',\n x: '-..-',\n y: '-.--',\n z: '--..',\n 0: '-----',\n 1: '.----',\n 2: '..---',\n 3: '...--',\n 4: '....-',\n 5: '.....',\n 6: '-....',\n 7: '--...',\n 8: '---..',\n 9: '----.',\n '.': '.-.-.-',\n ',': '--..--',\n '?': '..--..',\n \"'\": '.----.',\n '!': '-.-.--',\n '/': '-..-.',\n '(': '-.--.',\n ')': '-.--.-',\n '&': '.-...',\n ':': '---...',\n '=': '-...-',\n '+': '.-.-.',\n '-': '-....-',\n '@': '.--.-.',\n};\n\n// ── Reduced-motion watcher (shared by every instance) ──────────────────────\n// Motion is reduced when the OS asks for it OR when Vanilla Breeze's theme\n// picker has turned \"Motion Effects\" off, which sets :root[data-motion-reduced].\n// One media-query listener and one MutationObserver serve all instances, and\n// re-sync the JS-driven modes (morse, steps, decode) when either flips.\nconst REDUCED_MQ = matchMedia('(prefers-reduced-motion: reduce)');\nconst INSTANCES = new Set();\nlet rootObserver = null;\n\nconst systemReducesMotion = () =>\n REDUCED_MQ.matches || document.documentElement.hasAttribute('data-motion-reduced');\n\nconst resyncAll = () => INSTANCES.forEach((el) => el._update());\n\nfunction watchMotion(el) {\n INSTANCES.add(el);\n if (rootObserver) return;\n REDUCED_MQ.addEventListener('change', resyncAll);\n rootObserver = new MutationObserver(resyncAll);\n rootObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-motion-reduced'],\n });\n}\n\nfunction unwatchMotion(el) {\n INSTANCES.delete(el);\n if (INSTANCES.size || !rootObserver) return;\n REDUCED_MQ.removeEventListener('change', resyncAll);\n rootObserver.disconnect();\n rootObserver = null;\n}\n\nclass BlinkWc extends HTMLElement {\n static get observedAttributes() {\n return [\n 'rate',\n 'behavior',\n 'min-opacity',\n 'count',\n 'steps',\n 'step-durations',\n 'step-by',\n 'play-state',\n 'pause-on-hover',\n 'reduced-motion',\n 'mode',\n 'unit',\n ];\n }\n\n // ── Functional core: pure getters ──────────────────────────────────────\n get rate() {\n return this.getAttribute('rate') || '1s';\n }\n get behavior() {\n const b = this.getAttribute('behavior');\n return ['pulse', 'flicker', 'steps'].includes(b) ? b : 'blink';\n }\n get minOpacity() {\n const v = this.getAttribute('min-opacity');\n return v == null || v === '' ? null : v;\n }\n get count() {\n return this.getAttribute('count') || 'infinite';\n }\n get steps() {\n const v = parseInt(this.getAttribute('steps'), 10);\n if (Number.isFinite(v) && v >= 2) return v;\n return STEP_COUNTS[this.mode] || 2;\n }\n get stepDurations() {\n return this.getAttribute('step-durations') || null;\n }\n get stepBy() {\n const v = this.getAttribute('step-by');\n return v === 'letter' || v === 'word' ? v : 'element';\n }\n get playState() {\n return this.getAttribute('play-state') || 'running';\n }\n get mode() {\n return this.getAttribute('mode') || '';\n }\n get unit() {\n return this.getAttribute('unit') === 'word' ? 'word' : 'letter';\n }\n\n // ── Imperative shell ───────────────────────────────────────────────────\n connectedCallback() {\n watchMotion(this);\n if (this._built) {\n // Re-attached (e.g. moved in the DOM): disconnectedCallback tore down the\n // observers and stopped the JS drivers, so bring them back.\n this._observe();\n this._update();\n return;\n }\n this._build();\n this._observe();\n this._update();\n this._built = true;\n this._dispatch('blink-start');\n }\n\n disconnectedCallback() {\n unwatchMotion(this);\n this._intersectionObserver?.disconnect();\n this._content?.removeEventListener('animationiteration', this._onIteration);\n document.removeEventListener('visibilitychange', this._onVisibility);\n this._stopMorse();\n this._stopSteps();\n this._stopDecode();\n }\n\n attributeChangedCallback(name, oldValue, newValue) {\n if (oldValue === newValue || !this._built) return;\n if (name === 'mode' || name === 'unit' || name === 'step-by' || name === 'behavior') {\n this._renderContent();\n }\n this._update();\n if (name === 'play-state') {\n this._dispatch(newValue === 'paused' ? 'blink-pause' : 'blink-start');\n }\n }\n\n // ── Public API (legacy <blink> compat) ──────────────────────────────────\n start() {\n this.setAttribute('play-state', 'running');\n }\n stop() {\n this.setAttribute('play-state', 'paused');\n }\n toggle() {\n this.playState === 'paused' ? this.start() : this.stop();\n }\n refresh() {\n this._renderContent();\n this._update();\n }\n\n // ── Private ────────────────────────────────────────────────────────────\n _build() {\n const items = [...this.childNodes];\n this.textContent = '';\n\n const content = document.createElement('span');\n content.className = 'blink-content';\n items.forEach((n) => content.appendChild(n));\n this.appendChild(content);\n\n this._content = content;\n // Snapshot the authored markup so we can re-render when `mode` changes.\n this._sourceHTML = content.innerHTML;\n this._renderContent();\n }\n\n // Restore authored markup, then split into units if the mode needs it. Per-unit\n // motion modes split by `unit`; per-unit step sequences split by `step-by`.\n _renderContent() {\n if (this._sourceHTML == null) return;\n this._content.innerHTML = this._sourceHTML;\n const perUnitSteps = this._usesSteps() && this.stepBy !== 'element';\n if (LETTER_MODES.includes(this.mode) || perUnitSteps) {\n const byWord = perUnitSteps ? this.stepBy === 'word' : this.unit === 'word';\n this._splitLetters(byWord);\n this.dataset.split = '';\n } else {\n delete this.dataset.split;\n }\n }\n\n // Wrap each unit in a <span class=\"blink-char\"> with a stagger index, so CSS\n // can animate units individually (wave, twinkle, …) and the step engine can\n // advance each unit's data-step on its own staggered clock.\n _splitLetters(byWord = this.unit === 'word') {\n const walker = document.createTreeWalker(this._content, NodeFilter.SHOW_TEXT);\n const textNodes = [];\n while (walker.nextNode()) textNodes.push(walker.currentNode);\n\n // Per-unit randomized clocks: each unit blinks on its own timer.\n const twinkle = this.mode === 'twinkle' || this.mode === 'sparkle';\n const decode = this.mode === 'decode';\n const isWs = (c) => c.trim() === '';\n let i = 0;\n for (const node of textNodes) {\n const frag = document.createDocumentFragment();\n const tokens = byWord ? this._tokenizeWords(node.textContent) : [...node.textContent];\n for (const ch of tokens) {\n if (ch === '') continue;\n const space = isWs(ch);\n const span = document.createElement('span');\n span.className = 'blink-char';\n span.style.setProperty('--i', i++);\n if (twinkle) {\n span.style.setProperty('--rate', `${(0.4 + Math.random() * 1.6).toFixed(2)}s`);\n span.style.setProperty('--delay', `${Math.random().toFixed(2)}s`);\n }\n if (space) {\n span.classList.add('blink-space');\n span.textContent = ' ';\n } else {\n span.textContent = ch;\n // decode: remember the real glyph so the scramble loop can settle on it\n if (decode) span.dataset.ch = ch;\n }\n frag.appendChild(span);\n }\n node.replaceWith(frag);\n }\n this._content.style.setProperty('--n', i);\n }\n\n // Split text into word and whitespace-run tokens (no regex escapes needed).\n _tokenizeWords(text) {\n const tokens = [];\n let buf = '';\n let bufWs = null;\n for (const c of text) {\n const ws = c.trim() === '';\n if (buf !== '' && ws !== bufWs) {\n tokens.push(buf);\n buf = '';\n }\n buf += c;\n bufWs = ws;\n }\n if (buf !== '') tokens.push(buf);\n return tokens;\n }\n\n // Whether motion should be suppressed: OS preference or VB's data-motion-reduced,\n // unless this instance opts out with reduced-motion=\"ignore\".\n _reduceMotion() {\n return this.getAttribute('reduced-motion') !== 'ignore' && systemReducesMotion();\n }\n\n // Whether playback is currently paused (state, off-screen, tab-hidden, or hover).\n _isPaused() {\n if (this.hasAttribute('pause-on-hover') && this.matches(':hover, :focus-within')) return true;\n return (\n this.playState === 'paused' ||\n this.dataset.visible === 'false' ||\n this.dataset.tabVisible === 'false'\n );\n }\n\n _stopMorse() {\n if (this._morseRAF) cancelAnimationFrame(this._morseRAF);\n this._morseRAF = null;\n }\n\n // morse mode: blink the text out as Morse code. CSS keys off data-lit to light\n // the text; we also expose the dot/dash string via data-morse for a caption.\n _syncMorse() {\n this._stopMorse();\n if (this.mode !== 'morse') {\n delete this.dataset.lit;\n delete this.dataset.morse;\n return;\n }\n\n const text = (this._content?.textContent || '').trim();\n this.dataset.morse = this._toMorse(text);\n\n // Respect reduced motion: leave the text fully lit, no blinking.\n const reduce = this._reduceMotion();\n if (reduce) {\n this.dataset.lit = 'true';\n return;\n }\n\n const segments = this._morseSegments(text);\n if (segments.length === 0) {\n this.dataset.lit = 'true';\n return;\n }\n\n // Read the dot unit (ms) from a CSS var so it can be themed; default 170ms.\n const dotRaw = parseFloat(getComputedStyle(this).getPropertyValue('--blink-dot'));\n const DOT = Number.isFinite(dotRaw) && dotRaw > 0 ? dotRaw : 170;\n const totalUnits = segments.reduce((a, s) => a + s.dur, 0) + 7; // trailing word gap\n\n let elapsed = 0;\n let lastT = null;\n const tick = (t) => {\n this._morseRAF = requestAnimationFrame(tick);\n if (lastT == null) lastT = t;\n const dt = t - lastT;\n lastT = t;\n if (this._isPaused()) return; // freeze the timeline while paused\n elapsed += dt;\n const cycle = (elapsed / DOT) % totalUnits;\n let acc = 0;\n let lit = false;\n for (const s of segments) {\n if (cycle < acc + s.dur) {\n lit = s.lit;\n break;\n }\n acc += s.dur;\n }\n const next = lit ? 'true' : 'false';\n if (this.dataset.lit !== next) this.dataset.lit = next;\n };\n this.dataset.lit = 'false';\n this._morseRAF = requestAnimationFrame(tick);\n }\n\n // Build an on/off timeline (durations in Morse \"units\") for a string.\n _morseSegments(text) {\n const segments = [];\n const words = text.toLowerCase().split(/\\s+/).filter(Boolean);\n words.forEach((word, wi) => {\n const chars = [...word].filter((c) => MORSE[c]);\n chars.forEach((c, ci) => {\n const symbols = MORSE[c];\n [...symbols].forEach((sym, si) => {\n segments.push({ lit: true, dur: sym === '-' ? 3 : 1 });\n if (si < symbols.length - 1) segments.push({ lit: false, dur: 1 });\n });\n if (ci < chars.length - 1) segments.push({ lit: false, dur: 3 }); // inter-char gap\n });\n if (wi < words.length - 1) segments.push({ lit: false, dur: 7 }); // inter-word gap\n });\n return segments;\n }\n\n // Human-readable dot/dash string, words separated by \" / \".\n _toMorse(text) {\n return text\n .toLowerCase()\n .split(/\\s+/)\n .filter(Boolean)\n .map((word) =>\n [...word]\n .map((c) => MORSE[c] || '')\n .filter(Boolean)\n .join(' ')\n )\n .filter(Boolean)\n .join(' / ');\n }\n\n _observe() {\n this._intersectionObserver = new IntersectionObserver(\n (entries) =>\n entries.forEach((e) => {\n this.dataset.visible = String(e.isIntersecting);\n }),\n { rootMargin: '100px' }\n );\n this._intersectionObserver.observe(this);\n\n this._onVisibility = () => {\n this.dataset.tabVisible = String(!document.hidden);\n };\n document.addEventListener('visibilitychange', this._onVisibility);\n this.dataset.tabVisible = String(!document.hidden);\n\n this._onIteration = () => this._dispatch('blink-cycle');\n this._content.addEventListener('animationiteration', this._onIteration);\n }\n\n _update() {\n if (!this._built && !this._content) return;\n\n // Reflect state to data-* hooks the CSS keys off.\n this.dataset.behavior = this.behavior;\n this.dataset.state = this.playState;\n this.dataset.ready = '';\n\n this.style.setProperty('--blink-rate', this.rate);\n if (this.minOpacity != null) {\n this.style.setProperty('--blink-min-opacity', this.minOpacity);\n } else {\n this.style.removeProperty('--blink-min-opacity');\n }\n if (this.count !== 'infinite') {\n this.style.setProperty('--blink-count', this.count);\n } else {\n this.style.removeProperty('--blink-count');\n }\n\n this._syncMorse();\n this._syncSteps();\n this._syncDecode();\n }\n\n _stopDecode() {\n if (this._decodeRAF) cancelAnimationFrame(this._decodeRAF);\n this._decodeRAF = null;\n }\n\n // decode mode: each unit flickers through random glyphs, then locks onto its\n // real character (stored in data-ch), staggered left-to-right, then\n // re-scrambles on a loop. Monospace keeps the width stable as glyphs change.\n // Freezes when paused; shows the plain text under reduced motion.\n _syncDecode() {\n this._stopDecode();\n if (this.mode !== 'decode') return;\n\n const units = [...this.querySelectorAll('.blink-char')].filter(\n (s) => !s.classList.contains('blink-space') && s.dataset.ch != null\n );\n if (units.length === 0) return;\n\n const reduce = this._reduceMotion();\n if (reduce) {\n for (const s of units) s.textContent = s.dataset.ch;\n return;\n }\n\n const randGlyph = () => DECODE_GLYPHS[(Math.random() * DECODE_GLYPHS.length) | 0];\n\n const SCRAMBLE_MS = 650;\n const STAGGER_MS = 45;\n const HOLD_MS = 1800;\n const FLIP_MS = 55;\n let maxSettle = 0;\n const items = units.map((s) => {\n const i = Number(s.style.getPropertyValue('--i')) || 0;\n const settleAt = SCRAMBLE_MS + i * STAGGER_MS;\n if (settleAt > maxSettle) maxSettle = settleAt;\n return { s, settleAt, target: s.dataset.ch };\n });\n\n let elapsed = 0;\n let lastT = null;\n let lastFlip = 0;\n const tick = (t) => {\n this._decodeRAF = requestAnimationFrame(tick);\n if (lastT == null) lastT = t;\n const dt = t - lastT;\n lastT = t;\n if (this._isPaused()) return; // freeze the timeline while paused\n elapsed += dt;\n const flip = elapsed - lastFlip >= FLIP_MS;\n if (flip) lastFlip = elapsed;\n for (const it of items) {\n if (elapsed >= it.settleAt) {\n if (it.s.textContent !== it.target) it.s.textContent = it.target;\n } else if (flip) {\n it.s.textContent = randGlyph();\n }\n }\n if (elapsed > maxSettle + HOLD_MS) {\n elapsed = 0;\n lastFlip = 0;\n }\n };\n this._decodeRAF = requestAnimationFrame(tick);\n }\n\n // Whether the multi-step engine should drive this element.\n _usesSteps() {\n return this.behavior === 'steps' || STEP_MODES.includes(this.mode);\n }\n\n // Parse a CSS time (e.g. \"1s\", \"800ms\", or a bare number of seconds) to ms.\n _parseTime(value) {\n const t = String(value).trim();\n let ms;\n if (t.endsWith('ms')) ms = parseFloat(t);\n else if (t.endsWith('s')) ms = parseFloat(t) * 1000;\n else ms = parseFloat(t) * 1000;\n return Number.isFinite(ms) && ms > 0 ? ms : 1000;\n }\n\n _stopSteps() {\n if (this._stepRAF) cancelAnimationFrame(this._stepRAF);\n this._stepRAF = null;\n }\n\n // Parse `step-durations` into per-step weights, one per step. Accepts a\n // space- or comma-separated list (e.g. \"3 1 1\" or \"2,1\"); the list is cycled\n // or truncated to fit the step count. Absent/invalid → equal weights.\n _stepWeights(n) {\n const raw = this.stepDurations;\n if (!raw) return Array(n).fill(1);\n const parts = raw\n .split(/[\\s,]+/)\n .map(Number)\n .filter((v) => Number.isFinite(v) && v > 0);\n if (parts.length === 0) return Array(n).fill(1);\n return Array.from({ length: n }, (_, i) => parts[i % parts.length]);\n }\n\n // The element(s) that carry data-step: the whole content for step-by=\"element\",\n // or each unit span for step-by=\"letter\"/\"word\".\n _stepTargets() {\n if (this.stepBy !== 'element' && this.dataset.split != null) {\n return [...this.querySelectorAll('.blink-char')];\n }\n return this._content ? [this._content] : [];\n }\n\n // Clear any data-step left on the content or unit spans (when stepping stops).\n _clearStepTargets() {\n delete this._content?.dataset.step;\n this.querySelectorAll('.blink-char[data-step]').forEach((el) => delete el.dataset.step);\n }\n\n // Multi-step engine: advance data-step=\"0..N-1\" across one cycle (`rate`). Each\n // step holds for a slice of the cycle — equal by default, or weighted by\n // `step-durations`. CSS styles each step (colour, transform, outline, …) and\n // can give each step its own transition timing (--blink-step-ease /\n // --blink-step-timing); a plain blink is the 2-step case. With step-by=\"letter\"\n // (or \"word\") every unit runs the sequence offset by its index, so the states\n // ripple across the text. Freezes when paused/off-screen/tab-hidden; rests at\n // step 0 under reduced motion.\n _syncSteps() {\n this._stopSteps();\n if (!this._usesSteps()) {\n delete this.dataset.stepping;\n this._clearStepTargets();\n return;\n }\n this.dataset.stepping = '';\n\n const n = this.steps;\n const targets = this._stepTargets();\n if (targets.length === 0) return;\n\n const reduce = this._reduceMotion();\n if (reduce) {\n for (const el of targets) el.dataset.step = '0';\n return;\n }\n\n // Build cumulative time boundaries (ms) for each step from its weight.\n const weights = this._stepWeights(n);\n const total = weights.reduce((a, b) => a + b, 0);\n const cycleMs = this._parseTime(this.rate);\n const bounds = [];\n let acc = 0;\n for (const w of weights) {\n acc += w;\n bounds.push((acc / total) * cycleMs);\n }\n\n // Per-unit stepping offsets each unit's phase by a stagger so the states\n // travel across the text. Read --blink-stagger (seconds) for the step.\n const perUnit = targets.length > 1;\n const staggerMs = perUnit\n ? (parseFloat(getComputedStyle(this).getPropertyValue('--blink-stagger')) || 0.08) * 1000\n : 0;\n\n const stepAt = (pos) => {\n let idx = 0;\n while (idx < n - 1 && pos >= bounds[idx]) idx++;\n return idx;\n };\n\n const prev = new Array(targets.length).fill(0);\n targets.forEach((el) => (el.dataset.step = '0'));\n let elapsed = 0;\n let lastT = null;\n const tick = (t) => {\n this._stepRAF = requestAnimationFrame(tick);\n if (lastT == null) lastT = t;\n const dt = t - lastT;\n lastT = t;\n if (this._isPaused()) return; // freeze the timeline while paused\n elapsed += dt;\n for (let k = 0; k < targets.length; k++) {\n const idx = stepAt((elapsed + k * staggerMs) % cycleMs);\n if (idx !== prev[k]) {\n targets[k].dataset.step = String(idx);\n if (k === 0 && idx < prev[k]) this._dispatch('blink-cycle'); // wrapped to start\n prev[k] = idx;\n }\n }\n };\n this._stepRAF = requestAnimationFrame(tick);\n }\n\n _dispatch(name) {\n this.dispatchEvent(new CustomEvent(name, { bubbles: true }));\n }\n}\n\ncustomElements.define('blink-wc', BlinkWc);\n\nexport { BlinkWc };\n"],"names":["LETTER_MODES","STEP_MODES","STEP_COUNTS","DECODE_GLYPHS","MORSE","REDUCED_MQ","INSTANCES","rootObserver","systemReducesMotion","resyncAll","el","watchMotion","unwatchMotion","BlinkWc","b","v","_a","_b","name","oldValue","newValue","items","content","n","perUnitSteps","byWord","walker","textNodes","twinkle","decode","isWs","c","i","node","frag","tokens","ch","space","span","text","buf","bufWs","ws","segments","dotRaw","DOT","totalUnits","s","elapsed","lastT","tick","t","dt","cycle","acc","lit","next","words","word","wi","chars","ci","symbols","sym","si","entries","units","randGlyph","SCRAMBLE_MS","STAGGER_MS","HOLD_MS","FLIP_MS","maxSettle","settleAt","lastFlip","flip","it","value","ms","raw","parts","_","targets","weights","total","a","cycleMs","bounds","w","staggerMs","stepAt","pos","idx","prev","k"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"blink-wc.js","sources":["../src/blink-wc.js"],"sourcesContent":["/**\n * @class BlinkWc\n * @extends HTMLElement\n * @description Vanilla Breeze blink component. The deprecated <blink> element,\n * reborn for 2026 — light DOM, CSS-driven, themeable, accessible, and it\n * pauses when offscreen or tab-hidden. Blinking content is a real\n * accessibility hazard, so it stops entirely under prefers-reduced-motion\n * (unless you explicitly opt out) and is always pausable.\n *\n * @attr rate - CSS time for one blink cycle (default: 1s)\n * @attr behavior - blink | pulse | flicker (default: blink)\n * @attr min-opacity - opacity of the \"off\" phase, 0–1 (default per behavior)\n * @attr count - number of blinks, then stop, or `infinite` (default: infinite)\n * @attr pause-on-hover - boolean; also pauses on focus-within\n * @attr play-state - running | paused\n * @attr reduced-motion - respect | ignore (default: respect)\n * @attr mode - visual/motion preset. Surface themes: neon | crt | terminal |\n * warning | alert | police | vegas. Per-unit motion: wave | twinkle |\n * sparkle | rainbow | chase | glitch | cascade | typewriter. Special: morse.\n * @attr unit - letter | word (default: letter) — granularity for the motion modes\n *\n * @fires blink-start\n * @fires blink-pause\n * @fires blink-cycle - fires on each blink iteration\n */\n\n// Modes that split text into per-unit spans for letter/word-level effects.\nconst LETTER_MODES = [\n 'wave',\n 'twinkle',\n 'sparkle',\n 'rainbow',\n 'chase',\n 'glitch',\n 'cascade',\n 'typewriter',\n 'zoom',\n 'flip3d',\n 'swing',\n 'shake',\n 'heartbeat',\n 'decode',\n];\n\n// Modes driven by the multi-step engine: JS advances data-step=\"0..N-1\" across\n// the cycle and CSS styles each step. A plain blink is just the 2-step case.\nconst STEP_MODES = ['extrude', 'collapse', 'outline', 'morph', 'depth', 'revolve'];\n\n// Default step count per step-mode (overridable with the `steps` attribute).\nconst STEP_COUNTS = { morph: 4, revolve: 4 };\n\n// Glyphs the `decode` scramble cycles through before locking onto the text.\nconst DECODE_GLYPHS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%&*+=?<>/~^|';\n\n// International Morse code, plus digits and a little punctuation.\nconst MORSE = {\n a: '.-',\n b: '-...',\n c: '-.-.',\n d: '-..',\n e: '.',\n f: '..-.',\n g: '--.',\n h: '....',\n i: '..',\n j: '.---',\n k: '-.-',\n l: '.-..',\n m: '--',\n n: '-.',\n o: '---',\n p: '.--.',\n q: '--.-',\n r: '.-.',\n s: '...',\n t: '-',\n u: '..-',\n v: '...-',\n w: '.--',\n x: '-..-',\n y: '-.--',\n z: '--..',\n 0: '-----',\n 1: '.----',\n 2: '..---',\n 3: '...--',\n 4: '....-',\n 5: '.....',\n 6: '-....',\n 7: '--...',\n 8: '---..',\n 9: '----.',\n '.': '.-.-.-',\n ',': '--..--',\n '?': '..--..',\n \"'\": '.----.',\n '!': '-.-.--',\n '/': '-..-.',\n '(': '-.--.',\n ')': '-.--.-',\n '&': '.-...',\n ':': '---...',\n '=': '-...-',\n '+': '.-.-.',\n '-': '-....-',\n '@': '.--.-.',\n};\n\n// ── Reduced-motion watcher (shared by every instance) ──────────────────────\n// Motion is reduced when the OS asks for it OR when Vanilla Breeze's theme\n// picker has turned \"Motion Effects\" off, which sets :root[data-motion-reduced].\n// One media-query listener and one MutationObserver serve all instances, and\n// re-sync the JS-driven modes (morse, steps, decode) when either flips.\nconst REDUCED_MQ = matchMedia('(prefers-reduced-motion: reduce)');\nconst INSTANCES = new Set();\nlet rootObserver = null;\n\nconst systemReducesMotion = () =>\n REDUCED_MQ.matches || document.documentElement.hasAttribute('data-motion-reduced');\n\nconst resyncAll = () => INSTANCES.forEach((el) => el._update());\n\nfunction watchMotion(el) {\n INSTANCES.add(el);\n if (rootObserver) return;\n REDUCED_MQ.addEventListener('change', resyncAll);\n rootObserver = new MutationObserver(resyncAll);\n rootObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-motion-reduced'],\n });\n}\n\nfunction unwatchMotion(el) {\n INSTANCES.delete(el);\n if (INSTANCES.size || !rootObserver) return;\n REDUCED_MQ.removeEventListener('change', resyncAll);\n rootObserver.disconnect();\n rootObserver = null;\n}\n\n// ── Text segmentation ──────────────────────────────────────────────────────\n// Split by user-perceived characters (grapheme clusters), not code points, so\n// emoji sequences (👩💻, flags) and combining accents stay whole. Word mode uses\n// the locale's word boundaries, which also handles languages without spaces.\nconst SEGMENTERS = new Map();\nfunction segmenter(locale, granularity) {\n const key = `${locale || ''}|${granularity}`;\n if (!SEGMENTERS.has(key)) SEGMENTERS.set(key, new Intl.Segmenter(locale, { granularity }));\n return SEGMENTERS.get(key);\n}\n\nconst isWhitespace = (s) => s.trim() === '';\n\n// Graphemes, one per unit; whitespace stays one unit per character.\nfunction splitGraphemes(text, locale) {\n return [...segmenter(locale, 'grapheme').segment(text)].map((s) => s.segment);\n}\n\n// Words plus whitespace runs. Trailing punctuation sticks to the word before\n// it, so \"hello, world\" splits as \"hello,\" \" \" \"world\".\nfunction splitWords(text, locale) {\n const tokens = [];\n for (const { segment, isWordLike } of segmenter(locale, 'word').segment(text)) {\n const ws = isWhitespace(segment);\n const prev = tokens.length ? tokens[tokens.length - 1] : null;\n const prevWs = prev != null && isWhitespace(prev);\n if (prev != null && ((ws && prevWs) || (!ws && !isWordLike && !prevWs))) {\n tokens[tokens.length - 1] += segment;\n } else {\n tokens.push(segment);\n }\n }\n return tokens;\n}\n\n// Content a screen-reader-only copy can't stand in for: hiding the animated\n// spans would hide these from assistive tech, so such content is left as is.\nconst INTERACTIVE =\n 'a[href], button, input, select, textarea, summary, [tabindex], [contenteditable]';\n\nclass BlinkWc extends HTMLElement {\n static observedAttributes = [\n 'rate',\n 'behavior',\n 'min-opacity',\n 'count',\n 'steps',\n 'step-durations',\n 'step-by',\n 'play-state',\n 'pause-on-hover',\n 'reduced-motion',\n 'mode',\n 'unit',\n ];\n\n // ── Functional core: pure getters ──────────────────────────────────────\n get rate() {\n return this.getAttribute('rate') || '1s';\n }\n get behavior() {\n const b = this.getAttribute('behavior');\n return ['pulse', 'flicker', 'steps'].includes(b) ? b : 'blink';\n }\n get minOpacity() {\n const v = this.getAttribute('min-opacity');\n return v == null || v === '' ? null : v;\n }\n get count() {\n return this.getAttribute('count') || 'infinite';\n }\n get steps() {\n const v = parseInt(this.getAttribute('steps'), 10);\n if (Number.isFinite(v) && v >= 2) return v;\n return STEP_COUNTS[this.mode] || 2;\n }\n get stepDurations() {\n return this.getAttribute('step-durations') || null;\n }\n get stepBy() {\n const v = this.getAttribute('step-by');\n return v === 'letter' || v === 'word' ? v : 'element';\n }\n get playState() {\n return this.getAttribute('play-state') || 'running';\n }\n get mode() {\n return this.getAttribute('mode') || '';\n }\n get unit() {\n return this.getAttribute('unit') === 'word' ? 'word' : 'letter';\n }\n\n // ── Imperative shell ───────────────────────────────────────────────────\n connectedCallback() {\n watchMotion(this);\n if (this._built) {\n // Re-attached (e.g. moved in the DOM): disconnectedCallback tore down the\n // observers and stopped the JS drivers, so bring them back.\n this._observe();\n this._update();\n return;\n }\n this._build();\n this._observe();\n this._update();\n this._built = true;\n this._dispatch('blink-start');\n }\n\n // Moved with Node.moveBefore(): the browser skips disconnected/connected and\n // keeps animation state, so there is nothing to tear down or rebuild. (Where\n // moveBefore() is unsupported, a move falls back to the reconnect path above.)\n connectedMoveCallback() {}\n\n disconnectedCallback() {\n unwatchMotion(this);\n this._intersectionObserver?.disconnect();\n this._content?.removeEventListener('animationiteration', this._onIteration);\n document.removeEventListener('visibilitychange', this._onVisibility);\n this._stopMorse();\n this._stopSteps();\n this._stopDecode();\n }\n\n attributeChangedCallback(name, oldValue, newValue) {\n if (oldValue === newValue || !this._built) return;\n if (name === 'mode' || name === 'unit' || name === 'step-by' || name === 'behavior') {\n this._renderContent();\n }\n this._update();\n if (name === 'play-state') {\n this._dispatch(newValue === 'paused' ? 'blink-pause' : 'blink-start');\n }\n }\n\n // ── Public API (legacy <blink> compat) ──────────────────────────────────\n start() {\n this.setAttribute('play-state', 'running');\n }\n stop() {\n this.setAttribute('play-state', 'paused');\n }\n toggle() {\n this.playState === 'paused' ? this.start() : this.stop();\n }\n refresh() {\n this._renderContent();\n this._update();\n }\n\n // ── Private ────────────────────────────────────────────────────────────\n _build() {\n const items = [...this.childNodes];\n this.textContent = '';\n\n const content = document.createElement('span');\n content.className = 'blink-content';\n items.forEach((n) => content.appendChild(n));\n this.appendChild(content);\n\n this._content = content;\n // Snapshot the authored markup so we can re-render when `mode` changes.\n this._sourceHTML = content.innerHTML;\n this._sourceText = content.textContent.replace(/\\s+/g, ' ').trim();\n this._renderContent();\n }\n\n // Restore authored markup, then split into units if the mode needs it. Per-unit\n // motion modes split by `unit`; per-unit step sequences split by `step-by`.\n _renderContent() {\n if (this._sourceHTML == null) return;\n this._content.innerHTML = this._sourceHTML;\n const perUnitSteps = this._usesSteps() && this.stepBy !== 'element';\n if (LETTER_MODES.includes(this.mode) || perUnitSteps) {\n const byWord = perUnitSteps ? this.stepBy === 'word' : this.unit === 'word';\n this._splitLetters(byWord);\n this.dataset.split = '';\n } else {\n delete this.dataset.split;\n }\n this._syncSpeech();\n }\n\n // Per-unit spans make screen readers spell the text out letter by letter (and\n // decode would expose its scrambled glyphs). When split, hide the animated\n // spans and expose one visually-hidden copy of the original text instead.\n _syncSpeech() {\n const split = this.hasAttribute('data-split');\n const interactive = split && this._content.querySelector(INTERACTIVE);\n if (split && !interactive) {\n if (!this._speech) {\n this._speech = document.createElement('span');\n this._speech.className = 'blink-sr';\n }\n this._speech.textContent = this._sourceText;\n this._content.setAttribute('aria-hidden', 'true');\n if (!this._speech.isConnected) this._content.after(this._speech);\n } else {\n this._content.removeAttribute('aria-hidden');\n this._speech?.remove();\n }\n }\n\n // Wrap each unit in a <span class=\"blink-char\"> with a stagger index, so CSS\n // can animate units individually (wave, twinkle, …) and the step engine can\n // advance each unit's data-step on its own staggered clock.\n _splitLetters(byWord = this.unit === 'word') {\n const walker = document.createTreeWalker(this._content, NodeFilter.SHOW_TEXT);\n const textNodes = [];\n while (walker.nextNode()) textNodes.push(walker.currentNode);\n\n // Per-unit randomized clocks: each unit blinks on its own timer.\n const twinkle = this.mode === 'twinkle' || this.mode === 'sparkle';\n const decode = this.mode === 'decode';\n const locale = this.closest('[lang]')?.lang || undefined;\n let i = 0;\n for (const node of textNodes) {\n const frag = document.createDocumentFragment();\n const tokens = byWord\n ? splitWords(node.textContent, locale)\n : splitGraphemes(node.textContent, locale);\n for (const ch of tokens) {\n if (ch === '') continue;\n const space = isWhitespace(ch);\n const span = document.createElement('span');\n span.className = 'blink-char';\n span.style.setProperty('--i', i++);\n if (twinkle) {\n span.style.setProperty('--rate', `${(0.4 + Math.random() * 1.6).toFixed(2)}s`);\n span.style.setProperty('--delay', `${Math.random().toFixed(2)}s`);\n }\n if (space) {\n span.classList.add('blink-space');\n span.textContent = ' ';\n } else {\n span.textContent = ch;\n // decode: remember the real glyph so the scramble loop can settle on it\n if (decode) span.dataset.ch = ch;\n }\n frag.appendChild(span);\n }\n node.replaceWith(frag);\n }\n this._content.style.setProperty('--n', i);\n }\n\n // Whether motion should be suppressed: OS preference or VB's data-motion-reduced,\n // unless this instance opts out with reduced-motion=\"ignore\".\n _reduceMotion() {\n return this.getAttribute('reduced-motion') !== 'ignore' && systemReducesMotion();\n }\n\n // Whether playback is currently paused (state, off-screen, tab-hidden, or hover).\n _isPaused() {\n if (this.hasAttribute('pause-on-hover') && this.matches(':hover, :focus-within')) return true;\n return (\n this.playState === 'paused' ||\n this.dataset.visible === 'false' ||\n this.dataset.tabVisible === 'false'\n );\n }\n\n _stopMorse() {\n if (this._morseRAF) cancelAnimationFrame(this._morseRAF);\n this._morseRAF = null;\n }\n\n // morse mode: blink the text out as Morse code. CSS keys off data-lit to light\n // the text; we also expose the dot/dash string via data-morse for a caption.\n _syncMorse() {\n this._stopMorse();\n if (this.mode !== 'morse') {\n delete this.dataset.lit;\n delete this.dataset.morse;\n return;\n }\n\n const text = (this._content?.textContent || '').trim();\n this.dataset.morse = this._toMorse(text);\n\n // Respect reduced motion: leave the text fully lit, no blinking.\n const reduce = this._reduceMotion();\n if (reduce) {\n this.dataset.lit = 'true';\n return;\n }\n\n const segments = this._morseSegments(text);\n if (segments.length === 0) {\n this.dataset.lit = 'true';\n return;\n }\n\n // Read the dot unit (ms) from a CSS var so it can be themed; default 170ms.\n const dotRaw = parseFloat(getComputedStyle(this).getPropertyValue('--blink-dot'));\n const DOT = Number.isFinite(dotRaw) && dotRaw > 0 ? dotRaw : 170;\n const totalUnits = segments.reduce((a, s) => a + s.dur, 0) + 7; // trailing word gap\n\n let elapsed = 0;\n let lastT = null;\n const tick = (t) => {\n this._morseRAF = requestAnimationFrame(tick);\n if (lastT == null) lastT = t;\n const dt = t - lastT;\n lastT = t;\n if (this._isPaused()) return; // freeze the timeline while paused\n elapsed += dt;\n const cycle = (elapsed / DOT) % totalUnits;\n let acc = 0;\n let lit = false;\n for (const s of segments) {\n if (cycle < acc + s.dur) {\n lit = s.lit;\n break;\n }\n acc += s.dur;\n }\n const next = lit ? 'true' : 'false';\n if (this.dataset.lit !== next) this.dataset.lit = next;\n };\n this.dataset.lit = 'false';\n this._morseRAF = requestAnimationFrame(tick);\n }\n\n // Build an on/off timeline (durations in Morse \"units\") for a string.\n _morseSegments(text) {\n const segments = [];\n const words = text.toLowerCase().split(/\\s+/).filter(Boolean);\n words.forEach((word, wi) => {\n const chars = [...word].filter((c) => MORSE[c]);\n chars.forEach((c, ci) => {\n const symbols = MORSE[c];\n [...symbols].forEach((sym, si) => {\n segments.push({ lit: true, dur: sym === '-' ? 3 : 1 });\n if (si < symbols.length - 1) segments.push({ lit: false, dur: 1 });\n });\n if (ci < chars.length - 1) segments.push({ lit: false, dur: 3 }); // inter-char gap\n });\n if (wi < words.length - 1) segments.push({ lit: false, dur: 7 }); // inter-word gap\n });\n return segments;\n }\n\n // Human-readable dot/dash string, words separated by \" / \".\n _toMorse(text) {\n return text\n .toLowerCase()\n .split(/\\s+/)\n .filter(Boolean)\n .map((word) =>\n [...word]\n .map((c) => MORSE[c] || '')\n .filter(Boolean)\n .join(' ')\n )\n .filter(Boolean)\n .join(' / ');\n }\n\n _observe() {\n this._intersectionObserver = new IntersectionObserver(\n (entries) =>\n entries.forEach((e) => {\n this.dataset.visible = String(e.isIntersecting);\n }),\n { rootMargin: '100px' }\n );\n this._intersectionObserver.observe(this);\n\n this._onVisibility = () => {\n this.dataset.tabVisible = String(!document.hidden);\n };\n document.addEventListener('visibilitychange', this._onVisibility);\n this.dataset.tabVisible = String(!document.hidden);\n\n this._onIteration = () => this._dispatch('blink-cycle');\n this._content.addEventListener('animationiteration', this._onIteration);\n }\n\n _update() {\n if (!this._built && !this._content) return;\n\n // Reflect state to data-* hooks the CSS keys off.\n this.dataset.behavior = this.behavior;\n this.dataset.state = this.playState;\n this.dataset.ready = '';\n\n this.style.setProperty('--blink-rate', this.rate);\n if (this.minOpacity != null) {\n this.style.setProperty('--blink-min-opacity', this.minOpacity);\n } else {\n this.style.removeProperty('--blink-min-opacity');\n }\n if (this.count !== 'infinite') {\n this.style.setProperty('--blink-count', this.count);\n } else {\n this.style.removeProperty('--blink-count');\n }\n\n this._syncMorse();\n this._syncSteps();\n this._syncDecode();\n }\n\n _stopDecode() {\n if (this._decodeRAF) cancelAnimationFrame(this._decodeRAF);\n this._decodeRAF = null;\n }\n\n // decode mode: each unit flickers through random glyphs, then locks onto its\n // real character (stored in data-ch), staggered left-to-right, then\n // re-scrambles on a loop. Monospace keeps the width stable as glyphs change.\n // Freezes when paused; shows the plain text under reduced motion.\n _syncDecode() {\n this._stopDecode();\n if (this.mode !== 'decode') return;\n\n const units = [...this.querySelectorAll('.blink-char')].filter(\n (s) => !s.classList.contains('blink-space') && s.dataset.ch != null\n );\n if (units.length === 0) return;\n\n const reduce = this._reduceMotion();\n if (reduce) {\n for (const s of units) s.textContent = s.dataset.ch;\n return;\n }\n\n const randGlyph = () => DECODE_GLYPHS[(Math.random() * DECODE_GLYPHS.length) | 0];\n\n const SCRAMBLE_MS = 650;\n const STAGGER_MS = 45;\n const HOLD_MS = 1800;\n const FLIP_MS = 55;\n let maxSettle = 0;\n const items = units.map((s) => {\n const i = Number(s.style.getPropertyValue('--i')) || 0;\n const settleAt = SCRAMBLE_MS + i * STAGGER_MS;\n if (settleAt > maxSettle) maxSettle = settleAt;\n return { s, settleAt, target: s.dataset.ch };\n });\n\n let elapsed = 0;\n let lastT = null;\n let lastFlip = 0;\n const tick = (t) => {\n this._decodeRAF = requestAnimationFrame(tick);\n if (lastT == null) lastT = t;\n const dt = t - lastT;\n lastT = t;\n if (this._isPaused()) return; // freeze the timeline while paused\n elapsed += dt;\n const flip = elapsed - lastFlip >= FLIP_MS;\n if (flip) lastFlip = elapsed;\n for (const it of items) {\n if (elapsed >= it.settleAt) {\n if (it.s.textContent !== it.target) it.s.textContent = it.target;\n } else if (flip) {\n it.s.textContent = randGlyph();\n }\n }\n if (elapsed > maxSettle + HOLD_MS) {\n elapsed = 0;\n lastFlip = 0;\n }\n };\n this._decodeRAF = requestAnimationFrame(tick);\n }\n\n // Whether the multi-step engine should drive this element.\n _usesSteps() {\n return this.behavior === 'steps' || STEP_MODES.includes(this.mode);\n }\n\n // Parse a CSS time (e.g. \"1s\", \"800ms\", or a bare number of seconds) to ms.\n _parseTime(value) {\n const t = String(value).trim();\n let ms;\n if (t.endsWith('ms')) ms = parseFloat(t);\n else if (t.endsWith('s')) ms = parseFloat(t) * 1000;\n else ms = parseFloat(t) * 1000;\n return Number.isFinite(ms) && ms > 0 ? ms : 1000;\n }\n\n _stopSteps() {\n if (this._stepRAF) cancelAnimationFrame(this._stepRAF);\n this._stepRAF = null;\n }\n\n // Parse `step-durations` into per-step weights, one per step. Accepts a\n // space- or comma-separated list (e.g. \"3 1 1\" or \"2,1\"); the list is cycled\n // or truncated to fit the step count. Absent/invalid → equal weights.\n _stepWeights(n) {\n const raw = this.stepDurations;\n if (!raw) return Array(n).fill(1);\n const parts = raw\n .split(/[\\s,]+/)\n .map(Number)\n .filter((v) => Number.isFinite(v) && v > 0);\n if (parts.length === 0) return Array(n).fill(1);\n return Array.from({ length: n }, (_, i) => parts[i % parts.length]);\n }\n\n // The element(s) that carry data-step: the whole content for step-by=\"element\",\n // or each unit span for step-by=\"letter\"/\"word\".\n _stepTargets() {\n if (this.stepBy !== 'element' && this.dataset.split != null) {\n return [...this.querySelectorAll('.blink-char')];\n }\n return this._content ? [this._content] : [];\n }\n\n // Clear any data-step left on the content or unit spans (when stepping stops).\n _clearStepTargets() {\n delete this._content?.dataset.step;\n this.querySelectorAll('.blink-char[data-step]').forEach((el) => delete el.dataset.step);\n }\n\n // Multi-step engine: advance data-step=\"0..N-1\" across one cycle (`rate`). Each\n // step holds for a slice of the cycle — equal by default, or weighted by\n // `step-durations`. CSS styles each step (colour, transform, outline, …) and\n // can give each step its own transition timing (--blink-step-ease /\n // --blink-step-timing); a plain blink is the 2-step case. With step-by=\"letter\"\n // (or \"word\") every unit runs the sequence offset by its index, so the states\n // ripple across the text. Freezes when paused/off-screen/tab-hidden; rests at\n // step 0 under reduced motion.\n _syncSteps() {\n this._stopSteps();\n if (!this._usesSteps()) {\n delete this.dataset.stepping;\n this._clearStepTargets();\n return;\n }\n this.dataset.stepping = '';\n\n const n = this.steps;\n const targets = this._stepTargets();\n if (targets.length === 0) return;\n\n const reduce = this._reduceMotion();\n if (reduce) {\n for (const el of targets) el.dataset.step = '0';\n return;\n }\n\n // Build cumulative time boundaries (ms) for each step from its weight.\n const weights = this._stepWeights(n);\n const total = weights.reduce((a, b) => a + b, 0);\n const cycleMs = this._parseTime(this.rate);\n const bounds = [];\n let acc = 0;\n for (const w of weights) {\n acc += w;\n bounds.push((acc / total) * cycleMs);\n }\n\n // Per-unit stepping offsets each unit's phase by a stagger so the states\n // travel across the text. Read --blink-stagger (seconds) for the step.\n const perUnit = targets.length > 1;\n const staggerMs = perUnit\n ? (parseFloat(getComputedStyle(this).getPropertyValue('--blink-stagger')) || 0.08) * 1000\n : 0;\n\n const stepAt = (pos) => {\n let idx = 0;\n while (idx < n - 1 && pos >= bounds[idx]) idx++;\n return idx;\n };\n\n const prev = new Array(targets.length).fill(0);\n targets.forEach((el) => (el.dataset.step = '0'));\n let elapsed = 0;\n let lastT = null;\n const tick = (t) => {\n this._stepRAF = requestAnimationFrame(tick);\n if (lastT == null) lastT = t;\n const dt = t - lastT;\n lastT = t;\n if (this._isPaused()) return; // freeze the timeline while paused\n elapsed += dt;\n for (let k = 0; k < targets.length; k++) {\n const idx = stepAt((elapsed + k * staggerMs) % cycleMs);\n if (idx !== prev[k]) {\n targets[k].dataset.step = String(idx);\n if (k === 0 && idx < prev[k]) this._dispatch('blink-cycle'); // wrapped to start\n prev[k] = idx;\n }\n }\n };\n this._stepRAF = requestAnimationFrame(tick);\n }\n\n _dispatch(name) {\n this.dispatchEvent(new CustomEvent(name, { bubbles: true }));\n }\n}\n\ncustomElements.define('blink-wc', BlinkWc);\n\nexport { BlinkWc };\n"],"names":["LETTER_MODES","STEP_MODES","STEP_COUNTS","DECODE_GLYPHS","MORSE","REDUCED_MQ","INSTANCES","rootObserver","systemReducesMotion","resyncAll","el","watchMotion","unwatchMotion","SEGMENTERS","segmenter","locale","granularity","key","isWhitespace","s","splitGraphemes","text","splitWords","tokens","segment","isWordLike","ws","prev","prevWs","INTERACTIVE","BlinkWc","b","v","_a","_b","name","oldValue","newValue","items","content","n","perUnitSteps","byWord","split","interactive","walker","textNodes","twinkle","decode","i","node","frag","ch","space","span","segments","dotRaw","DOT","totalUnits","a","elapsed","lastT","tick","t","dt","cycle","acc","lit","next","words","word","wi","chars","ci","symbols","sym","si","c","entries","units","randGlyph","SCRAMBLE_MS","STAGGER_MS","HOLD_MS","FLIP_MS","maxSettle","settleAt","lastFlip","flip","it","value","ms","raw","parts","_","targets","weights","total","cycleMs","bounds","w","staggerMs","stepAt","pos","idx","k","__publicField"],"mappings":"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"}
|