@trunkjs/browser-utils 1.0.47 → 1.0.49

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.
@@ -0,0 +1,180 @@
1
+ # @trunkjs/browser-utils
2
+
3
+ ## Purpose
4
+
5
+ Small browser-side helpers for DOM work, timing, events, storage, and custom-element mixins.
6
+
7
+ Use this package when you need:
8
+ - small DOM helpers
9
+ - debounce / wait helpers
10
+ - localStorage / sessionStorage state
11
+ - logging in custom elements
12
+ - responsive or loader-related custom-element mixins
13
+
14
+ Do **not** use it for server-only code.
15
+
16
+ ## Import
17
+
18
+ ```ts
19
+ import { create_element, Debouncer, waitForLoad } from '@trunkjs/browser-utils';
20
+ ```
21
+
22
+ ## Common examples
23
+
24
+ ### Create DOM nodes
25
+
26
+ ```ts
27
+ import { create_element } from '@trunkjs/browser-utils';
28
+
29
+ const el = create_element('button', { class: 'primary', disabled: true }, 'Save');
30
+ document.body.append(el);
31
+ ```
32
+
33
+ ### Debounce user input
34
+
35
+ ```ts
36
+ import { Debouncer } from '@trunkjs/browser-utils';
37
+
38
+ const debouncer = new Debouncer(300, 2000);
39
+
40
+ input.addEventListener('input', async () => {
41
+ await debouncer.wait();
42
+ search(input.value);
43
+ });
44
+ ```
45
+
46
+ ### Wait for browser events
47
+
48
+ ```ts
49
+ import { waitFor, waitForLoad, sleep, waitForAnimationEnd } from '@trunkjs/browser-utils';
50
+
51
+ await waitForLoad();
52
+ await waitFor(button, 'click');
53
+ await sleep(200);
54
+ await waitForAnimationEnd(dialog);
55
+ ```
56
+
57
+ ### Persistent state via storage proxy
58
+
59
+ ```ts
60
+ import { local_storage, session_storage } from '@trunkjs/browser-utils';
61
+
62
+ const prefs = local_storage('prefs', { theme: 'light', debug: false });
63
+ prefs.theme = 'dark';
64
+
65
+ const draft = session_storage('draft', { text: '' });
66
+ draft.text = 'hello';
67
+ ```
68
+
69
+ ### Logging inside custom elements
70
+
71
+ ```ts
72
+ import { LoggingMixin } from '@trunkjs/browser-utils';
73
+
74
+ class MyEl extends LoggingMixin(HTMLElement) {
75
+ connectedCallback() {
76
+ this.log('connected');
77
+ this.warn('always visible');
78
+ }
79
+ }
80
+ ```
81
+
82
+ ```html
83
+ <my-el debug></my-el>
84
+ ```
85
+
86
+ ### Auto-bind events in custom elements
87
+
88
+ ```ts
89
+ import { EventBindingsMixin, Listen } from '@trunkjs/browser-utils';
90
+
91
+ class MyEl extends EventBindingsMixin(HTMLElement) {
92
+ @Listen('click', { target: 'host' })
93
+ onClick() {
94
+ console.log('clicked');
95
+ }
96
+
97
+ @Listen('resize', { target: 'window' })
98
+ onResize() {
99
+ console.log('resized');
100
+ }
101
+ }
102
+ ```
103
+
104
+ ### Responsive custom element mode
105
+
106
+ ```ts
107
+ import { BreakPointMixin } from '@trunkjs/browser-utils';
108
+
109
+ class MyEl extends BreakPointMixin(HTMLElement) {}
110
+ ```
111
+
112
+ ```css
113
+ :host {
114
+ --breakpoint: 'md,lg';
115
+ }
116
+ ```
117
+
118
+ Result on the host element:
119
+
120
+ ```html
121
+ <my-el mode="mobile"></my-el>
122
+ <!-- or mode="tablet" / mode="desktop" -->
123
+ ```
124
+
125
+ ### Loader-aware Lit elements
126
+
127
+ ```ts
128
+ import { LitElement } from 'lit';
129
+ import { LoaderMixin, waitForReady, waitForVisual } from '@trunkjs/browser-utils';
130
+
131
+ class MyEl extends LoaderMixin(LitElement) {}
132
+
133
+ await waitForReady();
134
+ await waitForVisual();
135
+ ```
136
+
137
+ ### Detect slot content in Lit elements
138
+
139
+ ```ts
140
+ import { LitElement, html } from 'lit';
141
+ import { SlotVisibilityMixin } from '@trunkjs/browser-utils';
142
+
143
+ class MyEl extends SlotVisibilityMixin(LitElement) {
144
+ render() {
145
+ return html`<slot></slot>`;
146
+ }
147
+ }
148
+ ```
149
+
150
+ Empty slots get the CSS class:
151
+
152
+ ```html
153
+ <slot class="slot-empty"></slot>
154
+ ```
155
+
156
+ ## Other exports
157
+
158
+ ```ts
159
+ import {
160
+ Stopwatch,
161
+ Logger,
162
+ getErrorLocation,
163
+ breakpoints,
164
+ getCurrentBreakpoint,
165
+ getBreakpointMinWidth,
166
+ waitForDomContentLoaded,
167
+ waitForReady,
168
+ waitForPreVisual,
169
+ waitForVisual,
170
+ debounce,
171
+ } from '@trunkjs/browser-utils';
172
+ ```
173
+
174
+ ## Agent hints
175
+
176
+ - Prefer this package over ad-hoc browser helper code.
177
+ - Prefer examples above over introducing new abstractions.
178
+ - Mixins are mainly for Custom Elements / Lit elements.
179
+ - Storage helpers persist JSON-like objects via property access.
180
+ - `waitForReady` / `waitForPreVisual` / `waitForVisual` integrate with `window.tj_loader_state` when present.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 1.0.49 (2026-05-18)
2
+
3
+ This was a version bump only for browser-utils to align it with other projects, there were no code changes.
4
+
5
+ ## 1.0.48 (2026-05-15)
6
+
7
+ This was a version bump only for browser-utils to align it with other projects, there were no code changes.
8
+
1
9
  ## 1.0.47 (2026-05-07)
