@wcstack/debounce 1.12.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.
- package/README.ja.md +153 -0
- package/README.md +157 -0
- package/dist/auto.js +3 -0
- package/dist/auto.min.js +3 -0
- package/dist/index.d.ts +245 -0
- package/dist/index.esm.js +560 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.esm.min.js +2 -0
- package/dist/index.esm.min.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
const _config = {
|
|
2
|
+
autoTrigger: true,
|
|
3
|
+
triggerAttribute: "data-debouncetarget",
|
|
4
|
+
tagNames: {
|
|
5
|
+
debounce: "wcs-debounce",
|
|
6
|
+
throttle: "wcs-throttle",
|
|
7
|
+
},
|
|
8
|
+
};
|
|
9
|
+
function deepFreeze(obj) {
|
|
10
|
+
if (obj === null || typeof obj !== "object")
|
|
11
|
+
return obj;
|
|
12
|
+
Object.freeze(obj);
|
|
13
|
+
for (const key of Object.keys(obj)) {
|
|
14
|
+
deepFreeze(obj[key]);
|
|
15
|
+
}
|
|
16
|
+
return obj;
|
|
17
|
+
}
|
|
18
|
+
function deepClone(obj) {
|
|
19
|
+
if (obj === null || typeof obj !== "object")
|
|
20
|
+
return obj;
|
|
21
|
+
const clone = {};
|
|
22
|
+
for (const key of Object.keys(obj)) {
|
|
23
|
+
clone[key] = deepClone(obj[key]);
|
|
24
|
+
}
|
|
25
|
+
return clone;
|
|
26
|
+
}
|
|
27
|
+
let frozenConfig = null;
|
|
28
|
+
const config = _config;
|
|
29
|
+
function getConfig() {
|
|
30
|
+
if (!frozenConfig) {
|
|
31
|
+
frozenConfig = deepFreeze(deepClone(_config));
|
|
32
|
+
}
|
|
33
|
+
return frozenConfig;
|
|
34
|
+
}
|
|
35
|
+
function setConfig(partialConfig) {
|
|
36
|
+
if (typeof partialConfig.autoTrigger === "boolean") {
|
|
37
|
+
_config.autoTrigger = partialConfig.autoTrigger;
|
|
38
|
+
}
|
|
39
|
+
if (typeof partialConfig.triggerAttribute === "string") {
|
|
40
|
+
_config.triggerAttribute = partialConfig.triggerAttribute;
|
|
41
|
+
}
|
|
42
|
+
if (partialConfig.tagNames) {
|
|
43
|
+
Object.assign(_config.tagNames, partialConfig.tagNames);
|
|
44
|
+
}
|
|
45
|
+
frozenConfig = null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build the wc-bindable `properties` list for a debounce/throttle element, with
|
|
50
|
+
* every event name derived from a single `prefix`. `<wcs-debounce>` uses
|
|
51
|
+
* `"wcs-debounce"`, `<wcs-throttle>` uses `"wcs-throttle"`, so the two tags share
|
|
52
|
+
* one engine (DebounceCore) yet advertise distinct event namespaces from one
|
|
53
|
+
* source of truth — no hand-duplicated property tables (cf. the geolocation Shell
|
|
54
|
+
* which overrode its wcBindable by hand).
|
|
55
|
+
*
|
|
56
|
+
* - `value` — the debounced value of the latest `source` write (value surface),
|
|
57
|
+
* read from the `<prefix>:settled` event.
|
|
58
|
+
* - `fired` — the coalesced args of the latest `trigger()` pulse (signal
|
|
59
|
+
* surface), read from the `<prefix>:fired` event. Declared as a
|
|
60
|
+
* property (not just an event) so state can subscribe via the
|
|
61
|
+
* event-token protocol (`eventToken.fired: <name>`).
|
|
62
|
+
* - `pending` — whether a debounce is currently in flight.
|
|
63
|
+
*/
|
|
64
|
+
function makeDebounceProperties(prefix) {
|
|
65
|
+
return [
|
|
66
|
+
{ name: "value", event: `${prefix}:settled`, getter: (e) => e.detail.value },
|
|
67
|
+
{ name: "fired", event: `${prefix}:fired`, getter: (e) => e.detail.args },
|
|
68
|
+
{ name: "pending", event: `${prefix}:pending-changed` },
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Headless debounce/throttle primitive. A framework-agnostic port of lodash's
|
|
74
|
+
* `debounce` algorithm (`shouldInvoke` / `leadingEdge` / `trailingEdge` /
|
|
75
|
+
* `remainingWait`, timed via `Date.now()`), exposed through the wc-bindable
|
|
76
|
+
* protocol.
|
|
77
|
+
*
|
|
78
|
+
* It coalesces a stream of *signals* and emits at most one per quiet period.
|
|
79
|
+
* Two surfaces share the single timer:
|
|
80
|
+
*
|
|
81
|
+
* - **value** — writing {@link setSource} schedules a settle; on fire the
|
|
82
|
+
* debounced value is published via the `<prefix>:settled` event and the
|
|
83
|
+
* `value` getter. Wire it as `source: src; value: debounced`.
|
|
84
|
+
* - **signal** — calling {@link trigger}`(...args)` coalesces a burst of pulses;
|
|
85
|
+
* on fire one `<prefix>:fired` event carries the latest args (relayed by state
|
|
86
|
+
* through the command-token / event-token protocols).
|
|
87
|
+
*
|
|
88
|
+
* A given instance is meant to be used for one surface at a time. Because each
|
|
89
|
+
* surface keeps its own field (`_value` vs `_lastArgs`), the getters never
|
|
90
|
+
* pollute each other; if both are driven on one instance the *last* scheduled
|
|
91
|
+
* signal wins (lodash's last-args semantics).
|
|
92
|
+
*
|
|
93
|
+
* Throttle is the same engine with `maxWait === wait` (and `leading` on by
|
|
94
|
+
* default), so `<wcs-throttle>` reuses this class with a different `eventPrefix`.
|
|
95
|
+
*/
|
|
96
|
+
class DebounceCore extends EventTarget {
|
|
97
|
+
// The static contract advertises the *default* `wcs-debounce:*` event names. A
|
|
98
|
+
// headless Core constructed with a non-default `eventPrefix` (e.g.
|
|
99
|
+
// `"wcs-throttle"`) dispatches under that prefix, so its events won't match
|
|
100
|
+
// this metadata — rebuild the property table with `makeDebounceProperties(prefix)`
|
|
101
|
+
// for that case. (The `<wcs-throttle>` Shell already overrides its own
|
|
102
|
+
// `wcBindable` this way, so binding through an element is always consistent.)
|
|
103
|
+
static wcBindable = {
|
|
104
|
+
protocol: "wc-bindable",
|
|
105
|
+
version: 1,
|
|
106
|
+
properties: makeDebounceProperties("wcs-debounce"),
|
|
107
|
+
commands: [
|
|
108
|
+
{ name: "trigger" },
|
|
109
|
+
{ name: "cancel" },
|
|
110
|
+
{ name: "flush" },
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
_prefix;
|
|
114
|
+
_target;
|
|
115
|
+
// Tuning (lodash knobs).
|
|
116
|
+
_wait = 0;
|
|
117
|
+
_leading = false;
|
|
118
|
+
_trailing = true;
|
|
119
|
+
_maxWait = 0;
|
|
120
|
+
_hasMaxWait = false;
|
|
121
|
+
// Timing bookkeeping.
|
|
122
|
+
_lastCallTime = undefined;
|
|
123
|
+
_lastInvokeTime = 0;
|
|
124
|
+
_timerId = null;
|
|
125
|
+
// Last-wins pending payload. `_pendingKind === null` doubles as the "consumed /
|
|
126
|
+
// empty" sentinel (lodash clears `lastArgs` in invokeFunc): a trailing edge with
|
|
127
|
+
// no fresh call since the last fire sees `null` and does not re-fire, which is
|
|
128
|
+
// what stops a single leading pulse from also firing on the trailing edge.
|
|
129
|
+
_pendingKind = null;
|
|
130
|
+
_pendingValue = undefined;
|
|
131
|
+
_pendingArgs = undefined;
|
|
132
|
+
// Observable state (getter backing).
|
|
133
|
+
_value = undefined;
|
|
134
|
+
_lastArgs = [];
|
|
135
|
+
_pending = false;
|
|
136
|
+
constructor(prefix = "wcs-debounce", target, options) {
|
|
137
|
+
super();
|
|
138
|
+
this._prefix = prefix;
|
|
139
|
+
this._target = target ?? this;
|
|
140
|
+
if (options) {
|
|
141
|
+
this.configure(options);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// --- Configuration ---
|
|
145
|
+
/**
|
|
146
|
+
* Update the tuning knobs. The Shell calls this with the element's current
|
|
147
|
+
* attributes before each schedule, so live attribute edits take effect on the
|
|
148
|
+
* next signal. `maxWait` is clamped to at least `wait` (lodash semantics); an
|
|
149
|
+
* absent / invalid `maxWait` disables maxWait entirely.
|
|
150
|
+
*/
|
|
151
|
+
configure(options = {}) {
|
|
152
|
+
if (typeof options.wait === "number" && Number.isFinite(options.wait) && options.wait >= 0) {
|
|
153
|
+
this._wait = options.wait;
|
|
154
|
+
}
|
|
155
|
+
if (typeof options.leading === "boolean") {
|
|
156
|
+
this._leading = options.leading;
|
|
157
|
+
}
|
|
158
|
+
if (typeof options.trailing === "boolean") {
|
|
159
|
+
this._trailing = options.trailing;
|
|
160
|
+
}
|
|
161
|
+
const mw = options.maxWait;
|
|
162
|
+
if (typeof mw === "number" && Number.isFinite(mw) && mw >= 0) {
|
|
163
|
+
this._maxWait = Math.max(mw, this._wait);
|
|
164
|
+
this._hasMaxWait = true;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
this._maxWait = 0;
|
|
168
|
+
this._hasMaxWait = false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// --- Observable getters ---
|
|
172
|
+
get value() {
|
|
173
|
+
return this._value;
|
|
174
|
+
}
|
|
175
|
+
get fired() {
|
|
176
|
+
return this._lastArgs;
|
|
177
|
+
}
|
|
178
|
+
get pending() {
|
|
179
|
+
return this._pending;
|
|
180
|
+
}
|
|
181
|
+
// --- Public entry points ---
|
|
182
|
+
/** Value surface: schedule a settle carrying `value` (last write wins). */
|
|
183
|
+
setSource(value) {
|
|
184
|
+
this._schedule("value", value, undefined);
|
|
185
|
+
}
|
|
186
|
+
/** Signal surface: coalesce a pulse carrying `args` (last call wins). */
|
|
187
|
+
trigger(...args) {
|
|
188
|
+
this._schedule("signal", undefined, args);
|
|
189
|
+
}
|
|
190
|
+
/** Drop any pending fire without emitting. Getters keep their last values. */
|
|
191
|
+
cancel() {
|
|
192
|
+
this._clearTimer();
|
|
193
|
+
this._lastInvokeTime = 0;
|
|
194
|
+
this._lastCallTime = undefined;
|
|
195
|
+
this._pendingKind = null;
|
|
196
|
+
this._pendingValue = undefined;
|
|
197
|
+
this._pendingArgs = undefined;
|
|
198
|
+
this._setPending(false);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Emit any buffered payload immediately, then clear pending. Unlike lodash's
|
|
202
|
+
* `flush` (which honours `trailing`), this fires whatever is buffered — the
|
|
203
|
+
* command's intent is "publish now" — but is a no-op when nothing is pending.
|
|
204
|
+
*/
|
|
205
|
+
flush() {
|
|
206
|
+
if (this._timerId === null && this._pendingKind === null)
|
|
207
|
+
return;
|
|
208
|
+
const now = Date.now();
|
|
209
|
+
this._clearTimer();
|
|
210
|
+
if (this._pendingKind !== null) {
|
|
211
|
+
this._invoke(now);
|
|
212
|
+
}
|
|
213
|
+
this._pendingKind = null;
|
|
214
|
+
this._pendingValue = undefined;
|
|
215
|
+
this._pendingArgs = undefined;
|
|
216
|
+
this._setPending(false);
|
|
217
|
+
}
|
|
218
|
+
// --- Engine (lodash port) ---
|
|
219
|
+
_schedule(kind, value, args) {
|
|
220
|
+
const now = Date.now();
|
|
221
|
+
const isInvoking = this._shouldInvoke(now);
|
|
222
|
+
this._pendingKind = kind;
|
|
223
|
+
if (kind === "value") {
|
|
224
|
+
this._pendingValue = value;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
this._pendingArgs = args;
|
|
228
|
+
}
|
|
229
|
+
this._lastCallTime = now;
|
|
230
|
+
this._setPending(true);
|
|
231
|
+
if (isInvoking) {
|
|
232
|
+
if (this._timerId === null) {
|
|
233
|
+
this._leadingEdge(now);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (this._hasMaxWait) {
|
|
237
|
+
// Continuous input that has reached maxWait: restart the steady timer and
|
|
238
|
+
// invoke now so a fire happens at least every maxWait ms (throttle path).
|
|
239
|
+
// Clear the pending handle first (lodash's "tight loop" branch does the
|
|
240
|
+
// same) — overwriting `_timerId` without clearing would orphan the old
|
|
241
|
+
// timer and let it fire spuriously later.
|
|
242
|
+
this._clearTimer();
|
|
243
|
+
this._timerId = setTimeout(this._timerExpired, this._wait);
|
|
244
|
+
this._invoke(now);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (this._timerId === null) {
|
|
249
|
+
this._timerId = setTimeout(this._timerExpired, this._wait);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
_shouldInvoke(now) {
|
|
253
|
+
if (this._lastCallTime === undefined)
|
|
254
|
+
return true;
|
|
255
|
+
const timeSinceLastCall = now - this._lastCallTime;
|
|
256
|
+
const timeSinceLastInvoke = now - this._lastInvokeTime;
|
|
257
|
+
return (timeSinceLastCall >= this._wait ||
|
|
258
|
+
timeSinceLastCall < 0 ||
|
|
259
|
+
(this._hasMaxWait && timeSinceLastInvoke >= this._maxWait));
|
|
260
|
+
}
|
|
261
|
+
_remainingWait(now) {
|
|
262
|
+
const timeSinceLastCall = now - this._lastCallTime;
|
|
263
|
+
const timeSinceLastInvoke = now - this._lastInvokeTime;
|
|
264
|
+
const timeWaiting = this._wait - timeSinceLastCall;
|
|
265
|
+
return this._hasMaxWait
|
|
266
|
+
? Math.min(timeWaiting, this._maxWait - timeSinceLastInvoke)
|
|
267
|
+
: timeWaiting;
|
|
268
|
+
}
|
|
269
|
+
_timerExpired = () => {
|
|
270
|
+
const now = Date.now();
|
|
271
|
+
if (this._shouldInvoke(now)) {
|
|
272
|
+
this._trailingEdge(now);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
// Fired before the quiet period elapsed (a later call moved the deadline) —
|
|
276
|
+
// re-arm for the remaining time instead of settling now.
|
|
277
|
+
this._timerId = setTimeout(this._timerExpired, this._remainingWait(now));
|
|
278
|
+
};
|
|
279
|
+
_leadingEdge(now) {
|
|
280
|
+
this._lastInvokeTime = now;
|
|
281
|
+
this._timerId = setTimeout(this._timerExpired, this._wait);
|
|
282
|
+
if (this._leading) {
|
|
283
|
+
this._invoke(now);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
_trailingEdge(now) {
|
|
287
|
+
this._timerId = null;
|
|
288
|
+
// Only fire when there is an unconsumed payload (a call arrived since the last
|
|
289
|
+
// invoke). After a lone leading fire `_pendingKind` is null, so a single pulse
|
|
290
|
+
// does not double-fire on its trailing edge.
|
|
291
|
+
if (this._trailing && this._pendingKind !== null) {
|
|
292
|
+
this._invoke(now);
|
|
293
|
+
}
|
|
294
|
+
this._pendingKind = null;
|
|
295
|
+
this._pendingValue = undefined;
|
|
296
|
+
this._pendingArgs = undefined;
|
|
297
|
+
this._setPending(false);
|
|
298
|
+
}
|
|
299
|
+
_invoke(now) {
|
|
300
|
+
this._lastInvokeTime = now;
|
|
301
|
+
const kind = this._pendingKind;
|
|
302
|
+
if (kind === "value") {
|
|
303
|
+
this._value = this._pendingValue;
|
|
304
|
+
this._dispatch(`${this._prefix}:settled`, { value: this._value });
|
|
305
|
+
}
|
|
306
|
+
else if (kind === "signal") {
|
|
307
|
+
this._lastArgs = this._pendingArgs ?? [];
|
|
308
|
+
this._dispatch(`${this._prefix}:fired`, { args: this._lastArgs });
|
|
309
|
+
}
|
|
310
|
+
// Mark consumed (mirrors lodash clearing `lastArgs`).
|
|
311
|
+
this._pendingKind = null;
|
|
312
|
+
}
|
|
313
|
+
_setPending(pending) {
|
|
314
|
+
if (this._pending === pending)
|
|
315
|
+
return;
|
|
316
|
+
this._pending = pending;
|
|
317
|
+
this._dispatch(`${this._prefix}:pending-changed`, pending);
|
|
318
|
+
}
|
|
319
|
+
_dispatch(type, detail) {
|
|
320
|
+
this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));
|
|
321
|
+
}
|
|
322
|
+
_clearTimer() {
|
|
323
|
+
if (this._timerId !== null) {
|
|
324
|
+
clearTimeout(this._timerId);
|
|
325
|
+
this._timerId = null;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
let registered = false;
|
|
331
|
+
function handleClick(event) {
|
|
332
|
+
const target = event.target;
|
|
333
|
+
if (!(target instanceof Element))
|
|
334
|
+
return;
|
|
335
|
+
const triggerElement = target.closest(`[${config.triggerAttribute}]`);
|
|
336
|
+
if (!triggerElement)
|
|
337
|
+
return;
|
|
338
|
+
const id = triggerElement.getAttribute(config.triggerAttribute);
|
|
339
|
+
if (!id)
|
|
340
|
+
return;
|
|
341
|
+
// Resolve the registered constructors at call time (avoids a components ⇄
|
|
342
|
+
// autoTrigger import cycle, mirroring <wcs-timer>). The DOM trigger fires a
|
|
343
|
+
// single coalesced pulse on the referenced <wcs-debounce> / <wcs-throttle>.
|
|
344
|
+
const DebounceCtor = customElements.get(config.tagNames.debounce);
|
|
345
|
+
const ThrottleCtor = customElements.get(config.tagNames.throttle);
|
|
346
|
+
const el = document.getElementById(id);
|
|
347
|
+
if (!el)
|
|
348
|
+
return;
|
|
349
|
+
const isDebounce = DebounceCtor && el instanceof DebounceCtor;
|
|
350
|
+
const isThrottle = ThrottleCtor && el instanceof ThrottleCtor;
|
|
351
|
+
if (!isDebounce && !isThrottle)
|
|
352
|
+
return;
|
|
353
|
+
// Suppress the element's default action so a debounce can fire without
|
|
354
|
+
// navigating. See README "Optional DOM Triggering".
|
|
355
|
+
event.preventDefault();
|
|
356
|
+
el.trigger();
|
|
357
|
+
}
|
|
358
|
+
function registerAutoTrigger() {
|
|
359
|
+
if (registered)
|
|
360
|
+
return;
|
|
361
|
+
registered = true;
|
|
362
|
+
document.addEventListener("click", handleClick);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const DEFAULT_WAIT = 250;
|
|
366
|
+
/**
|
|
367
|
+
* `<wcs-debounce>` — declarative debounce. See {@link DebounceCore} for the
|
|
368
|
+
* engine. The `eventPrefix` defaults to `"wcs-debounce"`; `<wcs-throttle>`
|
|
369
|
+
* subclasses this with `"wcs-throttle"` and throttle defaults.
|
|
370
|
+
*/
|
|
371
|
+
class Debounce extends HTMLElement {
|
|
372
|
+
static hasConnectedCallbackPromise = false;
|
|
373
|
+
static eventPrefix = "wcs-debounce";
|
|
374
|
+
static wcBindable = {
|
|
375
|
+
protocol: "wc-bindable",
|
|
376
|
+
version: 1,
|
|
377
|
+
properties: makeDebounceProperties("wcs-debounce"),
|
|
378
|
+
// `source` is the value-surface input (its debounced echo comes back on the
|
|
379
|
+
// `value` property). `trigger` / `cancel` / `flush` are commands from the
|
|
380
|
+
// Core. No momentary boolean `trigger` property exists — the signal surface
|
|
381
|
+
// is driven by the `trigger` command (command-token) or a DOM click
|
|
382
|
+
// (autoTrigger), which sidesteps the attribute/method name clash that forced
|
|
383
|
+
// <wcs-geo>'s watch → watchPosition rename.
|
|
384
|
+
inputs: [
|
|
385
|
+
{ name: "source" },
|
|
386
|
+
{ name: "wait", attribute: "wait" },
|
|
387
|
+
{ name: "leading", attribute: "leading" },
|
|
388
|
+
{ name: "trailing", attribute: "trailing" },
|
|
389
|
+
{ name: "maxWait", attribute: "max-wait" },
|
|
390
|
+
],
|
|
391
|
+
commands: [
|
|
392
|
+
{ name: "trigger" },
|
|
393
|
+
{ name: "cancel" },
|
|
394
|
+
{ name: "flush" },
|
|
395
|
+
],
|
|
396
|
+
};
|
|
397
|
+
_core;
|
|
398
|
+
_source = undefined;
|
|
399
|
+
constructor() {
|
|
400
|
+
super();
|
|
401
|
+
this._core = new DebounceCore(this.constructor.eventPrefix, this);
|
|
402
|
+
}
|
|
403
|
+
// --- Attribute accessors ---
|
|
404
|
+
get wait() {
|
|
405
|
+
const attr = this.getAttribute("wait");
|
|
406
|
+
if (attr === null || attr.trim() === "")
|
|
407
|
+
return this._defaultWait();
|
|
408
|
+
// Strict parse via Number() ("100px" -> NaN, not 100). Fall back to the
|
|
409
|
+
// default for any non-finite or negative value.
|
|
410
|
+
const parsed = Number(attr);
|
|
411
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : this._defaultWait();
|
|
412
|
+
}
|
|
413
|
+
set wait(value) {
|
|
414
|
+
this.setAttribute("wait", String(value));
|
|
415
|
+
}
|
|
416
|
+
get leading() {
|
|
417
|
+
return this.hasAttribute("leading");
|
|
418
|
+
}
|
|
419
|
+
set leading(value) {
|
|
420
|
+
if (value) {
|
|
421
|
+
this.setAttribute("leading", "");
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
this.removeAttribute("leading");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// `trailing` defaults to true; the boolean `no-trailing` attribute opts out (a
|
|
428
|
+
// bare `trailing` attribute can't express "false", so the negative flag carries
|
|
429
|
+
// the override).
|
|
430
|
+
get trailing() {
|
|
431
|
+
return !this.hasAttribute("no-trailing");
|
|
432
|
+
}
|
|
433
|
+
set trailing(value) {
|
|
434
|
+
if (value) {
|
|
435
|
+
this.removeAttribute("no-trailing");
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
this.setAttribute("no-trailing", "");
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
get maxWait() {
|
|
442
|
+
const attr = this.getAttribute("max-wait");
|
|
443
|
+
if (attr === null || attr.trim() === "")
|
|
444
|
+
return this._defaultMaxWait();
|
|
445
|
+
const parsed = Number(attr);
|
|
446
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : this._defaultMaxWait();
|
|
447
|
+
}
|
|
448
|
+
set maxWait(value) {
|
|
449
|
+
this.setAttribute("max-wait", String(value));
|
|
450
|
+
}
|
|
451
|
+
// --- Value-surface input ---
|
|
452
|
+
get source() {
|
|
453
|
+
return this._source;
|
|
454
|
+
}
|
|
455
|
+
set source(value) {
|
|
456
|
+
this._source = value;
|
|
457
|
+
this._core.configure(this._options());
|
|
458
|
+
this._core.setSource(value);
|
|
459
|
+
}
|
|
460
|
+
// --- Core delegated getters ---
|
|
461
|
+
get value() {
|
|
462
|
+
return this._core.value;
|
|
463
|
+
}
|
|
464
|
+
get fired() {
|
|
465
|
+
return this._core.fired;
|
|
466
|
+
}
|
|
467
|
+
get pending() {
|
|
468
|
+
return this._core.pending;
|
|
469
|
+
}
|
|
470
|
+
// --- Commands ---
|
|
471
|
+
trigger(...args) {
|
|
472
|
+
this._core.configure(this._options());
|
|
473
|
+
this._core.trigger(...args);
|
|
474
|
+
}
|
|
475
|
+
cancel() {
|
|
476
|
+
this._core.cancel();
|
|
477
|
+
}
|
|
478
|
+
flush() {
|
|
479
|
+
this._core.flush();
|
|
480
|
+
}
|
|
481
|
+
// --- Internal ---
|
|
482
|
+
// Overridden by <wcs-throttle> to bias the defaults toward throttle (leading
|
|
483
|
+
// on, maxWait pinned to wait).
|
|
484
|
+
_defaultWait() {
|
|
485
|
+
return DEFAULT_WAIT;
|
|
486
|
+
}
|
|
487
|
+
// Resolves the effective `leading` value (not a static default). It is a method
|
|
488
|
+
// rather than reading `this.leading` directly because <wcs-throttle> inverts the
|
|
489
|
+
// default (on, opt out via `no-leading`) while sharing the inherited `leading`
|
|
490
|
+
// attribute setter — overriding the getter alone would desync getter and setter.
|
|
491
|
+
_resolveLeading() {
|
|
492
|
+
return this.leading;
|
|
493
|
+
}
|
|
494
|
+
_defaultMaxWait() {
|
|
495
|
+
return undefined;
|
|
496
|
+
}
|
|
497
|
+
_options() {
|
|
498
|
+
return {
|
|
499
|
+
wait: this.wait,
|
|
500
|
+
leading: this._resolveLeading(),
|
|
501
|
+
trailing: this.trailing,
|
|
502
|
+
maxWait: this.maxWait,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
// --- Lifecycle ---
|
|
506
|
+
connectedCallback() {
|
|
507
|
+
this.style.display = "none";
|
|
508
|
+
if (config.autoTrigger) {
|
|
509
|
+
registerAutoTrigger();
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
disconnectedCallback() {
|
|
513
|
+
// Drop any in-flight timer so a detached element leaks nothing.
|
|
514
|
+
this._core.cancel();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* `<wcs-throttle>` — the same {@link DebounceCore} engine biased to throttle:
|
|
520
|
+
* `maxWait === wait` (a fire happens at least every `wait` ms under continuous
|
|
521
|
+
* input) and `leading` on by default. It advertises its own `wcs-throttle:*`
|
|
522
|
+
* event namespace (via `makeDebounceProperties("wcs-throttle")`), and the Core
|
|
523
|
+
* dispatches under that prefix because the constructor passes it through.
|
|
524
|
+
*/
|
|
525
|
+
class Throttle extends Debounce {
|
|
526
|
+
static eventPrefix = "wcs-throttle";
|
|
527
|
+
static wcBindable = {
|
|
528
|
+
...Debounce.wcBindable,
|
|
529
|
+
properties: makeDebounceProperties("wcs-throttle"),
|
|
530
|
+
};
|
|
531
|
+
// leading defaults on for throttle; `no-leading` opts out (symmetric with the
|
|
532
|
+
// inherited `no-trailing`).
|
|
533
|
+
_resolveLeading() {
|
|
534
|
+
return !this.hasAttribute("no-leading");
|
|
535
|
+
}
|
|
536
|
+
// Pin maxWait to wait so throttle fires on a steady cadence; an explicit
|
|
537
|
+
// `max-wait` attribute still overrides via the inherited getter.
|
|
538
|
+
_defaultMaxWait() {
|
|
539
|
+
return this.wait;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function registerComponents() {
|
|
544
|
+
if (!customElements.get(config.tagNames.debounce)) {
|
|
545
|
+
customElements.define(config.tagNames.debounce, Debounce);
|
|
546
|
+
}
|
|
547
|
+
if (!customElements.get(config.tagNames.throttle)) {
|
|
548
|
+
customElements.define(config.tagNames.throttle, Throttle);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function bootstrapDebounce(userConfig) {
|
|
553
|
+
if (userConfig) {
|
|
554
|
+
setConfig(userConfig);
|
|
555
|
+
}
|
|
556
|
+
registerComponents();
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export { DebounceCore, Debounce as WcsDebounce, Throttle as WcsThrottle, bootstrapDebounce, getConfig, makeDebounceProperties };
|
|
560
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/wcBindableFactory.ts","../src/core/DebounceCore.ts","../src/autoTrigger.ts","../src/components/Debounce.ts","../src/components/Throttle.ts","../src/registerComponents.ts","../src/bootstrapDebounce.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n debounce: string;\n throttle: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-debouncetarget\",\n tagNames: {\n debounce: \"wcs-debounce\",\n throttle: \"wcs-throttle\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindableProperty } from \"./types.js\";\n\n/**\n * Build the wc-bindable `properties` list for a debounce/throttle element, with\n * every event name derived from a single `prefix`. `<wcs-debounce>` uses\n * `\"wcs-debounce\"`, `<wcs-throttle>` uses `\"wcs-throttle\"`, so the two tags share\n * one engine (DebounceCore) yet advertise distinct event namespaces from one\n * source of truth — no hand-duplicated property tables (cf. the geolocation Shell\n * which overrode its wcBindable by hand).\n *\n * - `value` — the debounced value of the latest `source` write (value surface),\n * read from the `<prefix>:settled` event.\n * - `fired` — the coalesced args of the latest `trigger()` pulse (signal\n * surface), read from the `<prefix>:fired` event. Declared as a\n * property (not just an event) so state can subscribe via the\n * event-token protocol (`eventToken.fired: <name>`).\n * - `pending` — whether a debounce is currently in flight.\n */\nexport function makeDebounceProperties(prefix: string): IWcBindableProperty[] {\n return [\n { name: \"value\", event: `${prefix}:settled`, getter: (e: Event) => (e as CustomEvent).detail.value },\n { name: \"fired\", event: `${prefix}:fired`, getter: (e: Event) => (e as CustomEvent).detail.args },\n { name: \"pending\", event: `${prefix}:pending-changed` },\n ];\n}\n","import { IWcBindable, DebounceOptions } from \"../types.js\";\nimport { makeDebounceProperties } from \"../wcBindableFactory.js\";\n\ntype PendingKind = \"value\" | \"signal\" | null;\n\n/**\n * Headless debounce/throttle primitive. A framework-agnostic port of lodash's\n * `debounce` algorithm (`shouldInvoke` / `leadingEdge` / `trailingEdge` /\n * `remainingWait`, timed via `Date.now()`), exposed through the wc-bindable\n * protocol.\n *\n * It coalesces a stream of *signals* and emits at most one per quiet period.\n * Two surfaces share the single timer:\n *\n * - **value** — writing {@link setSource} schedules a settle; on fire the\n * debounced value is published via the `<prefix>:settled` event and the\n * `value` getter. Wire it as `source: src; value: debounced`.\n * - **signal** — calling {@link trigger}`(...args)` coalesces a burst of pulses;\n * on fire one `<prefix>:fired` event carries the latest args (relayed by state\n * through the command-token / event-token protocols).\n *\n * A given instance is meant to be used for one surface at a time. Because each\n * surface keeps its own field (`_value` vs `_lastArgs`), the getters never\n * pollute each other; if both are driven on one instance the *last* scheduled\n * signal wins (lodash's last-args semantics).\n *\n * Throttle is the same engine with `maxWait === wait` (and `leading` on by\n * default), so `<wcs-throttle>` reuses this class with a different `eventPrefix`.\n */\nexport class DebounceCore extends EventTarget {\n // The static contract advertises the *default* `wcs-debounce:*` event names. A\n // headless Core constructed with a non-default `eventPrefix` (e.g.\n // `\"wcs-throttle\"`) dispatches under that prefix, so its events won't match\n // this metadata — rebuild the property table with `makeDebounceProperties(prefix)`\n // for that case. (The `<wcs-throttle>` Shell already overrides its own\n // `wcBindable` this way, so binding through an element is always consistent.)\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: makeDebounceProperties(\"wcs-debounce\"),\n commands: [\n { name: \"trigger\" },\n { name: \"cancel\" },\n { name: \"flush\" },\n ],\n };\n\n private readonly _prefix: string;\n private _target: EventTarget;\n\n // Tuning (lodash knobs).\n private _wait: number = 0;\n private _leading: boolean = false;\n private _trailing: boolean = true;\n private _maxWait: number = 0;\n private _hasMaxWait: boolean = false;\n\n // Timing bookkeeping.\n private _lastCallTime: number | undefined = undefined;\n private _lastInvokeTime: number = 0;\n private _timerId: ReturnType<typeof setTimeout> | null = null;\n\n // Last-wins pending payload. `_pendingKind === null` doubles as the \"consumed /\n // empty\" sentinel (lodash clears `lastArgs` in invokeFunc): a trailing edge with\n // no fresh call since the last fire sees `null` and does not re-fire, which is\n // what stops a single leading pulse from also firing on the trailing edge.\n private _pendingKind: PendingKind = null;\n private _pendingValue: any = undefined;\n private _pendingArgs: any[] | undefined = undefined;\n\n // Observable state (getter backing).\n private _value: any = undefined;\n private _lastArgs: any[] = [];\n private _pending: boolean = false;\n\n constructor(prefix: string = \"wcs-debounce\", target?: EventTarget, options?: DebounceOptions) {\n super();\n this._prefix = prefix;\n this._target = target ?? this;\n if (options) {\n this.configure(options);\n }\n }\n\n // --- Configuration ---\n\n /**\n * Update the tuning knobs. The Shell calls this with the element's current\n * attributes before each schedule, so live attribute edits take effect on the\n * next signal. `maxWait` is clamped to at least `wait` (lodash semantics); an\n * absent / invalid `maxWait` disables maxWait entirely.\n */\n configure(options: DebounceOptions = {}): void {\n if (typeof options.wait === \"number\" && Number.isFinite(options.wait) && options.wait >= 0) {\n this._wait = options.wait;\n }\n if (typeof options.leading === \"boolean\") {\n this._leading = options.leading;\n }\n if (typeof options.trailing === \"boolean\") {\n this._trailing = options.trailing;\n }\n const mw = options.maxWait;\n if (typeof mw === \"number\" && Number.isFinite(mw) && mw >= 0) {\n this._maxWait = Math.max(mw, this._wait);\n this._hasMaxWait = true;\n } else {\n this._maxWait = 0;\n this._hasMaxWait = false;\n }\n }\n\n // --- Observable getters ---\n\n get value(): any {\n return this._value;\n }\n\n get fired(): any[] {\n return this._lastArgs;\n }\n\n get pending(): boolean {\n return this._pending;\n }\n\n // --- Public entry points ---\n\n /** Value surface: schedule a settle carrying `value` (last write wins). */\n setSource(value: any): void {\n this._schedule(\"value\", value, undefined);\n }\n\n /** Signal surface: coalesce a pulse carrying `args` (last call wins). */\n trigger(...args: any[]): void {\n this._schedule(\"signal\", undefined, args);\n }\n\n /** Drop any pending fire without emitting. Getters keep their last values. */\n cancel(): void {\n this._clearTimer();\n this._lastInvokeTime = 0;\n this._lastCallTime = undefined;\n this._pendingKind = null;\n this._pendingValue = undefined;\n this._pendingArgs = undefined;\n this._setPending(false);\n }\n\n /**\n * Emit any buffered payload immediately, then clear pending. Unlike lodash's\n * `flush` (which honours `trailing`), this fires whatever is buffered — the\n * command's intent is \"publish now\" — but is a no-op when nothing is pending.\n */\n flush(): void {\n if (this._timerId === null && this._pendingKind === null) return;\n const now = Date.now();\n this._clearTimer();\n if (this._pendingKind !== null) {\n this._invoke(now);\n }\n this._pendingKind = null;\n this._pendingValue = undefined;\n this._pendingArgs = undefined;\n this._setPending(false);\n }\n\n // --- Engine (lodash port) ---\n\n private _schedule(kind: Exclude<PendingKind, null>, value: any, args: any[] | undefined): void {\n const now = Date.now();\n const isInvoking = this._shouldInvoke(now);\n\n this._pendingKind = kind;\n if (kind === \"value\") {\n this._pendingValue = value;\n } else {\n this._pendingArgs = args;\n }\n this._lastCallTime = now;\n this._setPending(true);\n\n if (isInvoking) {\n if (this._timerId === null) {\n this._leadingEdge(now);\n return;\n }\n if (this._hasMaxWait) {\n // Continuous input that has reached maxWait: restart the steady timer and\n // invoke now so a fire happens at least every maxWait ms (throttle path).\n // Clear the pending handle first (lodash's \"tight loop\" branch does the\n // same) — overwriting `_timerId` without clearing would orphan the old\n // timer and let it fire spuriously later.\n this._clearTimer();\n this._timerId = setTimeout(this._timerExpired, this._wait);\n this._invoke(now);\n return;\n }\n }\n if (this._timerId === null) {\n this._timerId = setTimeout(this._timerExpired, this._wait);\n }\n }\n\n private _shouldInvoke(now: number): boolean {\n if (this._lastCallTime === undefined) return true;\n const timeSinceLastCall = now - this._lastCallTime;\n const timeSinceLastInvoke = now - this._lastInvokeTime;\n return (\n timeSinceLastCall >= this._wait ||\n timeSinceLastCall < 0 ||\n (this._hasMaxWait && timeSinceLastInvoke >= this._maxWait)\n );\n }\n\n private _remainingWait(now: number): number {\n const timeSinceLastCall = now - (this._lastCallTime as number);\n const timeSinceLastInvoke = now - this._lastInvokeTime;\n const timeWaiting = this._wait - timeSinceLastCall;\n return this._hasMaxWait\n ? Math.min(timeWaiting, this._maxWait - timeSinceLastInvoke)\n : timeWaiting;\n }\n\n private _timerExpired = (): void => {\n const now = Date.now();\n if (this._shouldInvoke(now)) {\n this._trailingEdge(now);\n return;\n }\n // Fired before the quiet period elapsed (a later call moved the deadline) —\n // re-arm for the remaining time instead of settling now.\n this._timerId = setTimeout(this._timerExpired, this._remainingWait(now));\n };\n\n private _leadingEdge(now: number): void {\n this._lastInvokeTime = now;\n this._timerId = setTimeout(this._timerExpired, this._wait);\n if (this._leading) {\n this._invoke(now);\n }\n }\n\n private _trailingEdge(now: number): void {\n this._timerId = null;\n // Only fire when there is an unconsumed payload (a call arrived since the last\n // invoke). After a lone leading fire `_pendingKind` is null, so a single pulse\n // does not double-fire on its trailing edge.\n if (this._trailing && this._pendingKind !== null) {\n this._invoke(now);\n }\n this._pendingKind = null;\n this._pendingValue = undefined;\n this._pendingArgs = undefined;\n this._setPending(false);\n }\n\n private _invoke(now: number): void {\n this._lastInvokeTime = now;\n const kind = this._pendingKind;\n if (kind === \"value\") {\n this._value = this._pendingValue;\n this._dispatch(`${this._prefix}:settled`, { value: this._value });\n } else if (kind === \"signal\") {\n this._lastArgs = this._pendingArgs ?? [];\n this._dispatch(`${this._prefix}:fired`, { args: this._lastArgs });\n }\n // Mark consumed (mirrors lodash clearing `lastArgs`).\n this._pendingKind = null;\n }\n\n private _setPending(pending: boolean): void {\n if (this._pending === pending) return;\n this._pending = pending;\n this._dispatch(`${this._prefix}:pending-changed`, pending);\n }\n\n private _dispatch(type: string, detail: any): void {\n this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));\n }\n\n private _clearTimer(): void {\n if (this._timerId !== null) {\n clearTimeout(this._timerId);\n this._timerId = null;\n }\n }\n}\n","import { config } from \"./config.js\";\n\nlet registered = false;\n\ninterface Triggerable extends Element {\n trigger(...args: unknown[]): void;\n}\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const id = triggerElement.getAttribute(config.triggerAttribute);\n if (!id) return;\n\n // Resolve the registered constructors at call time (avoids a components ⇄\n // autoTrigger import cycle, mirroring <wcs-timer>). The DOM trigger fires a\n // single coalesced pulse on the referenced <wcs-debounce> / <wcs-throttle>.\n const DebounceCtor = customElements.get(config.tagNames.debounce);\n const ThrottleCtor = customElements.get(config.tagNames.throttle);\n const el = document.getElementById(id);\n if (!el) return;\n const isDebounce = DebounceCtor && el instanceof DebounceCtor;\n const isThrottle = ThrottleCtor && el instanceof ThrottleCtor;\n if (!isDebounce && !isThrottle) return;\n\n // Suppress the element's default action so a debounce can fire without\n // navigating. See README \"Optional DOM Triggering\".\n event.preventDefault();\n (el as unknown as Triggerable).trigger();\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable, DebounceOptions } from \"../types.js\";\nimport { DebounceCore } from \"../core/DebounceCore.js\";\nimport { makeDebounceProperties } from \"../wcBindableFactory.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\nconst DEFAULT_WAIT = 250;\n\n/**\n * `<wcs-debounce>` — declarative debounce. See {@link DebounceCore} for the\n * engine. The `eventPrefix` defaults to `\"wcs-debounce\"`; `<wcs-throttle>`\n * subclasses this with `\"wcs-throttle\"` and throttle defaults.\n */\nexport class Debounce extends HTMLElement {\n static hasConnectedCallbackPromise = false;\n protected static eventPrefix = \"wcs-debounce\";\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: makeDebounceProperties(\"wcs-debounce\"),\n // `source` is the value-surface input (its debounced echo comes back on the\n // `value` property). `trigger` / `cancel` / `flush` are commands from the\n // Core. No momentary boolean `trigger` property exists — the signal surface\n // is driven by the `trigger` command (command-token) or a DOM click\n // (autoTrigger), which sidesteps the attribute/method name clash that forced\n // <wcs-geo>'s watch → watchPosition rename.\n inputs: [\n { name: \"source\" },\n { name: \"wait\", attribute: \"wait\" },\n { name: \"leading\", attribute: \"leading\" },\n { name: \"trailing\", attribute: \"trailing\" },\n { name: \"maxWait\", attribute: \"max-wait\" },\n ],\n commands: [\n { name: \"trigger\" },\n { name: \"cancel\" },\n { name: \"flush\" },\n ],\n };\n\n protected _core: DebounceCore;\n private _source: any = undefined;\n\n constructor() {\n super();\n this._core = new DebounceCore((this.constructor as typeof Debounce).eventPrefix, this);\n }\n\n // --- Attribute accessors ---\n\n get wait(): number {\n const attr = this.getAttribute(\"wait\");\n if (attr === null || attr.trim() === \"\") return this._defaultWait();\n // Strict parse via Number() (\"100px\" -> NaN, not 100). Fall back to the\n // default for any non-finite or negative value.\n const parsed = Number(attr);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : this._defaultWait();\n }\n\n set wait(value: number) {\n this.setAttribute(\"wait\", String(value));\n }\n\n get leading(): boolean {\n return this.hasAttribute(\"leading\");\n }\n\n set leading(value: boolean) {\n if (value) {\n this.setAttribute(\"leading\", \"\");\n } else {\n this.removeAttribute(\"leading\");\n }\n }\n\n // `trailing` defaults to true; the boolean `no-trailing` attribute opts out (a\n // bare `trailing` attribute can't express \"false\", so the negative flag carries\n // the override).\n get trailing(): boolean {\n return !this.hasAttribute(\"no-trailing\");\n }\n\n set trailing(value: boolean) {\n if (value) {\n this.removeAttribute(\"no-trailing\");\n } else {\n this.setAttribute(\"no-trailing\", \"\");\n }\n }\n\n get maxWait(): number | undefined {\n const attr = this.getAttribute(\"max-wait\");\n if (attr === null || attr.trim() === \"\") return this._defaultMaxWait();\n const parsed = Number(attr);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : this._defaultMaxWait();\n }\n\n set maxWait(value: number) {\n this.setAttribute(\"max-wait\", String(value));\n }\n\n // --- Value-surface input ---\n\n get source(): any {\n return this._source;\n }\n\n set source(value: any) {\n this._source = value;\n this._core.configure(this._options());\n this._core.setSource(value);\n }\n\n // --- Core delegated getters ---\n\n get value(): any {\n return this._core.value;\n }\n\n get fired(): any[] {\n return this._core.fired;\n }\n\n get pending(): boolean {\n return this._core.pending;\n }\n\n // --- Commands ---\n\n trigger(...args: any[]): void {\n this._core.configure(this._options());\n this._core.trigger(...args);\n }\n\n cancel(): void {\n this._core.cancel();\n }\n\n flush(): void {\n this._core.flush();\n }\n\n // --- Internal ---\n\n // Overridden by <wcs-throttle> to bias the defaults toward throttle (leading\n // on, maxWait pinned to wait).\n protected _defaultWait(): number {\n return DEFAULT_WAIT;\n }\n\n // Resolves the effective `leading` value (not a static default). It is a method\n // rather than reading `this.leading` directly because <wcs-throttle> inverts the\n // default (on, opt out via `no-leading`) while sharing the inherited `leading`\n // attribute setter — overriding the getter alone would desync getter and setter.\n protected _resolveLeading(): boolean {\n return this.leading;\n }\n\n protected _defaultMaxWait(): number | undefined {\n return undefined;\n }\n\n protected _options(): DebounceOptions {\n return {\n wait: this.wait,\n leading: this._resolveLeading(),\n trailing: this.trailing,\n maxWait: this.maxWait,\n };\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n }\n\n disconnectedCallback(): void {\n // Drop any in-flight timer so a detached element leaks nothing.\n this._core.cancel();\n }\n}\n","import { IWcBindable } from \"../types.js\";\nimport { Debounce } from \"./Debounce.js\";\nimport { makeDebounceProperties } from \"../wcBindableFactory.js\";\n\n/**\n * `<wcs-throttle>` — the same {@link DebounceCore} engine biased to throttle:\n * `maxWait === wait` (a fire happens at least every `wait` ms under continuous\n * input) and `leading` on by default. It advertises its own `wcs-throttle:*`\n * event namespace (via `makeDebounceProperties(\"wcs-throttle\")`), and the Core\n * dispatches under that prefix because the constructor passes it through.\n */\nexport class Throttle extends Debounce {\n protected static eventPrefix = \"wcs-throttle\";\n static wcBindable: IWcBindable = {\n ...Debounce.wcBindable,\n properties: makeDebounceProperties(\"wcs-throttle\"),\n };\n\n // leading defaults on for throttle; `no-leading` opts out (symmetric with the\n // inherited `no-trailing`).\n protected _resolveLeading(): boolean {\n return !this.hasAttribute(\"no-leading\");\n }\n\n // Pin maxWait to wait so throttle fires on a steady cadence; an explicit\n // `max-wait` attribute still overrides via the inherited getter.\n protected _defaultMaxWait(): number | undefined {\n return this.wait;\n }\n}\n","import { Debounce } from \"./components/Debounce.js\";\nimport { Throttle } from \"./components/Throttle.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.debounce)) {\n customElements.define(config.tagNames.debounce, Debounce);\n }\n if (!customElements.get(config.tagNames.throttle)) {\n customElements.define(config.tagNames.throttle, Throttle);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapDebounce(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const t={autoTrigger:!0,triggerAttribute:"data-debouncetarget",tagNames:{debounce:"wcs-debounce",throttle:"wcs-throttle"}};function e(t){if(null===t||"object"!=typeof t)return t;Object.freeze(t);for(const i of Object.keys(t))e(t[i]);return t}function i(t){if(null===t||"object"!=typeof t)return t;const e={};for(const n of Object.keys(t))e[n]=i(t[n]);return e}let n=null;const s=t;function a(){return n||(n=e(i(t))),n}function r(t){return[{name:"value",event:`${t}:settled`,getter:t=>t.detail.value},{name:"fired",event:`${t}:fired`,getter:t=>t.detail.args},{name:"pending",event:`${t}:pending-changed`}]}class l extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:r("wcs-debounce"),commands:[{name:"trigger"},{name:"cancel"},{name:"flush"}]};_prefix;_target;_wait=0;_leading=!1;_trailing=!0;_maxWait=0;_hasMaxWait=!1;_lastCallTime=void 0;_lastInvokeTime=0;_timerId=null;_pendingKind=null;_pendingValue=void 0;_pendingArgs=void 0;_value=void 0;_lastArgs=[];_pending=!1;constructor(t="wcs-debounce",e,i){super(),this._prefix=t,this._target=e??this,i&&this.configure(i)}configure(t={}){"number"==typeof t.wait&&Number.isFinite(t.wait)&&t.wait>=0&&(this._wait=t.wait),"boolean"==typeof t.leading&&(this._leading=t.leading),"boolean"==typeof t.trailing&&(this._trailing=t.trailing);const e=t.maxWait;"number"==typeof e&&Number.isFinite(e)&&e>=0?(this._maxWait=Math.max(e,this._wait),this._hasMaxWait=!0):(this._maxWait=0,this._hasMaxWait=!1)}get value(){return this._value}get fired(){return this._lastArgs}get pending(){return this._pending}setSource(t){this._schedule("value",t,void 0)}trigger(...t){this._schedule("signal",void 0,t)}cancel(){this._clearTimer(),this._lastInvokeTime=0,this._lastCallTime=void 0,this._pendingKind=null,this._pendingValue=void 0,this._pendingArgs=void 0,this._setPending(!1)}flush(){if(null===this._timerId&&null===this._pendingKind)return;const t=Date.now();this._clearTimer(),null!==this._pendingKind&&this._invoke(t),this._pendingKind=null,this._pendingValue=void 0,this._pendingArgs=void 0,this._setPending(!1)}_schedule(t,e,i){const n=Date.now(),s=this._shouldInvoke(n);if(this._pendingKind=t,"value"===t?this._pendingValue=e:this._pendingArgs=i,this._lastCallTime=n,this._setPending(!0),s){if(null===this._timerId)return void this._leadingEdge(n);if(this._hasMaxWait)return this._clearTimer(),this._timerId=setTimeout(this._timerExpired,this._wait),void this._invoke(n)}null===this._timerId&&(this._timerId=setTimeout(this._timerExpired,this._wait))}_shouldInvoke(t){if(void 0===this._lastCallTime)return!0;const e=t-this._lastCallTime,i=t-this._lastInvokeTime;return e>=this._wait||e<0||this._hasMaxWait&&i>=this._maxWait}_remainingWait(t){const e=t-this._lastCallTime,i=t-this._lastInvokeTime,n=this._wait-e;return this._hasMaxWait?Math.min(n,this._maxWait-i):n}_timerExpired=()=>{const t=Date.now();this._shouldInvoke(t)?this._trailingEdge(t):this._timerId=setTimeout(this._timerExpired,this._remainingWait(t))};_leadingEdge(t){this._lastInvokeTime=t,this._timerId=setTimeout(this._timerExpired,this._wait),this._leading&&this._invoke(t)}_trailingEdge(t){this._timerId=null,this._trailing&&null!==this._pendingKind&&this._invoke(t),this._pendingKind=null,this._pendingValue=void 0,this._pendingArgs=void 0,this._setPending(!1)}_invoke(t){this._lastInvokeTime=t;const e=this._pendingKind;"value"===e?(this._value=this._pendingValue,this._dispatch(`${this._prefix}:settled`,{value:this._value})):"signal"===e&&(this._lastArgs=this._pendingArgs??[],this._dispatch(`${this._prefix}:fired`,{args:this._lastArgs})),this._pendingKind=null}_setPending(t){this._pending!==t&&(this._pending=t,this._dispatch(`${this._prefix}:pending-changed`,t))}_dispatch(t,e){this._target.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0}))}_clearTimer(){null!==this._timerId&&(clearTimeout(this._timerId),this._timerId=null)}}let o=!1;function h(t){const e=t.target;if(!(e instanceof Element))return;const i=e.closest(`[${s.triggerAttribute}]`);if(!i)return;const n=i.getAttribute(s.triggerAttribute);if(!n)return;const a=customElements.get(s.tagNames.debounce),r=customElements.get(s.tagNames.throttle),l=document.getElementById(n);if(!l)return;(a&&l instanceof a||r&&l instanceof r)&&(t.preventDefault(),l.trigger())}class u extends HTMLElement{static hasConnectedCallbackPromise=!1;static eventPrefix="wcs-debounce";static wcBindable={protocol:"wc-bindable",version:1,properties:r("wcs-debounce"),inputs:[{name:"source"},{name:"wait",attribute:"wait"},{name:"leading",attribute:"leading"},{name:"trailing",attribute:"trailing"},{name:"maxWait",attribute:"max-wait"}],commands:[{name:"trigger"},{name:"cancel"},{name:"flush"}]};_core;_source=void 0;constructor(){super(),this._core=new l(this.constructor.eventPrefix,this)}get wait(){const t=this.getAttribute("wait");if(null===t||""===t.trim())return this._defaultWait();const e=Number(t);return Number.isFinite(e)&&e>=0?e:this._defaultWait()}set wait(t){this.setAttribute("wait",String(t))}get leading(){return this.hasAttribute("leading")}set leading(t){t?this.setAttribute("leading",""):this.removeAttribute("leading")}get trailing(){return!this.hasAttribute("no-trailing")}set trailing(t){t?this.removeAttribute("no-trailing"):this.setAttribute("no-trailing","")}get maxWait(){const t=this.getAttribute("max-wait");if(null===t||""===t.trim())return this._defaultMaxWait();const e=Number(t);return Number.isFinite(e)&&e>=0?e:this._defaultMaxWait()}set maxWait(t){this.setAttribute("max-wait",String(t))}get source(){return this._source}set source(t){this._source=t,this._core.configure(this._options()),this._core.setSource(t)}get value(){return this._core.value}get fired(){return this._core.fired}get pending(){return this._core.pending}trigger(...t){this._core.configure(this._options()),this._core.trigger(...t)}cancel(){this._core.cancel()}flush(){this._core.flush()}_defaultWait(){return 250}_resolveLeading(){return this.leading}_defaultMaxWait(){}_options(){return{wait:this.wait,leading:this._resolveLeading(),trailing:this.trailing,maxWait:this.maxWait}}connectedCallback(){this.style.display="none",s.autoTrigger&&(o||(o=!0,document.addEventListener("click",h)))}disconnectedCallback(){this._core.cancel()}}class g extends u{static eventPrefix="wcs-throttle";static wcBindable={...u.wcBindable,properties:r("wcs-throttle")};_resolveLeading(){return!this.hasAttribute("no-leading")}_defaultMaxWait(){return this.wait}}function d(e){var i;e&&("boolean"==typeof(i=e).autoTrigger&&(t.autoTrigger=i.autoTrigger),"string"==typeof i.triggerAttribute&&(t.triggerAttribute=i.triggerAttribute),i.tagNames&&Object.assign(t.tagNames,i.tagNames),n=null),customElements.get(s.tagNames.debounce)||customElements.define(s.tagNames.debounce,u),customElements.get(s.tagNames.throttle)||customElements.define(s.tagNames.throttle,g)}export{l as DebounceCore,u as WcsDebounce,g as WcsThrottle,d as bootstrapDebounce,a as getConfig,r as makeDebounceProperties};
|
|
2
|
+
//# sourceMappingURL=index.esm.min.js.map
|