jj 2.7.2 → 2.9.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/SKILL.md CHANGED
@@ -60,6 +60,8 @@ const nav = h(
60
60
 
61
61
  // Append to document body
62
62
  doc.body.ref.append(nav.ref)
63
+ // Or using the wrapper API
64
+ doc.body.addChild(nav.ref)
63
65
  ```
64
66
 
65
67
  For batch DOM operations, use `JJDF` (DocumentFragment) to avoid multiple reflows:
@@ -173,6 +175,20 @@ el.style('background-color', 'blue').style('padding', '10px')
173
175
  el.setAria('hidden', 'true').rmAria('label')
174
176
  ```
175
177
 
178
+ Node traversal and detach helpers on `JJN`:
179
+
180
+ ```typescript
181
+ const item = doc.find('#item', true)
182
+ const parent = item.parent // wrapped parent or null
183
+ const children = item.children // wrapped child nodes
184
+
185
+ children.forEach((child) => {
186
+ console.log(child.ref)
187
+ })
188
+
189
+ item.rm() // detach from its current parent
190
+ ```
191
+
176
192
  ### 3. Type-Safe Element Creation
177
193
 
178
194
  JJ leverages TypeScript generics to ensure type safety and prevent LLM hallucinations:
@@ -614,8 +630,10 @@ Output: `doc/` folder with HTML documentation
614
630
  1. **Accessing the Native Node**: Always use `.ref` to access the underlying DOM node for operations not exposed by JJ
615
631
  2. **Event Listeners**: Use `.on()` and `.off()` for proper cleanup. Event handlers are automatically bound to the JJ\* instance, so `this` refers to the wrapper, not the DOM element. Use `function` instead of arrow functions (`=>`) for `.bind()` to work correctly. Use `this.ref` to access the native element.
616
632
  3. **Type Safety**: Use `JJHE.create()` for automatic type inference instead of generic parameters
617
- 4. **Fragments**: Use JJDF for batch operations before appending
618
- 5. **Error Messages**: Read them carefully—they suggest the correct approach
633
+ 4. **Tree Navigation**: Use `node.parent` and `node.children` to stay in JJ wrappers while traversing the DOM tree
634
+ 5. **Detaching Nodes**: Use `node.rm()` instead of reaching for native removal APIs unless you specifically need them
635
+ 6. **Fragments**: Use JJDF for batch operations before appending
636
+ 7. **Error Messages**: Read them carefully—they suggest the correct approach
619
637
 
620
638
  ## Anti-patterns to Avoid
621
639
 
package/lib/bundle.cjs CHANGED
@@ -354,9 +354,9 @@ var JJN = class _JJN extends JJET {
354
354
  * This is useful for filtering the array that is passed to `append()`, `prepend()` or `setChildren()`
355
355
  *
356
356
  * @param x an unknown value
357
- * @returns true if `x` is a string, Node (or its descendents), JJN (or its descendents)
357
+ * @returns true if `x` is a string, Node (or its descendent), JJN (or its descendent)
358
358
  */
359
- static isWrapable(x) {
359
+ static isWrappable(x) {
360
360
  return isStr(x) || isA(x, Node) || isA(x, _JJN);
361
361
  }
362
362
  /**
@@ -365,6 +365,7 @@ var JJN = class _JJN extends JJET {
365
365
  * @remarks
366
366
  * This function acts as a factory, inspecting the input type and returning the appropriate
367
367
  * subclass of `JJN` (e.g., `JJHE` for `HTMLElement`, `JJT` for `Text`).
368
+ * JJN.ts overrides this method to a richer version that handles all subclasses of JJN.
368
369
  *
369
370
  * @example
370
371
  * ```ts
@@ -377,7 +378,15 @@ var JJN = class _JJN extends JJET {
377
378
  * @throws {TypeError} If the input is not a Node, string, or JJ wrapper.
378
379
  */
379
380
  static wrap(raw) {
380
- throw new ReferenceError(`The mixin is supposed to override this method.`);
381
+ if (isObj(raw)) {
382
+ if (isA(raw, _JJN)) {
383
+ return raw;
384
+ }
385
+ if (isA(raw, Node)) {
386
+ return new _JJN(raw);
387
+ }
388
+ }
389
+ throw typeErr("raw", "a Node", raw);
381
390
  }
382
391
  /**
383
392
  * Extracts the underlying native DOM node from a wrapper.
@@ -454,6 +463,44 @@ var JJN = class _JJN extends JJET {
454
463
  }
455
464
  super(ref);
456
465
  }
466
+ /**
467
+ * Gets the parent node wrapped in the most specific JJ wrapper available.
468
+ *
469
+ * @remarks
470
+ * Returns `null` when this node is detached and therefore has no parent.
471
+ *
472
+ * @example
473
+ * ```ts
474
+ * const text = JJT.fromStr('hello')
475
+ * JJHE.create('div').addChild(text)
476
+ * const parent = text.parent // JJHE
477
+ * ```
478
+ *
479
+ * @returns The wrapped parent node, or `null` if this node has no parent.
480
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/parentNode | Node.parentNode}
481
+ */
482
+ get parent() {
483
+ const { parentNode } = this.ref;
484
+ return parentNode ? _JJN.wrap(parentNode) : null;
485
+ }
486
+ /**
487
+ * Gets the child nodes wrapped in the most specific JJ wrappers available.
488
+ *
489
+ * @remarks
490
+ * Returns an empty array when this node has no children.
491
+ *
492
+ * @example
493
+ * ```ts
494
+ * const el = JJHE.create('div').addChild('hello', JJHE.create('span'))
495
+ * const children = el.children // [JJT, JJHE]
496
+ * ```
497
+ *
498
+ * @returns The wrapped child nodes.
499
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes | Node.childNodes}
500
+ */
501
+ get children() {
502
+ return _JJN.wrapAll(this.ref.childNodes);
503
+ }
457
504
  /**
458
505
  * Clones the Node.
459
506
  *
@@ -464,6 +511,29 @@ var JJN = class _JJN extends JJET {
464
511
  clone(deep) {
465
512
  return _JJN.wrap(this.ref.cloneNode(deep));
466
513
  }
514
+ /**
515
+ * Removes this node from its parent.
516
+ *
517
+ * @remarks
518
+ * If the node has no parent, this method does nothing.
519
+ *
520
+ * @example
521
+ * ```ts
522
+ * const el = JJHE.create('div')
523
+ * doc.body.addChild(el)
524
+ * el.rm()
525
+ * ```
526
+ *
527
+ * @returns This instance for chaining.
528
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild | Node.removeChild}
529
+ */
530
+ rm() {
531
+ const { parentNode } = this.ref;
532
+ if (parentNode) {
533
+ parentNode.removeChild(this.ref);
534
+ }
535
+ return this;
536
+ }
467
537
  /**
468
538
  * Creates a Text node from a string and appends it to this Node.
469
539
  *
@@ -480,9 +550,9 @@ var JJN = class _JJN extends JJET {
480
550
  * @returns This instance for chaining.
481
551
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode | document.createTextNode}
482
552
  */
483
- addText(text) {
484
- if (text) {
485
- this.ref.appendChild(document.createTextNode(text));
553
+ addText(...textArr) {
554
+ if (textArr) {
555
+ this.ref.appendChild(document.createTextNode(textArr.join("")));
486
556
  }
487
557
  return this;
488
558
  }
@@ -547,7 +617,7 @@ var JJNx = class extends JJN {
547
617
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/append | Element.append}
548
618
  */
549
619
  addChild(...children) {
550
- const nodes = JJN.unwrapAll(children.filter(JJN.isWrapable));
620
+ const nodes = JJN.unwrapAll(children.filter(JJN.isWrappable));
551
621
  this.ref.append(...nodes);
552
622
  return this;
553
623
  }
@@ -567,7 +637,7 @@ var JJNx = class extends JJN {
567
637
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/prepend | Element.prepend}
568
638
  */
569
639
  preChild(...children) {
570
- const nodes = JJN.unwrapAll(children.filter(JJN.isWrapable));
640
+ const nodes = JJN.unwrapAll(children.filter(JJN.isWrappable));
571
641
  this.ref.prepend(...nodes);
572
642
  return this;
573
643
  }
@@ -623,7 +693,7 @@ var JJNx = class extends JJN {
623
693
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceChildren | Element.replaceChildren}
624
694
  */
625
695
  setChildren(...children) {
626
- const nodes = JJN.unwrapAll(children.filter(JJN.isWrapable));
696
+ const nodes = JJN.unwrapAll(children.filter(JJN.isWrappable));
627
697
  this.ref.replaceChildren(...nodes);
628
698
  return this;
629
699
  }
@@ -1385,7 +1455,7 @@ var JJHE = class _JJHE extends JJEx {
1385
1455
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText | HTMLElement.innerText}
1386
1456
  */
1387
1457
  setText(text) {
1388
- this.ref.innerText = text ?? "";
1458
+ this.ref.innerText = text;
1389
1459
  return this;
1390
1460
  }
1391
1461
  };
@@ -1457,7 +1527,7 @@ var JJT = class _JJT extends JJN {
1457
1527
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent | Node.textContent}
1458
1528
  */
1459
1529
  setText(text) {
1460
- this.ref.textContent = text ?? null;
1530
+ this.ref.textContent = text;
1461
1531
  return this;
1462
1532
  }
1463
1533
  /**
@@ -1470,14 +1540,12 @@ var JJT = class _JJT extends JJN {
1470
1540
  * console.log(text.getText()) // 'hello world'
1471
1541
  * ```
1472
1542
  *
1473
- * @param text - The string to add to the existing contents. If null or undefined, nothing is added.
1543
+ * @param textArr - The string to add to the existing contents. If null or undefined, nothing is added.
1474
1544
  * @returns This instance for chaining.
1475
1545
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent | Node.textContent}
1476
1546
  */
1477
- addText(text) {
1478
- if (text != null) {
1479
- this.ref.textContent += text;
1480
- }
1547
+ addText(...textArr) {
1548
+ this.setText(this.getText() + textArr.join(""));
1481
1549
  return this;
1482
1550
  }
1483
1551
  /**
@@ -1636,7 +1704,7 @@ var JJSE = class _JJSE extends JJEx {
1636
1704
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent | Node.textContent}
1637
1705
  */
1638
1706
  setText(text) {
1639
- this.ref.textContent = text ?? "";
1707
+ this.ref.textContent = text;
1640
1708
  return this;
1641
1709
  }
1642
1710
  /**