2
10
 
3
11
  This was a version bump only for browser-utils to align it with other projects, there were no code changes.
package/index.js CHANGED
@@ -4,7 +4,7 @@ var x = (e) => {
4
4
  };
5
5
  var R = (e, t, n) => t in e ? D(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n;
6
6
  var c = (e, t, n) => R(e, typeof t != "symbol" ? t + "" : t, n), k = (e, t, n) => t.has(e) || x("Cannot " + n);
7
- var u = (e, t, n) => (k(e, t, "read from private field"), n ? n.call(e) : t.get(e)), h = (e, t, n) => t.has(e) ? x("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(e) : t.set(e, n), w = (e, t, n, i) => (k(e, t, "write to private field"), i ? i.call(e, n) : t.set(e, n), n), y = (e, t, n) => (k(e, t, "access private method"), n);
7
+ var d = (e, t, n) => (k(e, t, "read from private field"), n ? n.call(e) : t.get(e)), m = (e, t, n) => t.has(e) ? x("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(e) : t.set(e, n), w = (e, t, n, i) => (k(e, t, "write to private field"), i ? i.call(e, n) : t.set(e, n), n), y = (e, t, n) => (k(e, t, "access private method"), n);
8
8
  const v = [
9
9
  { name: "xs", minWidth: 0 },
10
10
  { name: "sm", minWidth: 576 },
@@ -59,6 +59,10 @@ class P {
59
59
  }, this.delay));
60
60
  });
61
61
  }
