@jinntec/fore 3.2.0 → 3.2.1

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/dist/fore-dev.js CHANGED
@@ -1,4 +1,4 @@
1
- /* Version: 3.2.0 - June 9, 2026 10:50:39 */
1
+ /* Version: 3.2.1 - June 12, 2026 14:10:39 */
2
2
  function t$2(t, s, r, i) {
3
3
  const n = {
4
4
  op: s,
@@ -25479,7 +25479,7 @@ class FxFore extends HTMLElement {
25479
25479
  this._createRepeatsFromAttributes();
25480
25480
  this.inited = true;
25481
25481
  };
25482
- this.version = 'Version: 3.2.0 - built on June 9, 2026 10:50:39';
25482
+ this.version = 'Version: 3.2.1 - built on June 12, 2026 14:10:39';
25483
25483
 
25484
25484
  /**
25485
25485
  * @type {import('./fx-model.js').FxModel}
@@ -25708,10 +25708,21 @@ class FxFore extends HTMLElement {
25708
25708
  }
25709
25709
  return null;
25710
25710
  }
25711
- _isReadyTarget(el) {
25711
+ static _isReadyTarget(el) {
25712
25712
  return !!(el && (el.ready === true || el.classList && el.classList.contains('fx-ready') || typeof el.hasAttribute === 'function' && el.hasAttribute('ready')));
25713
25713
  }
25714
25714
 
25715
+ /**
25716
+ * Resolves once `fore` has dispatched its initial `ready` event (or
25717
+ * immediately, if it's already ready).
25718
+ */
25719
+ static waitUntilReady(fore) {
25720
+ if (FxFore._isReadyTarget(fore)) {
25721
+ return Promise.resolve();
25722
+ }
25723
+ return FxFore._waitForEvent(fore, 'ready', FxFore._isReadyTarget);
25724
+ }
25725
+
25715
25726
  /**
25716
25727
  * Collect all init gates derived from attributes.
25717
25728
  *
@@ -25748,7 +25759,7 @@ class FxFore extends HTMLElement {
25748
25759
  }
25749
25760
  return gates;
25750
25761
  }
25751
- _waitForEvent(target, eventName, isSatisfiedFn = null) {
25762
+ static _waitForEvent(target, eventName, isSatisfiedFn = null) {
25752
25763
  // If a caller provides an explicit satisfaction check, honor it first.
25753
25764
  if (typeof isSatisfiedFn === 'function' && isSatisfiedFn(target)) {
25754
25765
  FxFore._markInitEventSeen(target, eventName);
@@ -25812,19 +25823,19 @@ class FxFore extends HTMLElement {
25812
25823
  }) {
25813
25824
  // Direct targets
25814
25825
  if (targetSpec === 'self') {
25815
- const satisfied = event === 'ready' ? t => this._isReadyTarget(t) : null;
25816
- return this._waitForEvent(this, event, satisfied);
25826
+ const satisfied = event === 'ready' ? t => FxFore._isReadyTarget(t) : null;
25827
+ return FxFore._waitForEvent(this, event, satisfied);
25817
25828
  }
25818
25829
  if (targetSpec === 'document') {
25819
- return this._waitForEvent(document, event);
25830
+ return FxFore._waitForEvent(document, event);
25820
25831
  }
25821
25832
  if (targetSpec === 'window') {
25822
- return this._waitForEvent(window, event);
25833
+ return FxFore._waitForEvent(window, event);
25823
25834
  }
25824
25835
 
25825
25836
  // Special: closest fx-fore
25826
25837
  if (targetSpec === 'closest') {
25827
- const recheckFn = event === 'ready' ? () => this._isReadyTarget(this.closest('fx-fore')) : null;
25838
+ const recheckFn = event === 'ready' ? () => FxFore._isReadyTarget(this.closest('fx-fore')) : null;
25828
25839
  const matchesFn = ev => {
25829
25840
  const t = ev.target;
25830
25841
  return t?.tagName === 'FX-FORE' && t.contains(this);
@@ -25834,7 +25845,7 @@ class FxFore extends HTMLElement {
25834
25845
 
25835
25846
  // Selector targets
25836
25847
  const selector = targetSpec;
25837
- const recheckFn = event === 'ready' ? () => this._isReadyTarget(this._findBySelector(selector)) : null;
25848
+ const recheckFn = event === 'ready' ? () => FxFore._isReadyTarget(this._findBySelector(selector)) : null;
25838
25849
  if (typeof recheckFn === 'function' && recheckFn()) {
25839
25850
  return Promise.resolve();
25840
25851
  }
@@ -29681,11 +29692,26 @@ class FxInclude extends HTMLElement {
29681
29692
  this.immediate = this.hasAttribute('immediate');
29682
29693
  this.reload = this.hasAttribute('reload');
29683
29694
  if (this.immediate) {
29684
- queueMicrotask(() => this.include());
29695
+ this._includeWhenReady();
29685
29696
  return;
29686
29697
  }
29687
29698
  this._bindListener();
29688
29699
  }
29700
+
29701
+ /**
29702
+ * Includes content immediately, but waits until the closest `fx-fore` has
29703
+ * finished its initial Rebuild/Recalculate/Revalidate/Refresh cycle (i.e. is
29704
+ * "ready"), so the included content can resolve binding context against an
29705
+ * already-initialized ancestor chain.
29706
+ */
29707
+ _includeWhenReady() {
29708
+ const fore = this.closest('fx-fore');
29709
+ if (!fore) {
29710
+ queueMicrotask(() => this.include());
29711
+ return;
29712
+ }
29713
+ FxFore.waitUntilReady(fore).then(() => this.include());
29714
+ }
29689
29715
  disconnectedCallback() {
29690
29716
  this._unbindListener();
29691
29717
  }
@@ -29778,10 +29804,16 @@ class FxInclude extends HTMLElement {
29778
29804
  fragment.appendChild(document.importNode(selected, true));
29779
29805
  return fragment;
29780
29806
  }
29781
- const template = parsed.querySelector('template');
29807
+ const template = parsed.body.querySelector(':scope > template');
29782
29808
  if (template) {
29783
29809
  return document.importNode(template.content, true);
29784
29810
  }
29811
+ const rootElements = Array.from(parsed.body.children);
29812
+ if (rootElements.length === 1) {
29813
+ const fragment = document.createDocumentFragment();
29814
+ fragment.appendChild(document.importNode(rootElements[0], true));
29815
+ return fragment;
29816
+ }
29785
29817
  const fragment = document.createDocumentFragment();
29786
29818
  Array.from(parsed.body.childNodes).forEach(node => {
29787
29819
  fragment.appendChild(document.importNode(node, true));
@@ -29816,6 +29848,10 @@ class FxInclude extends HTMLElement {
29816
29848
  }
29817
29849
  async _initializeInsertedContent(startElement) {
29818
29850
  this._initForeUiDescendants(startElement);
29851
+ const fore = startElement.closest('fx-fore');
29852
+ if (fore?.createNodes && typeof fore.initData === 'function') {
29853
+ fore.initData(startElement);
29854
+ }
29819
29855
  await Fore.refreshChildren(startElement, true);
29820
29856
  }
29821
29857
  _initForeUiDescendants(startElement) {
package/dist/fore.js CHANGED
@@ -1,4 +1,4 @@
1
- /* Version: 3.2.0 - June 9, 2026 10:50:37 */
1
+ /* Version: 3.2.1 - June 12, 2026 14:10:38 */
2
2
  function t$2(t, s, r, i) {
3
3
  const n = {
4
4
  op: s,
@@ -25425,7 +25425,7 @@ class FxFore extends HTMLElement {
25425
25425
  this._createRepeatsFromAttributes();
25426
25426
  this.inited = true;
25427
25427
  };
25428
- this.version = 'Version: 3.2.0 - built on June 9, 2026 10:50:37';
25428
+ this.version = 'Version: 3.2.1 - built on June 12, 2026 14:10:38';
25429
25429
 
25430
25430
  /**
25431
25431
  * @type {import('./fx-model.js').FxModel}
@@ -25653,10 +25653,21 @@ class FxFore extends HTMLElement {
25653
25653
  }
25654
25654
  return null;
25655
25655
  }
25656
- _isReadyTarget(el) {
25656
+ static _isReadyTarget(el) {
25657
25657
  return !!(el && (el.ready === true || el.classList && el.classList.contains('fx-ready') || typeof el.hasAttribute === 'function' && el.hasAttribute('ready')));
25658
25658
  }
25659
25659
 
25660
+ /**
25661
+ * Resolves once `fore` has dispatched its initial `ready` event (or
25662
+ * immediately, if it's already ready).
25663
+ */
25664
+ static waitUntilReady(fore) {
25665
+ if (FxFore._isReadyTarget(fore)) {
25666
+ return Promise.resolve();
25667
+ }
25668
+ return FxFore._waitForEvent(fore, 'ready', FxFore._isReadyTarget);
25669
+ }
25670
+
25660
25671
  /**
25661
25672
  * Collect all init gates derived from attributes.
25662
25673
  *
@@ -25692,7 +25703,7 @@ class FxFore extends HTMLElement {
25692
25703
  }
25693
25704
  return gates;
25694
25705
  }
25695
- _waitForEvent(target, eventName, isSatisfiedFn = null) {
25706
+ static _waitForEvent(target, eventName, isSatisfiedFn = null) {
25696
25707
  // If a caller provides an explicit satisfaction check, honor it first.
25697
25708
  if (typeof isSatisfiedFn === 'function' && isSatisfiedFn(target)) {
25698
25709
  FxFore._markInitEventSeen(target, eventName);
@@ -25756,19 +25767,19 @@ class FxFore extends HTMLElement {
25756
25767
  }) {
25757
25768
  // Direct targets
25758
25769
  if (targetSpec === 'self') {
25759
- const satisfied = event === 'ready' ? t => this._isReadyTarget(t) : null;
25760
- return this._waitForEvent(this, event, satisfied);
25770
+ const satisfied = event === 'ready' ? t => FxFore._isReadyTarget(t) : null;
25771
+ return FxFore._waitForEvent(this, event, satisfied);
25761
25772
  }
25762
25773
  if (targetSpec === 'document') {
25763
- return this._waitForEvent(document, event);
25774
+ return FxFore._waitForEvent(document, event);
25764
25775
  }
25765
25776
  if (targetSpec === 'window') {
25766
- return this._waitForEvent(window, event);
25777
+ return FxFore._waitForEvent(window, event);
25767
25778
  }
25768
25779
 
25769
25780
  // Special: closest fx-fore
25770
25781
  if (targetSpec === 'closest') {
25771
- const recheckFn = event === 'ready' ? () => this._isReadyTarget(this.closest('fx-fore')) : null;
25782
+ const recheckFn = event === 'ready' ? () => FxFore._isReadyTarget(this.closest('fx-fore')) : null;
25772
25783
  const matchesFn = ev => {
25773
25784
  const t = ev.target;
25774
25785
  return t?.tagName === 'FX-FORE' && t.contains(this);
@@ -25778,7 +25789,7 @@ class FxFore extends HTMLElement {
25778
25789
 
25779
25790
  // Selector targets
25780
25791
  const selector = targetSpec;
25781
- const recheckFn = event === 'ready' ? () => this._isReadyTarget(this._findBySelector(selector)) : null;
25792
+ const recheckFn = event === 'ready' ? () => FxFore._isReadyTarget(this._findBySelector(selector)) : null;
25782
25793
  if (typeof recheckFn === 'function' && recheckFn()) {
25783
25794
  return Promise.resolve();
25784
25795
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jinntec/fore",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "Fore - declarative user interfaces in plain HTML",
5
5
  "module": "./index.js",
6
6
  "publishConfig": {
package/src/fx-fore.js CHANGED
@@ -387,7 +387,7 @@ export class FxFore extends HTMLElement {
387
387
  return null;
388
388
  }
389
389
 
390
- _isReadyTarget(el) {
390
+ static _isReadyTarget(el) {
391
391
  return !!(
392
392
  el &&
393
393
  (el.ready === true ||
@@ -396,6 +396,17 @@ export class FxFore extends HTMLElement {
396
396
  );
397
397
  }
398
398
 
399
+ /**
400
+ * Resolves once `fore` has dispatched its initial `ready` event (or
401
+ * immediately, if it's already ready).
402
+ */
403
+ static waitUntilReady(fore) {
404
+ if (FxFore._isReadyTarget(fore)) {
405
+ return Promise.resolve();
406
+ }
407
+ return FxFore._waitForEvent(fore, 'ready', FxFore._isReadyTarget);
408
+ }
409
+
399
410
  /**
400
411
  * Collect all init gates derived from attributes.
401
412
  *
@@ -433,7 +444,7 @@ export class FxFore extends HTMLElement {
433
444
  return gates;
434
445
  }
435
446
 
436
- _waitForEvent(target, eventName, isSatisfiedFn = null) {
447
+ static _waitForEvent(target, eventName, isSatisfiedFn = null) {
437
448
  // If a caller provides an explicit satisfaction check, honor it first.
438
449
  if (typeof isSatisfiedFn === 'function' && isSatisfiedFn(target)) {
439
450
  FxFore._markInitEventSeen(target, eventName);
@@ -495,20 +506,20 @@ export class FxFore extends HTMLElement {
495
506
  _waitForInitGate({ event, targetSpec }) {
496
507
  // Direct targets
497
508
  if (targetSpec === 'self') {
498
- const satisfied = event === 'ready' ? t => this._isReadyTarget(t) : null;
499
- return this._waitForEvent(this, event, satisfied);
509
+ const satisfied = event === 'ready' ? t => FxFore._isReadyTarget(t) : null;
510
+ return FxFore._waitForEvent(this, event, satisfied);
500
511
  }
501
512
  if (targetSpec === 'document') {
502
- return this._waitForEvent(document, event);
513
+ return FxFore._waitForEvent(document, event);
503
514
  }
504
515
  if (targetSpec === 'window') {
505
- return this._waitForEvent(window, event);
516
+ return FxFore._waitForEvent(window, event);
506
517
  }
507
518
 
508
519
  // Special: closest fx-fore
509
520
  if (targetSpec === 'closest') {
510
521
  const recheckFn =
511
- event === 'ready' ? () => this._isReadyTarget(this.closest('fx-fore')) : null;
522
+ event === 'ready' ? () => FxFore._isReadyTarget(this.closest('fx-fore')) : null;
512
523
 
513
524
  const matchesFn = ev => {
514
525
  const t = ev.target;
@@ -522,7 +533,7 @@ export class FxFore extends HTMLElement {
522
533
  const selector = targetSpec;
523
534
 
524
535
  const recheckFn =
525
- event === 'ready' ? () => this._isReadyTarget(this._findBySelector(selector)) : null;
536
+ event === 'ready' ? () => FxFore._isReadyTarget(this._findBySelector(selector)) : null;
526
537
 
527
538
  if (typeof recheckFn === 'function' && recheckFn()) {
528
539
  return Promise.resolve();
@@ -1,4 +1,5 @@
1
1
  import { Fore } from '../fore.js';
2
+ import { FxFore } from '../fx-fore.js';
2
3
 
3
4
  /**
4
5
  * <fx-include>
@@ -50,13 +51,30 @@ export class FxInclude extends HTMLElement {
50
51
  this.reload = this.hasAttribute('reload');
51
52
 
52
53
  if (this.immediate) {
53
- queueMicrotask(() => this.include());
54
+ this._includeWhenReady();
54
55
  return;
55
56
  }
56
57
 
57
58
  this._bindListener();
58
59
  }
59
60
 
61
+ /**
62
+ * Includes content immediately, but waits until the closest `fx-fore` has
63
+ * finished its initial Rebuild/Recalculate/Revalidate/Refresh cycle (i.e. is
64
+ * "ready"), so the included content can resolve binding context against an
65
+ * already-initialized ancestor chain.
66
+ */
67
+ _includeWhenReady() {
68
+ const fore = this.closest('fx-fore');
69
+
70
+ if (!fore) {
71
+ queueMicrotask(() => this.include());
72
+ return;
73
+ }
74
+
75
+ FxFore.waitUntilReady(fore).then(() => this.include());
76
+ }
77
+
60
78
  disconnectedCallback() {
61
79
  this._unbindListener();
62
80
  }
@@ -182,12 +200,20 @@ export class FxInclude extends HTMLElement {
182
200
  return fragment;
183
201
  }
184
202
 
185
- const template = parsed.querySelector('template');
203
+ const template = parsed.body.querySelector(':scope > template');
186
204
 
187
205
  if (template) {
188
206
  return document.importNode(template.content, true);
189
207
  }
190
208
 
209
+ const rootElements = Array.from(parsed.body.children);
210
+
211
+ if (rootElements.length === 1) {
212
+ const fragment = document.createDocumentFragment();
213
+ fragment.appendChild(document.importNode(rootElements[0], true));
214
+ return fragment;
215
+ }
216
+
191
217
  const fragment = document.createDocumentFragment();
192
218
 
193
219
  Array.from(parsed.body.childNodes).forEach(node => {
@@ -234,6 +260,12 @@ export class FxInclude extends HTMLElement {
234
260
 
235
261
  async _initializeInsertedContent(startElement) {
236
262
  this._initForeUiDescendants(startElement);
263
+
264
+ const fore = startElement.closest('fx-fore');
265
+ if (fore?.createNodes && typeof fore.initData === 'function') {
266
+ fore.initData(startElement);
267
+ }
268
+
237
269
  await Fore.refreshChildren(startElement, true);
238
270
  }
239
271