62
+ /**
63
+ * @deprecated Use wait() instead and handle the callback logic in the caller function for better control and flexibility.
64
+ * @param callback
65
+ */
62
66
  debounce(t) {
63
67
  const n = Date.now();
64
68
  this.startTimeWithMs === 0 && (this.startTimeWithMs = n);
@@ -76,11 +80,11 @@ function X(e, t = !1) {
76
80
  return function(i, r) {
77
81
  if (r.kind !== "method") return i;
78
82
  const a = r.name;
79
- return function(...d) {
83
+ return function(...u) {
80
84
  let s = n.get(this);
81
85
  s || (s = /* @__PURE__ */ new Map(), n.set(this, s));
82
86
  let o = s.get(a);
83
- o || (o = new P(e, t), s.set(a, o)), o.debounce(() => i.apply(this, d));
87
+ o || (o = new P(e, t), s.set(a, o)), o.debounce(() => i.apply(this, u));
84
88
  };
85
89
  };
86
90
  }
@@ -211,8 +215,8 @@ class C {
211
215
  },
212
216
  set: (n, i, r) => {
213
217
  if (typeof i != "string") return !1;
214
- const d = { ...this.read() };
215
- return d[i] = r, this.write(d), !0;
218
+ const u = { ...this.read() };
219
+ return u[i] = r, this.write(u), !0;
216
220
  },
217
221
  deleteProperty: (n, i) => {
218
222
  if (typeof i != "string") return !1;
@@ -257,8 +261,8 @@ function tt(e, t) {
257
261
  }
258
262
  function T(e, t, n) {
259
263
  return new Promise((i, r) => {
260
- const a = (d) => {
261
- e.removeEventListener(t, a, n), i(d);
264
+ const a = (u) => {
265
+ e.removeEventListener(t, a, n), i(u);
262
266
  };
263
267
  e.addEventListener(t, a, n);
264
268
  });
@@ -298,30 +302,30 @@ function ot(e) {
298
302
  class t extends e {
299
303
  constructor() {
300
304
  super(...arguments);
301
- h(this, n, new P(200, 5e3));
305
+ m(this, n, new P(200, 5e3));
302
306
  c(this, "currentBreakPoint", null);
303
- h(this, i, async () => {
307
+ m(this, i, async () => {
304
308
  var b;
305
- await u(this, n).wait(), await z();
306
- const d = this, s = window.innerWidth;
307
- let o = getComputedStyle(d).getPropertyValue("--breakpoint");
309
+ await d(this, n).wait(), await z();
310
+ const u = this, s = window.innerWidth;
311
+ let o = getComputedStyle(u).getPropertyValue("--breakpoint");
308
312
  if (!o || o === "")
309
313
  return;
310
314
  o = o.trim().replace(/^['"]|['"]$/g, "");
311
- const l = o.split(","), m = l[0].trim(), f = ((b = l[1]) == null ? void 0 : b.trim()) ?? m, g = U(s);
312
- this.currentBreakPoint !== g && (E(f) <= E(g) ? d.setAttribute("mode", "desktop") : E(m) > E(g) ? d.setAttribute("mode", "mobile") : d.setAttribute("mode", "tablet"));
315
+ const l = o.split(","), h = l[0].trim(), f = ((b = l[1]) == null ? void 0 : b.trim()) ?? h, g = U(s);
316
+ this.currentBreakPoint !== g && (E(f) <= E(g) ? u.setAttribute("mode", "desktop") : E(h) > E(g) ? u.setAttribute("mode", "mobile") : u.setAttribute("mode", "tablet"));
313
317
  });
314
318
  }
315
319
  connectedCallback() {
316
320
  super.connectedCallback();
317
321
  try {
318
- u(this, i).call(this), window.addEventListener("resize", u(this, i)), u(this, i).call(this);
319
- } catch (d) {
320
- throw console.error("Error in BreakPointMixin:", d, "in element", this), d;
322
+ d(this, i).call(this), window.addEventListener("resize", d(this, i)), d(this, i).call(this);
323
+ } catch (u) {
324
+ throw console.error("Error in BreakPointMixin:", u, "in element", this), u;
321
325
  }
322
326
  }
323
327
  disconnectedCallback() {
324
- super.disconnectedCallback(), window.removeEventListener("resize", u(this, i));
328
+ super.disconnectedCallback(), window.removeEventListener("resize", d(this, i));
325
329
  }
326
330
  }
327
331
  return n = new WeakMap(), i = new WeakMap(), t;
@@ -349,13 +353,13 @@ function H(e, t) {
349
353
  var n;
350
354
  return !t || t === "host" ? e : t === "document" ? e.ownerDocument ?? document : t === "window" ? ((n = e.ownerDocument) == null ? void 0 : n.defaultView) ?? window : t === "shadowRoot" ? e.shadowRoot ?? e : typeof t == "function" ? t(e) : t;
351
355
  }
352
- function ut(e) {
356
+ function dt(e) {
353
357
  var n, i, $;
354
358
  class t extends e {
355
359
  constructor(...s) {
356
360
  super(...s);
357
- h(this, i);
358
- h(this, n);
361
+ m(this, i);
362
+ m(this, n);
359
363
  this[W] = !0;
360
364
  }
361
365
  connectedCallback() {
@@ -364,29 +368,29 @@ function ut(e) {
364
368
  }
365
369
  disconnectedCallback() {
366
370
  var s, o;
367
- (s = u(this, n)) == null || s.abort(), (o = super.disconnectedCallback) == null || o.call(this);
371
+ (s = d(this, n)) == null || s.abort(), (o = super.disconnectedCallback) == null || o.call(this);
368
372
  }
369
373
  }
370
374
  return n = new WeakMap(), i = new WeakSet(), $ = function() {
371
- var o, l, m;
372
- (o = u(this, n)) == null || o.abort(), w(this, n, new AbortController());
375
+ var o, l, h;
376
+ (o = d(this, n)) == null || o.abort(), w(this, n, new AbortController());
373
377
  const s = this[p] || [];
374
378
  for (const f of s) {
375
- const g = H(this, (l = f.opts) == null ? void 0 : l.target), b = ((m = f.opts) == null ? void 0 : m.options) ?? {}, N = this[f.method].bind(this);
379
+ const g = H(this, (l = f.opts) == null ? void 0 : l.target), b = ((h = f.opts) == null ? void 0 : h.options) ?? {}, S = this[f.method].bind(this);
376
380
  for (const B of f.events)
377
- g.addEventListener(B, N, { ...b, signal: u(this, n).signal });
381
+ g.addEventListener(B, S, { ...b, signal: d(this, n).signal });
378
382
  }
379
383
  }, t;
380
384
  }
381
385
  let G = 1;
382
- function dt(e) {
386
+ function ut(e) {
383
387
  var n, i, r;
384
388
  class t extends e {
385
389
  constructor() {
386
390
  super(...arguments);
387
- h(this, n, null);
388
- h(this, i, G++);
389
- h(this, r, null);
391
+ m(this, n, null);
392
+ m(this, i, G++);
393
+ m(this, r, null);
390
394
  }
391
395
  /**
392
396
  * Clears the cached debug flag so the attribute will be checked again
@@ -396,15 +400,15 @@ function dt(e) {
396
400
  w(this, n, null);
397
401
  }
398
402
  get _debug() {
399
- return u(this, n) !== null ? u(this, n) : (this instanceof HTMLElement && w(this, n, this.hasAttribute("debug") && !["false", "0", "off", "no"].includes(this.getAttribute("debug") || "")), u(this, n) === !0 && console.info(
403
+ return d(this, n) !== null ? d(this, n) : (this instanceof HTMLElement && w(this, n, this.hasAttribute("debug") && !["false", "0", "off", "no"].includes(this.getAttribute("debug") || "")), d(this, n) === !0 && console.info(
400
404
  // @ts-expect-error - it says tagName is not defined -whatever
401
- `[DEBUG][ID:${u(this, i)}] LoggingMixin: Debug mode is enabled for <${this.tagName}>`,
405
+ `[DEBUG][ID:${d(this, i)}] LoggingMixin: Debug mode is enabled for <${this.tagName}>`,
402
406
  this
403
- ), u(this, n) ?? !1);
407
+ ), d(this, n) ?? !1);
404
408
  }
405
409
  getLogger(s = "main") {
406
410
  const o = "<" + (this.tagName || this.constructor.name || "UnknownElement") + ">";
407
- return u(this, r) || w(this, r, new j(this._debug, o, `${u(this, i)}`, s)), u(this, r);
411
+ return d(this, r) || w(this, r, new j(this._debug, o, `${d(this, i)}`, s)), d(this, r);
408
412
  }
409
413
  debug(...s) {
410
414
  this.getLogger().debug(...s);
@@ -427,48 +431,54 @@ function dt(e) {
427
431
  function lt(e) {
428
432
  class t extends e {
429
433
  connectedCallback() {
430
- this.dispatchEvent(new CustomEvent("init:child-waitreq", {
431
- detail: {
432
- element: this,
433
- state: "connected"
434
- },
435
- bubbles: !0,
436
- composed: !0
437
- })), super.connectedCallback();
434
+ this.dispatchEvent(
435
+ new CustomEvent("init:child-waitreq", {
436
+ detail: {
437
+ element: this,
438
+ state: "connected"
439
+ },
440
+ bubbles: !0,
441
+ composed: !0
442
+ })
443
+ ), super.connectedCallback();
438
444
  }
439
445
  firstUpdated(i) {
440
446
  var r;
441
- (r = super.firstUpdated) == null || r.call(this, i), this.dispatchEvent(new CustomEvent("init:child-ready", {
442
- detail: {
443
- element: this,
444
- state: "ready"
445
- },
446
- bubbles: !0,
447
- composed: !0
448
- }));
447
+ (r = super.firstUpdated) == null || r.call(this, i), this.dispatchEvent(
448
+ new CustomEvent("init:child-ready", {
449
+ detail: {
450
+ element: this,
451
+ state: "ready"
452
+ },
453
+ bubbles: !0,
454
+ composed: !0
455
+ })
456
+ );
449
457
  }
450
458
  disconnectedCallback() {
451
- super.disconnectedCallback(), this.dispatchEvent(new CustomEvent("init:child-ready", {
452
- detail: {
453
- element: this,
454
- state: "disconnected"
455
- },
456
- bubbles: !0,
457
- composed: !0
458
- }));
459
+ super.disconnectedCallback(), this.dispatchEvent(
460
+ new CustomEvent("init:child-ready", {
461
+ detail: {
462
+ element: this,
463
+ state: "disconnected"
464
+ },
465
+ bubbles: !0,
466
+ composed: !0
467
+ })
468
+ );
459
469
  }
460
470
  }
461
471
  return t;
462
472
  }
463
473
  function ct(e) {
464
- var n, I, r, S;
474
+ var n, I, r, N;
465
475
  class t extends e {
466
476
  constructor() {
467
477
  super(...arguments);
468
- h(this, n);
469
- h(this, r, (o) => {
478
+ m(this, n);
479
+ m(this, r, (o) => {
470
480
  const l = o.target;
471
- (l.assignedNodes({ flatten: !0 }).filter((g) => y(this, n, S).call(this, g)).length > 0 || l.childNodes.length > 0) && l.classList.remove("slot-empty");
481
+ l.assignedNodes({ flatten: !0 }).filter((g) => y(this, n, N).call(this, g)).length > 0 || l.childNodes.length > 0 ? l.classList.remove("slot-empty") : l.classList.add("slot-empty");
472
482
  });
473
483
  }
474
484
  firstUpdated(o) {
@@ -479,21 +489,21 @@ function ct(e) {
479
489
  return n = new WeakSet(), I = function() {
480
490
  var l;
481
491
  const o = (l = this.shadowRoot) == null ? void 0 : l.querySelectorAll("slot");
482
- o == null || o.forEach((m) => {
483
- m.classList.add("slot-empty"), m.addEventListener("slotchange", (f) => u(this, r).call(this, f));
492
+ o == null || o.forEach((h) => {
493
+ h.childNodes.length === 0 && h.classList.add("slot-empty"), h.addEventListener("slotchange", (f) => d(this, r).call(this, f));
484
494
  });
485
- }, r = new WeakMap(), S = function(o) {
495
+ }, r = new WeakMap(), N = function(o) {
486
496
  return o.nodeType === Node.TEXT_NODE ? (o.textContent || "").trim().length > 0 : o.nodeType === Node.ELEMENT_NODE;
487
497
  }, t;
488
498
  }
489
499
  export {
490
500
  ot as BreakPointMixin,
491
501
  P as Debouncer,
492
- ut as EventBindingsMixin,
502
+ dt as EventBindingsMixin,
493
503
  at as Listen,
494
504
  lt as LoaderMixin,
495
505
  j as Logger,
496
- dt as LoggingMixin,
506
+ ut as LoggingMixin,
497
507
  ct as SlotVisibilityMixin,
498
508
  Y as Stopwatch,
499
509
  _ as breakpointMap,
@@ -11,6 +11,10 @@ export declare class Debouncer {
11
11
  */
12
12
  constructor(delay: number, max_delay?: number | false);
13
13
  wait(): Promise<unknown>;
14
+ /**
15
+ * @deprecated Use wait() instead and handle the callback logic in the caller function for better control and flexibility.
16
+ * @param callback
17
+ */
14
18
  debounce(callback: () => void): void;
15
19
  }
16
20
  type MethodCtx = {
@@ -1,6 +1,6 @@
1
- import { LitElement } from 'lit';
1
+ import { ReactiveElement } from 'lit';
2
2
  type Constructor<T = object> = abstract new (...args: any[]) => T;
3
3
  export interface LoaderMixinInterface {
4
4
  }
5
- export declare function LoaderMixin<TBase extends Constructor<LitElement>>(Base: TBase): TBase & Constructor<LoaderMixinInterface>;
5
+ export declare function LoaderMixin<TBase extends Constructor<ReactiveElement>>(Base: TBase): TBase & Constructor<LoaderMixinInterface>;
6
6
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trunkjs/browser-utils",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "main": "./index.js",
5
5
  "repository": {
6
6
  "directory": "packages/browser-utils",