native-document 1.0.92 → 1.0.93

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.
Files changed (68) hide show
  1. package/dist/native-document.components.min.js +1088 -65
  2. package/dist/native-document.dev.js +695 -142
  3. package/dist/native-document.dev.js.map +1 -1
  4. package/dist/native-document.devtools.min.js +1 -1
  5. package/dist/native-document.min.js +1 -1
  6. package/docs/advanced-components.md +814 -0
  7. package/docs/anchor.md +71 -11
  8. package/docs/cache.md +888 -0
  9. package/docs/conditional-rendering.md +91 -1
  10. package/docs/core-concepts.md +9 -2
  11. package/docs/elements.md +127 -2
  12. package/docs/extending-native-document-element.md +7 -1
  13. package/docs/filters.md +1216 -0
  14. package/docs/getting-started.md +12 -3
  15. package/docs/lifecycle-events.md +10 -2
  16. package/docs/list-rendering.md +453 -54
  17. package/docs/memory-management.md +9 -7
  18. package/docs/native-document-element.md +30 -9
  19. package/docs/native-fetch.md +744 -0
  20. package/docs/observables.md +135 -6
  21. package/docs/routing.md +7 -1
  22. package/docs/state-management.md +7 -1
  23. package/docs/validation.md +8 -1
  24. package/eslint.config.js +3 -3
  25. package/package.json +3 -2
  26. package/readme.md +53 -14
  27. package/src/components/$traits/HasItems.js +42 -1
  28. package/src/components/BaseComponent.js +4 -1
  29. package/src/components/accordion/Accordion.js +112 -8
  30. package/src/components/accordion/AccordionItem.js +93 -4
  31. package/src/components/alert/Alert.js +164 -4
  32. package/src/components/avatar/Avatar.js +236 -22
  33. package/src/components/menu/index.js +1 -2
  34. package/src/core/data/ObservableArray.js +120 -2
  35. package/src/core/data/ObservableChecker.js +50 -0
  36. package/src/core/data/ObservableItem.js +124 -4
  37. package/src/core/data/ObservableWhen.js +36 -6
  38. package/src/core/data/observable-helpers/array.js +12 -3
  39. package/src/core/data/observable-helpers/computed.js +17 -4
  40. package/src/core/data/observable-helpers/object.js +19 -3
  41. package/src/core/elements/control/for-each-array.js +20 -2
  42. package/src/core/elements/control/for-each.js +17 -5
  43. package/src/core/elements/control/show-if.js +31 -15
  44. package/src/core/elements/control/show-when.js +23 -0
  45. package/src/core/elements/control/switch.js +40 -10
  46. package/src/core/utils/cache.js +5 -0
  47. package/src/core/utils/memoize.js +25 -16
  48. package/src/core/utils/prototypes.js +3 -2
  49. package/src/core/wrappers/AttributesWrapper.js +1 -1
  50. package/src/core/wrappers/NDElement.js +41 -1
  51. package/src/core/wrappers/NdPrototype.js +4 -0
  52. package/src/core/wrappers/TemplateCloner.js +13 -10
  53. package/src/core/wrappers/prototypes/bind-class-extensions.js +1 -1
  54. package/src/core/wrappers/prototypes/nd-element-extensions.js +3 -0
  55. package/src/router/Route.js +9 -4
  56. package/src/router/Router.js +28 -9
  57. package/src/router/errors/RouterError.js +0 -1
  58. package/types/control-flow.d.ts +9 -6
  59. package/types/elements.d.ts +6 -3
  60. package/types/filters/index.d.ts +4 -0
  61. package/types/nd-element.d.ts +5 -238
  62. package/types/observable.d.ts +9 -3
  63. package/types/router.d.ts +5 -1
  64. package/types/template-cloner.ts +1 -0
  65. package/types/validator.ts +11 -1
  66. package/utils.d.ts +2 -1
  67. package/utils.js +4 -4
  68. package/src/core/utils/service.js +0 -6
@@ -1,10 +1,10 @@
1
1
  var NativeDocument = (function (exports) {
2
2
  'use strict';
3
3
 
4
- let DebugManager$1 = {};
4
+ let DebugManager = {};
5
5
 
6
6
  {
7
- DebugManager$1 = {
7
+ DebugManager = {
8
8
  enabled: false,
9
9
 
10
10
  enable() {
@@ -35,7 +35,7 @@ var NativeDocument = (function (exports) {
35
35
  };
36
36
 
37
37
  }
38
- var DebugManager = DebugManager$1;
38
+ var DebugManager$1 = DebugManager;
39
39
 
40
40
  const MemoryManager = (function() {
41
41
 
@@ -84,7 +84,7 @@ var NativeDocument = (function (exports) {
84
84
  }
85
85
  }
86
86
  if (cleanedCount > 0) {
87
- DebugManager.log('Memory Auto Clean', `🧹 Cleaned ${cleanedCount} orphaned observables`);
87
+ DebugManager$1.log('Memory Auto Clean', `🧹 Cleaned ${cleanedCount} orphaned observables`);
88
88
  }
89
89
  }
90
90
  };
@@ -113,6 +113,16 @@ var NativeDocument = (function (exports) {
113
113
 
114
114
  ObservableChecker.prototype.__$isObservableChecker = true;
115
115
 
116
+ /**
117
+ * Subscribes to changes in the checked/transformed value.
118
+ *
119
+ * @param {Function} callback - Function called with the transformed value when observable changes
120
+ * @returns {Function} Unsubscribe function
121
+ * @example
122
+ * const count = Observable(5);
123
+ * const doubled = count.check(n => n * 2);
124
+ * doubled.subscribe(value => console.log(value)); // Logs: 10
125
+ */
116
126
  ObservableChecker.prototype.subscribe = function(callback) {
117
127
  const unSubscribe = this.observable.subscribe((value) => {
118
128
  callback && callback(this.checker(value));
@@ -121,30 +131,70 @@ var NativeDocument = (function (exports) {
121
131
  return unSubscribe;
122
132
  };
123
133
 
134
+ /**
135
+ * Creates a new ObservableChecker by applying another transformation.
136
+ * Allows chaining transformations.
137
+ *
138
+ * @param {(value: *) => *} callback - Transformation function to apply to the current checked value
139
+ * @returns {ObservableChecker} New ObservableChecker with chained transformation
140
+ * @example
141
+ * const count = Observable(5);
142
+ * const result = count.check(n => n * 2).check(n => n + 1);
143
+ * result.val(); // 11
144
+ */
124
145
  ObservableChecker.prototype.check = function(callback) {
125
146
  return this.observable.check(() => callback(this.val()));
126
147
  };
127
148
 
149
+ /**
150
+ * Gets the current transformed/checked value.
151
+ *
152
+ * @returns {*} The result of applying the checker function to the observable's current value
153
+ * @example
154
+ * const count = Observable(5);
155
+ * const doubled = count.check(n => n * 2);
156
+ * doubled.val(); // 10
157
+ */
128
158
  ObservableChecker.prototype.val = function() {
129
159
  return this.checker && this.checker(this.observable.val());
130
160
  };
131
161
 
162
+ /**
163
+ * Sets the value of the underlying observable (not the transformed value).
164
+ *
165
+ * @param {*} value - New value for the underlying observable
166
+ * @example
167
+ * const count = Observable(5);
168
+ * const doubled = count.check(n => n * 2);
169
+ * doubled.set(10); // Sets count to 10, doubled.val() returns 20
170
+ */
132
171
  ObservableChecker.prototype.set = function(value) {
133
172
  return this.observable.set(value);
134
173
  };
135
174
 
175
+ /**
176
+ * Manually triggers the underlying observable to notify subscribers.
177
+ *
178
+ * @example
179
+ * const count = Observable(5);
180
+ * const doubled = count.check(n => n * 2);
181
+ * doubled.trigger(); // Notifies all subscribers
182
+ */
136
183
  ObservableChecker.prototype.trigger = function() {
137
184
  return this.observable.trigger();
138
185
  };
139
186
 
187
+ /**
188
+ * Cleans up the underlying observable and all its subscriptions.
189
+ */
140
190
  ObservableChecker.prototype.cleanup = function() {
141
191
  return this.observable.cleanup();
142
192
  };
143
193
 
144
- let PluginsManager$1 = null;
194
+ let PluginsManager = null;
145
195
 
146
196
  {
147
- PluginsManager$1 = (function() {
197
+ PluginsManager = (function() {
148
198
 
149
199
  const $plugins = new Map();
150
200
  const $pluginByEvents = new Map();
@@ -210,7 +260,7 @@ var NativeDocument = (function (exports) {
210
260
  try{
211
261
  callback.call(plugin, ...data);
212
262
  } catch (error) {
213
- DebugManager.error('Plugin Manager', `Error in plugin ${plugin.$name} for event ${eventName}`, error);
263
+ DebugManager$1.error('Plugin Manager', `Error in plugin ${plugin.$name} for event ${eventName}`, error);
214
264
  }
215
265
  }
216
266
  }
@@ -219,8 +269,15 @@ var NativeDocument = (function (exports) {
219
269
  }());
220
270
  }
221
271
 
222
- var PluginsManager = PluginsManager$1;
272
+ var PluginsManager$1 = PluginsManager;
223
273
 
274
+ /**
275
+ * Creates an ObservableWhen that tracks whether an observable equals a specific value.
276
+ *
277
+ * @param {ObservableItem} observer - The observable to watch
278
+ * @param {*} value - The value to compare against
279
+ * @class ObservableWhen
280
+ */
224
281
  const ObservableWhen = function(observer, value) {
225
282
  this.$target = value;
226
283
  this.$observer = observer;
@@ -228,21 +285,44 @@ var NativeDocument = (function (exports) {
228
285
 
229
286
  ObservableWhen.prototype.__$isObservableWhen = true;
230
287
 
288
+ /**
289
+ * Subscribes to changes in the match status (true when observable equals target value).
290
+ *
291
+ * @param {Function} callback - Function called with boolean indicating if values match
292
+ * @returns {Function} Unsubscribe function
293
+ * @example
294
+ * const status = Observable('idle');
295
+ * const isLoading = status.when('loading');
296
+ * isLoading.subscribe(active => console.log('Loading:', active));
297
+ */
231
298
  ObservableWhen.prototype.subscribe = function(callback) {
232
299
  return this.$observer.on(this.$target, callback);
233
300
  };
234
301
 
302
+ /**
303
+ * Returns true if the observable's current value equals the target value.
304
+ *
305
+ * @returns {boolean} True if observable value matches target value
306
+ */
235
307
  ObservableWhen.prototype.val = function() {
236
308
  return this.$observer.$currentValue === this.$target;
237
309
  };
238
310
 
239
- ObservableWhen.prototype.isMath = function() {
240
- return this.$observer.$currentValue === this.$target;
241
- };
311
+ /**
312
+ * Returns true if the observable's current value equals the target value.
313
+ * Alias for val().
314
+ *
315
+ * @returns {boolean} True if observable value matches target value
316
+ */
317
+ ObservableWhen.prototype.isMatch = ObservableWhen.prototype.val;
242
318
 
243
- ObservableWhen.prototype.isActive = function() {
244
- return this.$observer.$currentValue === this.$target;
245
- };
319
+ /**
320
+ * Returns true if the observable's current value equals the target value.
321
+ * Alias for val().
322
+ *
323
+ * @returns {boolean} True if observable value matches target value
324
+ */
325
+ ObservableWhen.prototype.isActive = ObservableWhen.prototype.val;
246
326
 
247
327
  const nextTick = function(fn) {
248
328
  let pending = false;
@@ -343,7 +423,7 @@ var NativeDocument = (function (exports) {
343
423
  }
344
424
  }
345
425
  {
346
- PluginsManager.emit('CreateObservable', this);
426
+ PluginsManager$1.emit('CreateObservable', this);
347
427
  }
348
428
  }
349
429
 
@@ -360,6 +440,16 @@ var NativeDocument = (function (exports) {
360
440
  ObservableItem.prototype.__$isObservable = true;
361
441
  const noneTrigger = function() {};
362
442
 
443
+ /**
444
+ * Intercepts and transforms values before they are set on the observable.
445
+ * The interceptor can modify the value or return undefined to use the original value.
446
+ *
447
+ * @param {(value) => any} callback - Interceptor function that receives (newValue, currentValue) and returns the transformed value or undefined
448
+ * @returns {ObservableItem} The observable instance for chaining
449
+ * @example
450
+ * const count = Observable(0);
451
+ * count.intercept((newVal, oldVal) => Math.max(0, newVal)); // Prevent negative values
452
+ */
363
453
  ObservableItem.prototype.intercept = function(callback) {
364
454
  this.$interceptor = callback;
365
455
  this.set = this.$setWithInterceptor;
@@ -437,12 +527,12 @@ var NativeDocument = (function (exports) {
437
527
  this.$previousValue = this.$currentValue;
438
528
  this.$currentValue = newValue;
439
529
  {
440
- PluginsManager.emit('ObservableBeforeChange', this);
530
+ PluginsManager$1.emit('ObservableBeforeChange', this);
441
531
  }
442
532
  this.trigger();
443
533
  this.$previousValue = null;
444
534
  {
445
- PluginsManager.emit('ObservableAfterChange', this);
535
+ PluginsManager$1.emit('ObservableAfterChange', this);
446
536
  }
447
537
  };
448
538
 
@@ -491,6 +581,16 @@ var NativeDocument = (function (exports) {
491
581
  this.trigger = noneTrigger;
492
582
  };
493
583
 
584
+ /**
585
+ * Registers a cleanup callback that will be executed when the observable is cleaned up.
586
+ * Useful for disposing resources, removing event listeners, or other cleanup tasks.
587
+ *
588
+ * @param {Function} callback - Cleanup function to execute on observable disposal
589
+ * @example
590
+ * const obs = Observable(0);
591
+ * obs.onCleanup(() => console.log('Cleaned up!'));
592
+ * obs.cleanup(); // Logs: "Cleaned up!"
593
+ */
494
594
  ObservableItem.prototype.onCleanup = function(callback) {
495
595
  this.$cleanupListeners = this.$cleanupListeners ?? [];
496
596
  this.$cleanupListeners.push(callback);
@@ -519,7 +619,7 @@ var NativeDocument = (function (exports) {
519
619
  ObservableItem.prototype.subscribe = function(callback) {
520
620
  {
521
621
  if (this.$isCleanedUp) {
522
- DebugManager.warn('Observable subscription', '⚠️ Attempted to subscribe to a cleaned up observable.');
622
+ DebugManager$1.warn('Observable subscription', '⚠️ Attempted to subscribe to a cleaned up observable.');
523
623
  return;
524
624
  }
525
625
  if (typeof callback !== 'function') {
@@ -531,10 +631,21 @@ var NativeDocument = (function (exports) {
531
631
  this.$listeners.push(callback);
532
632
  this.assocTrigger();
533
633
  {
534
- PluginsManager.emit('ObservableSubscribe', this);
634
+ PluginsManager$1.emit('ObservableSubscribe', this);
535
635
  }
536
636
  };
537
637
 
638
+ /**
639
+ * Watches for a specific value and executes callback when the observable equals that value.
640
+ * Creates a watcher that only triggers when the observable changes to the specified value.
641
+ *
642
+ * @param {*} value - The value to watch for
643
+ * @param {(value) => void|ObservableItem} callback - Callback function or observable to set when value matches
644
+ * @example
645
+ * const status = Observable('idle');
646
+ * status.on('loading', () => console.log('Started loading'));
647
+ * status.on('error', isError); // Set another observable
648
+ */
538
649
  ObservableItem.prototype.on = function(value, callback) {
539
650
  this.$watchers = this.$watchers ?? new Map();
540
651
 
@@ -564,8 +675,16 @@ var NativeDocument = (function (exports) {
564
675
  };
565
676
 
566
677
  /**
567
- * @param {*} value
568
- * @param {Function} callback - if omitted, removes all watchers for this value
678
+ * Removes a watcher for a specific value. If no callback is provided, removes all watchers for that value.
679
+ *
680
+ * @param {*} value - The value to stop watching
681
+ * @param {Function} [callback] - Specific callback to remove. If omitted, removes all watchers for this value
682
+ * @example
683
+ * const status = Observable('idle');
684
+ * const handler = () => console.log('Loading');
685
+ * status.on('loading', handler);
686
+ * status.off('loading', handler); // Remove specific handler
687
+ * status.off('loading'); // Remove all handlers for 'loading'
569
688
  */
570
689
  ObservableItem.prototype.off = function(value, callback) {
571
690
  if(!this.$watchers) return;
@@ -585,11 +704,20 @@ var NativeDocument = (function (exports) {
585
704
  }
586
705
  else if(watchValueList.length === 0) {
587
706
  this.$watchers?.delete(value);
588
- watchValueList = null;
589
707
  }
590
708
  this.assocTrigger();
591
709
  };
592
710
 
711
+ /**
712
+ * Subscribes to the observable but automatically unsubscribes after the first time the predicate matches.
713
+ *
714
+ * @param {(value) => Boolean|any} predicate - Value to match or function that returns true when condition is met
715
+ * @param {(value) => void} callback - Callback to execute when predicate matches, receives the matched value
716
+ * @example
717
+ * const status = Observable('loading');
718
+ * status.once('ready', (val) => console.log('Ready!'));
719
+ * status.once(val => val === 'error', (val) => console.log('Error occurred'));
720
+ */
593
721
  ObservableItem.prototype.once = function(predicate, callback) {
594
722
  const fn = typeof predicate === 'function' ? predicate : (v) => v === predicate;
595
723
 
@@ -614,7 +742,7 @@ var NativeDocument = (function (exports) {
614
742
  }
615
743
  this.assocTrigger();
616
744
  {
617
- PluginsManager.emit('ObservableUnsubscribe', this);
745
+ PluginsManager$1.emit('ObservableUnsubscribe', this);
618
746
  }
619
747
  };
620
748
 
@@ -627,15 +755,50 @@ var NativeDocument = (function (exports) {
627
755
  return new ObservableChecker(this, callback)
628
756
  };
629
757
 
758
+ /**
759
+ * Gets a property value from the observable's current value.
760
+ * If the property is an observable, returns its value.
761
+ *
762
+ * @param {string|number} key - Property key to retrieve
763
+ * @returns {*} The value of the property, unwrapped if it's an observable
764
+ * @example
765
+ * const user = Observable({ name: 'John', age: Observable(25) });
766
+ * user.get('name'); // 'John'
767
+ * user.get('age'); // 25 (unwrapped from observable)
768
+ */
630
769
  ObservableItem.prototype.get = function(key) {
631
770
  const item = this.$currentValue[key];
632
771
  return Validator.isObservable(item) ? item.val() : item;
633
772
  };
634
773
 
774
+ /**
775
+ * Creates an ObservableWhen that represents whether the observable equals a specific value.
776
+ * Returns an object that can be subscribed to and will emit true/false.
777
+ *
778
+ * @param {*} value - The value to compare against
779
+ * @returns {ObservableWhen} An ObservableWhen instance that tracks when the observable equals the value
780
+ * @example
781
+ * const status = Observable('idle');
782
+ * const isLoading = status.when('loading');
783
+ * isLoading.subscribe(active => console.log('Loading:', active));
784
+ * status.set('loading'); // Logs: "Loading: true"
785
+ */
635
786
  ObservableItem.prototype.when = function(value) {
636
787
  return new ObservableWhen(this, value);
637
788
  };
638
789
 
790
+ /**
791
+ * Compares the observable's current value with another value or observable.
792
+ *
793
+ * @param {*|ObservableItem} other - Value or observable to compare against
794
+ * @returns {boolean} True if values are equal
795
+ * @example
796
+ * const a = Observable(5);
797
+ * const b = Observable(5);
798
+ * a.equals(5); // true
799
+ * a.equals(b); // true
800
+ * a.equals(10); // false
801
+ */
639
802
  ObservableItem.prototype.equals = function(other) {
640
803
  if(Validator.isObservable(other)) {
641
804
  return this.$currentValue === other.$currentValue;
@@ -643,14 +806,41 @@ var NativeDocument = (function (exports) {
643
806
  return this.$currentValue === other;
644
807
  };
645
808
 
809
+ /**
810
+ * Converts the observable's current value to a boolean.
811
+ *
812
+ * @returns {boolean} The boolean representation of the current value
813
+ * @example
814
+ * const count = Observable(0);
815
+ * count.toBool(); // false
816
+ * count.set(5);
817
+ * count.toBool(); // true
818
+ */
646
819
  ObservableItem.prototype.toBool = function() {
647
820
  return !!this.$currentValue;
648
821
  };
649
822
 
823
+ /**
824
+ * Toggles the boolean value of the observable (false becomes true, true becomes false).
825
+ *
826
+ * @example
827
+ * const isOpen = Observable(false);
828
+ * isOpen.toggle(); // Now true
829
+ * isOpen.toggle(); // Now false
830
+ */
650
831
  ObservableItem.prototype.toggle = function() {
651
832
  this.set(!this.$currentValue);
652
833
  };
653
834
 
835
+ /**
836
+ * Resets the observable to its initial value.
837
+ * Only works if the observable was created with { reset: true } config.
838
+ *
839
+ * @example
840
+ * const count = Observable(0, { reset: true });
841
+ * count.set(10);
842
+ * count.reset(); // Back to 0
843
+ */
654
844
  ObservableItem.prototype.reset = function() {
655
845
  if(!this.configs?.reset) {
656
846
  return;
@@ -663,11 +853,21 @@ var NativeDocument = (function (exports) {
663
853
  this.set(resetValue);
664
854
  };
665
855
 
666
-
856
+ /**
857
+ * Returns a string representation of the observable's current value.
858
+ *
859
+ * @returns {string} String representation of the current value
860
+ */
667
861
  ObservableItem.prototype.toString = function() {
668
862
  return String(this.$currentValue);
669
863
  };
670
864
 
865
+ /**
866
+ * Returns the primitive value of the observable (its current value).
867
+ * Called automatically in type coercion contexts.
868
+ *
869
+ * @returns {*} The current value of the observable
870
+ */
671
871
  ObservableItem.prototype.valueOf = function() {
672
872
  return this.$currentValue;
673
873
  };
@@ -779,7 +979,7 @@ var NativeDocument = (function (exports) {
779
979
  this.$element = element;
780
980
  this.$observer = null;
781
981
  {
782
- PluginsManager.emit('NDElementCreated', element, this);
982
+ PluginsManager$1.emit('NDElementCreated', element, this);
783
983
  }
784
984
  }
785
985
 
@@ -875,12 +1075,35 @@ var NativeDocument = (function (exports) {
875
1075
  return this.shadow('closed', style);
876
1076
  };
877
1077
 
1078
+ /**
1079
+ * Attaches a template binding to the element by hydrating it with the specified method.
1080
+ *
1081
+ * @param {string} methodName - Name of the hydration method to call
1082
+ * @param {BindingHydrator} bindingHydrator - Template binding with $hydrate method
1083
+ * @returns {HTMLElement} The underlying HTML element
1084
+ * @example
1085
+ * const onClick = $binder.attach((event, data) => console.log(data));
1086
+ * element.nd.attach('onClick', onClick);
1087
+ */
878
1088
  NDElement.prototype.attach = function(methodName, bindingHydrator) {
879
1089
  bindingHydrator.$hydrate(this.$element, methodName);
880
1090
  return this.$element;
881
1091
  };
882
1092
 
883
-
1093
+ /**
1094
+ * Extends the current NDElement instance with custom methods.
1095
+ * Methods are bound to the instance and available for chaining.
1096
+ *
1097
+ * @param {Object} methods - Object containing method definitions
1098
+ * @returns {this} The NDElement instance with added methods for chaining
1099
+ * @example
1100
+ * element.nd.with({
1101
+ * highlight() {
1102
+ * this.$element.style.background = 'yellow';
1103
+ * return this;
1104
+ * }
1105
+ * }).highlight().onClick(() => console.log('Clicked'));
1106
+ */
884
1107
  NDElement.prototype.with = function(methods) {
885
1108
  if (!methods || typeof methods !== 'object') {
886
1109
  throw new NativeDocumentError('extend() requires an object of methods');
@@ -900,7 +1123,7 @@ var NativeDocument = (function (exports) {
900
1123
  }
901
1124
  {
902
1125
  if (this[name] && !this.$localExtensions.has(name)) {
903
- DebugManager.warn('NDElement.extend', `Method "${name}" already exists and will be overwritten`);
1126
+ DebugManager$1.warn('NDElement.extend', `Method "${name}" already exists and will be overwritten`);
904
1127
  }
905
1128
  this.$localExtensions.set(name, method);
906
1129
  }
@@ -911,6 +1134,23 @@ var NativeDocument = (function (exports) {
911
1134
  return this;
912
1135
  };
913
1136
 
1137
+ /**
1138
+ * Extends the NDElement prototype with new methods available to all NDElement instances.
1139
+ * Use this to add global methods to all NDElements.
1140
+ *
1141
+ * @param {Object} methods - Object containing method definitions to add to prototype
1142
+ * @returns {typeof NDElement} The NDElement constructor
1143
+ * @throws {NativeDocumentError} If methods is not an object or contains non-function values
1144
+ * @example
1145
+ * NDElement.extend({
1146
+ * fadeIn() {
1147
+ * this.$element.style.opacity = '1';
1148
+ * return this;
1149
+ * }
1150
+ * });
1151
+ * // Now all NDElements have .fadeIn() method
1152
+ * Div().nd.fadeIn();
1153
+ */
914
1154
  NDElement.extend = function(methods) {
915
1155
  if (!methods || typeof methods !== 'object') {
916
1156
  throw new NativeDocumentError('NDElement.extend() requires an object of methods');
@@ -934,23 +1174,23 @@ var NativeDocument = (function (exports) {
934
1174
  const method = methods[name];
935
1175
 
936
1176
  if (typeof method !== 'function') {
937
- DebugManager.warn('NDElement.extend', `"${name}" is not a function, skipping`);
1177
+ DebugManager$1.warn('NDElement.extend', `"${name}" is not a function, skipping`);
938
1178
  continue;
939
1179
  }
940
1180
 
941
1181
  if (protectedMethods.has(name)) {
942
- DebugManager.error('NDElement.extend', `Cannot override protected method "${name}"`);
1182
+ DebugManager$1.error('NDElement.extend', `Cannot override protected method "${name}"`);
943
1183
  throw new NativeDocumentError(`Cannot override protected method "${name}"`);
944
1184
  }
945
1185
 
946
1186
  if (NDElement.prototype[name]) {
947
- DebugManager.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
1187
+ DebugManager$1.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
948
1188
  }
949
1189
 
950
1190
  NDElement.prototype[name] = method;
951
1191
  }
952
1192
  {
953
- PluginsManager.emit('NDElementExtended', methods);
1193
+ PluginsManager$1.emit('NDElementExtended', methods);
954
1194
  }
955
1195
 
956
1196
  return NDElement;
@@ -1103,7 +1343,7 @@ var NativeDocument = (function (exports) {
1103
1343
  const foundReserved = Object.keys(attributes).filter(key => reserved.includes(key));
1104
1344
 
1105
1345
  if (foundReserved.length > 0) {
1106
- DebugManager.warn('Validator', `Reserved attributes found: ${foundReserved.join(', ')}`);
1346
+ DebugManager$1.warn('Validator', `Reserved attributes found: ${foundReserved.join(', ')}`);
1107
1347
  }
1108
1348
 
1109
1349
  return attributes;
@@ -1151,7 +1391,7 @@ var NativeDocument = (function (exports) {
1151
1391
  anchorFragment.appendChild = function(child, before = null) {
1152
1392
  const parent = anchorEnd.parentNode;
1153
1393
  if(!parent) {
1154
- DebugManager.error('Anchor', 'Anchor : parent not found', child);
1394
+ DebugManager$1.error('Anchor', 'Anchor : parent not found', child);
1155
1395
  return;
1156
1396
  }
1157
1397
  before = before ?? anchorEnd;
@@ -1379,7 +1619,7 @@ var NativeDocument = (function (exports) {
1379
1619
  continue;
1380
1620
  }
1381
1621
  if(value.__$isObservableWhen) {
1382
- element.classes.toggle(className, value.isMath());
1622
+ element.classes.toggle(className, value.isActive());
1383
1623
  value.subscribe((shouldAdd) => element.classes.toggle(className, shouldAdd));
1384
1624
  continue;
1385
1625
  }
@@ -1525,6 +1765,8 @@ var NativeDocument = (function (exports) {
1525
1765
  return ElementCreator.createObservableNode(null, this);
1526
1766
  };
1527
1767
 
1768
+ ObservableChecker.prototype.toNdElement = ObservableItem.prototype.toNdElement;
1769
+
1528
1770
  NDElement.prototype.toNdElement = function () {
1529
1771
  return this.$element ?? this.$build?.() ?? this.build?.() ?? null;
1530
1772
  };
@@ -1542,7 +1784,7 @@ var NativeDocument = (function (exports) {
1542
1784
  Function.prototype.toNdElement = function () {
1543
1785
  const child = this;
1544
1786
  {
1545
- PluginsManager.emit('BeforeProcessComponent', child);
1787
+ PluginsManager$1.emit('BeforeProcessComponent', child);
1546
1788
  }
1547
1789
  return ElementCreator.getChild(child());
1548
1790
  };
@@ -1648,14 +1890,14 @@ var NativeDocument = (function (exports) {
1648
1890
  processChildren(children, parent) {
1649
1891
  if(children === null) return;
1650
1892
  {
1651
- PluginsManager.emit('BeforeProcessChildren', parent);
1893
+ PluginsManager$1.emit('BeforeProcessChildren', parent);
1652
1894
  }
1653
1895
  let child = this.getChild(children);
1654
1896
  if(child) {
1655
1897
  parent.appendChild(child);
1656
1898
  }
1657
1899
  {
1658
- PluginsManager.emit('AfterProcessChildren', parent);
1900
+ PluginsManager$1.emit('AfterProcessChildren', parent);
1659
1901
  }
1660
1902
  },
1661
1903
  getChild(child) {
@@ -1871,6 +2113,10 @@ var NativeDocument = (function (exports) {
1871
2113
  _stop(this.$element, eventName, callback);
1872
2114
  return this;
1873
2115
  };
2116
+ NDElement.prototype['onPreventStop'+eventSourceName] = function(callback = null) {
2117
+ _preventStop(this.$element, eventName, callback);
2118
+ return this;
2119
+ };
1874
2120
  });
1875
2121
 
1876
2122
  EVENTS_WITH_PREVENT.forEach(eventSourceName => {
@@ -1904,6 +2150,16 @@ var NativeDocument = (function (exports) {
1904
2150
  return this;
1905
2151
  };
1906
2152
 
2153
+ const _preventStop = function(element, eventName, callback) {
2154
+ const handler = (event) => {
2155
+ event.stopPropagation();
2156
+ event.preventDefault();
2157
+ callback && callback.call(element, event);
2158
+ };
2159
+ element.addEventListener(eventName, handler);
2160
+ return this;
2161
+ };
2162
+
1907
2163
 
1908
2164
 
1909
2165
  // ----------------------------------------------------------------
@@ -2176,7 +2432,12 @@ var NativeDocument = (function (exports) {
2176
2432
 
2177
2433
  const applyBindingTreePath = (root, target, data, path) => {
2178
2434
  if(path.fn) {
2179
- path.fn(data, target, root);
2435
+ if(typeof path.fn === 'string') {
2436
+ ElementCreator.bindTextNode(target, data[0][path.fn]);
2437
+ }
2438
+ else {
2439
+ path.fn(data, target, root);
2440
+ }
2180
2441
  }
2181
2442
  if(path.children) {
2182
2443
  for(let i = 0, length = path.children.length; i < length; i++) {
@@ -2202,15 +2463,16 @@ var NativeDocument = (function (exports) {
2202
2463
  if(bindDingData && bindDingData.value) {
2203
2464
  currentPath.fn = bindDingData.value;
2204
2465
  const textNode = node.cloneNode();
2466
+ if(typeof bindDingData.value === 'string') {
2467
+ ElementCreator.bindTextNode(textNode, data[0][bindDingData.value]);
2468
+ return textNode;
2469
+ }
2205
2470
  bindDingData.value(data, textNode);
2206
2471
  return textNode;
2207
2472
  }
2208
2473
  return node.cloneNode(true);
2209
2474
  }
2210
- const nodeCloned = node.cloneNode(node.fullCloneNode);
2211
- if(node.fullCloneNode) {
2212
- return nodeCloned;
2213
- }
2475
+ const nodeCloned = node.cloneNode();
2214
2476
  if(bindDingData) {
2215
2477
  bindAttributes(nodeCloned, bindDingData, data);
2216
2478
  bindAttachMethods(nodeCloned, bindDingData, data);
@@ -2275,12 +2537,9 @@ var NativeDocument = (function (exports) {
2275
2537
  };
2276
2538
  this.value = (callbackOrProperty) => {
2277
2539
  if(typeof callbackOrProperty !== 'function') {
2278
- return createBinding(function(data, textNode) {
2279
- const firstArgument = data[0];
2280
- ElementCreator.bindTextNode(textNode, firstArgument[callbackOrProperty]);
2281
- }, 'value');
2540
+ return createBinding(callbackOrProperty, 'value');
2282
2541
  }
2283
- return createBinding(function(data, textNode) {
2542
+ return createBinding((data, textNode) => {
2284
2543
  ElementCreator.bindTextNode(textNode, callbackOrProperty(...data));
2285
2544
  }, 'value');
2286
2545
  };
@@ -2382,13 +2641,14 @@ var NativeDocument = (function (exports) {
2382
2641
  };
2383
2642
 
2384
2643
  Function.prototype.errorBoundary = function(callback) {
2385
- return (...args) => {
2644
+ const handler = (...args) => {
2386
2645
  try {
2387
2646
  return this.apply(this, args);
2388
2647
  } catch(e) {
2389
- return callback(e);
2648
+ return callback(e, {caller: handler, args: args });
2390
2649
  }
2391
2650
  };
2651
+ return handler;
2392
2652
  };
2393
2653
 
2394
2654
  String.prototype.use = function(args) {
@@ -2460,12 +2720,13 @@ var NativeDocument = (function (exports) {
2460
2720
  };
2461
2721
  };
2462
2722
 
2463
- const once = (fn) => {
2723
+ const once$1 = (fn) => {
2464
2724
  let result = null;
2465
2725
  return (...args) => {
2466
- if(result === null) {
2467
- result = fn(...args);
2726
+ if(result) {
2727
+ return result;
2468
2728
  }
2729
+ result = fn(...args);
2469
2730
  return result;
2470
2731
  };
2471
2732
  };
@@ -2474,22 +2735,26 @@ var NativeDocument = (function (exports) {
2474
2735
  let target = null;
2475
2736
  return new Proxy({}, {
2476
2737
  get: (_, key) => {
2477
- if(target === null) {
2478
- target = fn();
2738
+ if(target) {
2739
+ return target[key];
2479
2740
  }
2741
+ target = fn();
2480
2742
  return target[key];
2481
2743
  }
2482
2744
  });
2483
2745
  };
2484
2746
 
2485
- const memoize = (fn) => {
2747
+ const memoize$1 = (fn) => {
2486
2748
  const cache = new Map();
2487
2749
  return (...args) => {
2488
2750
  const [key, ...rest] = args;
2489
- if(!cache.has(key)) {
2490
- cache.set(key, fn(...rest));
2751
+ const cached = cache.get(key);
2752
+ if(cached) {
2753
+ return cached;
2491
2754
  }
2492
- return cache.get(key);
2755
+ const result = fn(...rest);
2756
+ cache.set(key, result);
2757
+ return result;
2493
2758
  };
2494
2759
  };
2495
2760
 
@@ -2497,17 +2762,21 @@ var NativeDocument = (function (exports) {
2497
2762
  const cache = new Map();
2498
2763
  return new Proxy({}, {
2499
2764
  get: (_, key) => {
2500
- if(!cache.has(key)) {
2501
- if(fn.length > 0) {
2502
- return (...args) => {
2503
- const result = fn(...args);
2504
- cache.set(key, result);
2505
- return result;
2506
- }
2765
+ const cached = cache.get(key);
2766
+ if(cached) {
2767
+ return cached;
2768
+ }
2769
+
2770
+ if(fn.length > 0) {
2771
+ return (...args) => {
2772
+ const result = fn(...args, key);
2773
+ cache.set(key, result);
2774
+ return result;
2507
2775
  }
2508
- cache.set(key, fn());
2509
2776
  }
2510
- return cache.get(key);
2777
+ const result = fn(key);
2778
+ cache.set(key, result);
2779
+ return result;
2511
2780
  }
2512
2781
  });
2513
2782
  };
@@ -2880,7 +3149,7 @@ var NativeDocument = (function (exports) {
2880
3149
 
2881
3150
  ObservableItem.call(this, target, configs);
2882
3151
  {
2883
- PluginsManager.emit('CreateObservableArray', this);
3152
+ PluginsManager$1.emit('CreateObservableArray', this);
2884
3153
  }
2885
3154
  };
2886
3155
 
@@ -2909,6 +3178,14 @@ var NativeDocument = (function (exports) {
2909
3178
  };
2910
3179
  });
2911
3180
 
3181
+ /**
3182
+ * Removes all items from the array and triggers an update.
3183
+ *
3184
+ * @returns {boolean} True if array was cleared, false if it was already empty
3185
+ * @example
3186
+ * const items = Observable.array([1, 2, 3]);
3187
+ * items.clear(); // []
3188
+ */
2912
3189
  ObservableArray.prototype.clear = function() {
2913
3190
  if(this.$currentValue.length === 0) {
2914
3191
  return;
@@ -2918,19 +3195,42 @@ var NativeDocument = (function (exports) {
2918
3195
  return true;
2919
3196
  };
2920
3197
 
3198
+ /**
3199
+ * Returns the element at the specified index in the array.
3200
+ *
3201
+ * @param {number} index - Zero-based index of the element to retrieve
3202
+ * @returns {*} The element at the specified index
3203
+ * @example
3204
+ * const items = Observable.array(['a', 'b', 'c']);
3205
+ * items.at(1); // 'b'
3206
+ */
2921
3207
  ObservableArray.prototype.at = function(index) {
2922
3208
  return this.$currentValue[index];
2923
3209
  };
2924
3210
 
3211
+
3212
+ /**
3213
+ * Merges multiple values into the array and triggers an update.
3214
+ * Similar to push but with a different operation name.
3215
+ *
3216
+ * @param {Array} values - Array of values to merge
3217
+ * @example
3218
+ * const items = Observable.array([1, 2]);
3219
+ * items.merge([3, 4]); // [1, 2, 3, 4]
3220
+ */
2925
3221
  ObservableArray.prototype.merge = function(values) {
2926
3222
  this.$currentValue.push.apply(this.$currentValue, values);
2927
3223
  this.trigger({ action: 'merge', args: values });
2928
3224
  };
2929
3225
 
2930
3226
  /**
3227
+ * Counts the number of elements that satisfy the provided condition.
2931
3228
  *
2932
- * @param {Function} condition
2933
- * @returns {number}
3229
+ * @param {(value: *, index: number) => Boolean} condition - Function that tests each element (item, index) => boolean
3230
+ * @returns {number} The count of elements that satisfy the condition
3231
+ * @example
3232
+ * const numbers = Observable.array([1, 2, 3, 4, 5]);
3233
+ * numbers.count(n => n > 3); // 2
2934
3234
  */
2935
3235
  ObservableArray.prototype.count = function(condition) {
2936
3236
  let count = 0;
@@ -2942,6 +3242,16 @@ var NativeDocument = (function (exports) {
2942
3242
  return count;
2943
3243
  };
2944
3244
 
3245
+ /**
3246
+ * Swaps two elements at the specified indices and triggers an update.
3247
+ *
3248
+ * @param {number} indexA - Index of the first element
3249
+ * @param {number} indexB - Index of the second element
3250
+ * @returns {boolean} True if swap was successful, false if indices are out of bounds
3251
+ * @example
3252
+ * const items = Observable.array(['a', 'b', 'c']);
3253
+ * items.swap(0, 2); // ['c', 'b', 'a']
3254
+ */
2945
3255
  ObservableArray.prototype.swap = function(indexA, indexB) {
2946
3256
  const value = this.$currentValue;
2947
3257
  const length = value.length;
@@ -2962,6 +3272,15 @@ var NativeDocument = (function (exports) {
2962
3272
  return true;
2963
3273
  };
2964
3274
 
3275
+ /**
3276
+ * Removes the element at the specified index and triggers an update.
3277
+ *
3278
+ * @param {number} index - Index of the element to remove
3279
+ * @returns {Array} Array containing the removed element, or empty array if index is invalid
3280
+ * @example
3281
+ * const items = Observable.array(['a', 'b', 'c']);
3282
+ * items.remove(1); // ['b'] - Array is now ['a', 'c']
3283
+ */
2965
3284
  ObservableArray.prototype.remove = function(index) {
2966
3285
  const deleted = this.$currentValue.splice(index, 1);
2967
3286
  if(deleted.length === 0) {
@@ -2971,20 +3290,60 @@ var NativeDocument = (function (exports) {
2971
3290
  return deleted;
2972
3291
  };
2973
3292
 
3293
+ /**
3294
+ * Removes the first occurrence of the specified item from the array.
3295
+ *
3296
+ * @param {*} item - The item to remove
3297
+ * @returns {Array} Array containing the removed element, or empty array if item not found
3298
+ * @example
3299
+ * const items = Observable.array(['a', 'b', 'c']);
3300
+ * items.removeItem('b'); // ['b'] - Array is now ['a', 'c']
3301
+ */
2974
3302
  ObservableArray.prototype.removeItem = function(item) {
2975
3303
  const indexOfItem = this.$currentValue.indexOf(item);
3304
+ if(indexOfItem === -1) {
3305
+ return [];
3306
+ }
2976
3307
  return this.remove(indexOfItem);
2977
3308
  };
2978
3309
 
3310
+ /**
3311
+ * Checks if the array is empty.
3312
+ *
3313
+ * @returns {boolean} True if array has no elements
3314
+ * @example
3315
+ * const items = Observable.array([]);
3316
+ * items.isEmpty(); // true
3317
+ */
2979
3318
  ObservableArray.prototype.isEmpty = function() {
2980
3319
  return this.$currentValue.length === 0;
2981
3320
  };
2982
3321
 
3322
+ /**
3323
+ * Triggers a populate operation with the current array, iteration count, and callback.
3324
+ * Used internally for rendering optimizations.
3325
+ *
3326
+ * @param {number} iteration - Iteration count for rendering
3327
+ * @param {Function} callback - Callback function for rendering items
3328
+ */
2983
3329
  ObservableArray.prototype.populateAndRender = function(iteration, callback) {
2984
3330
  this.trigger({ action: 'populate', args: [this.$currentValue, iteration, callback] });
2985
3331
  };
2986
3332
 
2987
3333
 
3334
+ /**
3335
+ * Creates a filtered view of the array based on predicates.
3336
+ * The filtered array updates automatically when source data or predicates change.
3337
+ *
3338
+ * @param {Object} predicates - Object mapping property names to filter conditions or functions
3339
+ * @returns {ObservableArray} A new observable array containing filtered items
3340
+ * @example
3341
+ * const users = Observable.array([
3342
+ * { name: 'John', age: 25 },
3343
+ * { name: 'Jane', age: 30 }
3344
+ * ]);
3345
+ * const adults = users.where({ age: (val) => val >= 18 });
3346
+ */
2988
3347
  ObservableArray.prototype.where = function(predicates) {
2989
3348
  const sourceArray = this;
2990
3349
  const observableDependencies = [sourceArray];
@@ -3033,6 +3392,20 @@ var NativeDocument = (function (exports) {
3033
3392
  return viewArray;
3034
3393
  };
3035
3394
 
3395
+ /**
3396
+ * Creates a filtered view where at least one of the specified fields matches the filter.
3397
+ *
3398
+ * @param {Array<string>} fields - Array of field names to check
3399
+ * @param {FilterResult} filter - Filter condition with callback and dependencies
3400
+ * @returns {ObservableArray} A new observable array containing filtered items
3401
+ * @example
3402
+ * const products = Observable.array([
3403
+ * { name: 'Apple', category: 'Fruit' },
3404
+ * { name: 'Carrot', category: 'Vegetable' }
3405
+ * ]);
3406
+ * const searchTerm = Observable('App');
3407
+ * const filtered = products.whereSome(['name', 'category'], match(searchTerm));
3408
+ */
3036
3409
  ObservableArray.prototype.whereSome = function(fields, filter) {
3037
3410
  return this.where({
3038
3411
  _: {
@@ -3042,6 +3415,20 @@ var NativeDocument = (function (exports) {
3042
3415
  });
3043
3416
  };
3044
3417
 
3418
+ /**
3419
+ * Creates a filtered view where all specified fields match the filter.
3420
+ *
3421
+ * @param {Array<string>} fields - Array of field names to check
3422
+ * @param {FilterResult} filter - Filter condition with callback and dependencies
3423
+ * @returns {ObservableArray} A new observable array containing filtered items
3424
+ * @example
3425
+ * const items = Observable.array([
3426
+ * { status: 'active', verified: true },
3427
+ * { status: 'active', verified: false }
3428
+ * ]);
3429
+ * const activeFilter = equals('active');
3430
+ * const filtered = items.whereEvery(['status', 'verified'], activeFilter);
3431
+ */
3045
3432
  ObservableArray.prototype.whereEvery = function(fields, filter) {
3046
3433
  return this.where({
3047
3434
  _: {
@@ -3052,10 +3439,19 @@ var NativeDocument = (function (exports) {
3052
3439
  };
3053
3440
 
3054
3441
  /**
3442
+ * Creates an observable array with reactive array methods.
3443
+ * All mutations trigger updates automatically.
3055
3444
  *
3056
- * @param {Array} target
3057
- * @param {{propagation: boolean, deep: boolean, reset: boolean}|null} configs
3058
- * @returns {ObservableArray}
3445
+ * @param {Array} [target=[]] - Initial array value
3446
+ * @param {Object|null} [configs=null] - Configuration options
3447
+ * // @param {boolean} [configs.propagation=true] - Whether to propagate changes to parent observables
3448
+ * // @param {boolean} [configs.deep=false] - Whether to make nested objects observable
3449
+ * @param {boolean} [configs.reset=false] - Whether to store initial value for reset()
3450
+ * @returns {ObservableArray} An observable array with reactive methods
3451
+ * @example
3452
+ * const items = Observable.array([1, 2, 3]);
3453
+ * items.push(4); // Triggers update
3454
+ * items.subscribe((arr) => console.log(arr));
3059
3455
  */
3060
3456
  Observable.array = function(target = [], configs = null) {
3061
3457
  return new ObservableArray(target, configs);
@@ -3120,10 +3516,26 @@ var NativeDocument = (function (exports) {
3120
3516
  };
3121
3517
 
3122
3518
  /**
3519
+ * Creates an observable proxy for an object where each property becomes an observable.
3520
+ * Properties can be accessed directly or via getter methods.
3123
3521
  *
3124
- * @param {Object} initialValue
3125
- * @param {{propagation: boolean, deep: boolean, reset: boolean}|null} configs
3126
- * @returns {Proxy}
3522
+ * @param {Object} initialValue - Initial object value
3523
+ * @param {Object|null} [configs=null] - Configuration options
3524
+ * // @param {boolean} [configs.propagation=true] - Whether changes propagate to parent
3525
+ * @param {boolean} [configs.deep=false] - Whether to make nested objects observable
3526
+ * @param {boolean} [configs.reset=false] - Whether to enable reset() method
3527
+ * @returns {ObservableProxy} A proxy where each property is an observable
3528
+ * @example
3529
+ * const user = Observable.init({
3530
+ * name: 'John',
3531
+ * age: 25,
3532
+ * address: { city: 'NYC' }
3533
+ * }, { deep: true });
3534
+ *
3535
+ * user.name.val(); // 'John'
3536
+ * user.name.set('Jane');
3537
+ * user.name = 'Jane X'
3538
+ * user.age.subscribe(val => console.log('Age:', val));
3127
3539
  */
3128
3540
  Observable.init = function(initialValue, configs = null) {
3129
3541
  const data = {};
@@ -3271,17 +3683,30 @@ var NativeDocument = (function (exports) {
3271
3683
  Observable.json = Observable.init;
3272
3684
 
3273
3685
  /**
3686
+ * Creates a computed observable that automatically updates when its dependencies change.
3687
+ * The callback is re-executed whenever any dependency observable changes.
3274
3688
  *
3275
- * @param {Function} callback
3276
- * @param {Array|Function} dependencies
3277
- * @returns {ObservableItem}
3278
- */
3689
+ * @param {Function} callback - Function that returns the computed value
3690
+ * @param {Array<ObservableItem|ObservableChecker|ObservableProxy>|Function} [dependencies=[]] - Array of observables to watch, or batch function
3691
+ * @returns {ObservableItem} A new observable that updates automatically
3692
+ * @example
3693
+ * const firstName = Observable('John');
3694
+ * const lastName = Observable('Doe');
3695
+ * const fullName = Observable.computed(
3696
+ * () => `${firstName.val()} ${lastName.val()}`,
3697
+ * [firstName, lastName]
3698
+ * );
3699
+ *
3700
+ * // With batch function
3701
+ * const batch = Observable.batch(() => { ... });
3702
+ * const computed = Observable.computed(() => { ... }, batch);
3703
+ */
3279
3704
  Observable.computed = function(callback, dependencies = []) {
3280
3705
  const initialValue = callback();
3281
3706
  const observable = new ObservableItem(initialValue);
3282
3707
  const updatedValue = nextTick(() => observable.set(callback()));
3283
3708
  {
3284
- PluginsManager.emit('CreateObservableComputed', observable, dependencies);
3709
+ PluginsManager$1.emit('CreateObservableComputed', observable, dependencies);
3285
3710
  }
3286
3711
 
3287
3712
  if(Validator.isFunction(dependencies)) {
@@ -3379,12 +3804,24 @@ var NativeDocument = (function (exports) {
3379
3804
  }());
3380
3805
 
3381
3806
  /**
3807
+ * Renders a list of items from an observable array or object, automatically updating when data changes.
3808
+ * Efficiently manages DOM updates by tracking items with keys.
3382
3809
  *
3383
- * @param {Array|Object|ObservableItem} data
3384
- * @param {Function} callback
3385
- * @param {?Function|?string} key
3386
- * @param {{shouldKeepItemsInCache: boolean}?} configs
3387
- * @returns {DocumentFragment}
3810
+ * @param {ObservableItem<Array|Object>} data - Observable containing array or object to iterate over
3811
+ * @param {Function} callback - Function that renders each item (item, index) => ValidChild
3812
+ * @param {string|Function} [key] - Property name or function to generate unique keys for items
3813
+ * @param {Object} [options={}] - Configuration options
3814
+ * @param {boolean} [options.shouldKeepItemsInCache=false] - Whether to cache rendered items
3815
+ * @returns {AnchorDocumentFragment} Fragment managing the list rendering
3816
+ * @example
3817
+ * const users = Observable([
3818
+ * { id: 1, name: 'John' },
3819
+ * { id: 2, name: 'Jane' }
3820
+ * ]);
3821
+ * ForEach(users, (user) => Div({}, user.name), 'id');
3822
+ *
3823
+ * // With function key
3824
+ * ForEach(items, (item) => Div({}, item), (item) => item.id);
3388
3825
  */
3389
3826
  function ForEach(data, callback, key, { shouldKeepItemsInCache = false } = {}) {
3390
3827
  const element = Anchor('ForEach');
@@ -3441,7 +3878,7 @@ var NativeDocument = (function (exports) {
3441
3878
  }
3442
3879
  cache.set(keyId, { keyId, isNew: true, child: new WeakRef(child), indexObserver});
3443
3880
  } catch (e) {
3444
- DebugManager.error('ForEach', `Error creating element for key ${keyId}` , e);
3881
+ DebugManager$1.error('ForEach', `Error creating element for key ${keyId}` , e);
3445
3882
  throw e;
3446
3883
  }
3447
3884
  return keyId;
@@ -3523,8 +3960,26 @@ var NativeDocument = (function (exports) {
3523
3960
  return element;
3524
3961
  }
3525
3962
 
3963
+ /**
3964
+ * Renders items from an ObservableArray with optimized array-specific updates.
3965
+ * Provides index observables and handles array mutations efficiently.
3966
+ *
3967
+ * @param {ObservableArray} data - ObservableArray to iterate over
3968
+ * @param {Function} callback - Function that renders each item (item, indexObservable) => ValidChild
3969
+ * @param {Object} [configs={}] - Configuration options
3970
+ * @param {boolean} [configs.shouldKeepItemsInCache] - Whether to cache rendered items
3971
+ * @param {boolean} [configs.isParentUniqueChild] - When it's the only child of the parent
3972
+ * @returns {AnchorDocumentFragment} Fragment managing the list rendering
3973
+ * @example
3974
+ * const items = Observable.array([1, 2, 3]);
3975
+ * ForEachArray(items, (item, index) =>
3976
+ * Div({}, `Item ${item} at index ${index.val()}`)
3977
+ * );
3978
+ *
3979
+ * items.push(4); // Automatically updates DOM
3980
+ */
3526
3981
  function ForEachArray(data, callback, configs = {}) {
3527
- const element = Anchor('ForEach Array');
3982
+ const element = Anchor('ForEach Array', configs.isParentUniqueChild);
3528
3983
  const blockEnd = element.endElement();
3529
3984
  const blockStart = element.startElement();
3530
3985
 
@@ -3699,7 +4154,7 @@ var NativeDocument = (function (exports) {
3699
4154
  elementBeforeFirst = firstChildRemoved?.previousSibling;
3700
4155
 
3701
4156
  for(let i = 0; i < deleted.length; i++) {
3702
- firstItem(deleted[i], garbageFragment);
4157
+ removeByItem(deleted[i], garbageFragment);
3703
4158
  }
3704
4159
  }
3705
4160
  } else {
@@ -3778,16 +4233,22 @@ var NativeDocument = (function (exports) {
3778
4233
  }
3779
4234
 
3780
4235
  /**
3781
- * Show the element if the condition is true
4236
+ * Conditionally shows an element based on an observable condition.
4237
+ * The element is mounted/unmounted from the DOM as the condition changes.
3782
4238
  *
3783
- * @param {ObservableItem|ObservableChecker} condition
3784
- * @param {*} child
3785
- * @param {{comment?: string|null, shouldKeepInCache?: Boolean}} configs
3786
- * @returns {DocumentFragment}
4239
+ * @param {ObservableItem<boolean>|ObservableChecker<boolean>|ObservableWhen} condition - Observable condition to watch
4240
+ * @param {ValidChild} child - Element or content to show/hide
4241
+ * @param {Object} [options={}] - Configuration options
4242
+ * @param {string|null} [options.comment=null] - Comment for debugging
4243
+ * @param {boolean} [options.shouldKeepInCache=true] - Whether to cache the element when hidden
4244
+ * @returns {AnchorDocumentFragment} Anchor fragment managing the conditional content
4245
+ * @example
4246
+ * const isVisible = Observable(false);
4247
+ * ShowIf(isVisible, Div({}, 'Hello World'));
3787
4248
  */
3788
4249
  const ShowIf = function(condition, child, { comment = null, shouldKeepInCache = true} = {}) {
3789
4250
  if(!(Validator.isObservable(condition)) && !Validator.isObservableWhenResult(condition)) {
3790
- return DebugManager.warn('ShowIf', "ShowIf : condition must be an Observable / "+comment, condition);
4251
+ return DebugManager$1.warn('ShowIf', "ShowIf : condition must be an Observable / "+comment, condition);
3791
4252
  }
3792
4253
  const element = Anchor('Show if : '+(comment || ''));
3793
4254
 
@@ -3820,11 +4281,18 @@ var NativeDocument = (function (exports) {
3820
4281
  };
3821
4282
 
3822
4283
  /**
3823
- * Hide the element if the condition is true
3824
- * @param {ObservableItem|ObservableChecker} condition
3825
- * @param child
3826
- * @param {{comment?: string|null, shouldKeepInCache?: Boolean}} configs
3827
- * @returns {DocumentFragment}
4284
+ * Conditionally hides an element when the observable condition is true.
4285
+ * Inverse of ShowIf - element is shown when condition is false.
4286
+ *
4287
+ * @param {ObservableItem<boolean>|ObservableChecker<boolean>} condition - Observable condition to watch
4288
+ * @param {ValidChild} child - Element or content to show/hide
4289
+ * @param {Object} [configs] - Configuration options
4290
+ * @param {string|null} [configs.comment] - Comment for debugging
4291
+ * @param {boolean} [configs.shouldKeepInCache] - Whether to cache element when hidden
4292
+ * @returns {AnchorDocumentFragment} Anchor fragment managing the conditional content
4293
+ * @example
4294
+ * const hasError = Observable(false);
4295
+ * HideIf(hasError, Div({}, 'Content'));
3828
4296
  */
3829
4297
  const HideIf = function(condition, child, configs) {
3830
4298
  const hideCondition = Observable(!condition.val());
@@ -3834,17 +4302,43 @@ var NativeDocument = (function (exports) {
3834
4302
  };
3835
4303
 
3836
4304
  /**
3837
- * Hide the element if the condition is false
4305
+ * Conditionally hides an element when the observable condition is false.
4306
+ * Same as ShowIf - element is shown when condition is true.
3838
4307
  *
3839
- * @param {ObservableItem|ObservableChecker} condition
3840
- * @param {*} child
3841
- * @param {{comment?: string|null, shouldKeepInCache?: Boolean}} configs
3842
- * @returns {DocumentFragment}
4308
+ * @param {ObservableItem<boolean>|ObservableChecker<boolean>|ObservableWhen} condition - Observable condition to watch
4309
+ * @param {ValidChild} child - Element or content to show/hide
4310
+ * @param {Object} [configs] - Configuration options
4311
+ * @param {string|null} [configs.comment] - Comment for debugging
4312
+ * @param {boolean} [configs.shouldKeepInCache] - Whether to cache element when hidden
4313
+ * @returns {AnchorDocumentFragment} Anchor fragment managing the conditional content
3843
4314
  */
3844
4315
  const HideIfNot = function(condition, child, configs) {
3845
4316
  return ShowIf(condition, child, configs);
3846
4317
  };
3847
4318
 
4319
+ /**
4320
+ * Shows content when an observable equals a specific value.
4321
+ * Can be called with 2 or 3 arguments.
4322
+ *
4323
+ * @overload
4324
+ * @param {ObservableWhen} observerWhenResult - Result from observable.when(value)
4325
+ * @param {ValidChild} view - Content to show when condition matches
4326
+ * @returns {AnchorDocumentFragment}
4327
+ *
4328
+ * @overload
4329
+ * @param {ObservableItem} observer - Observable to watch
4330
+ * @param {*} target - Value to match
4331
+ * @param {ValidChild} view - Content to show when observable equals target
4332
+ * @returns {AnchorDocumentFragment}
4333
+ *
4334
+ * @example
4335
+ * // 2 arguments
4336
+ * const status = Observable('idle');
4337
+ * ShowWhen(status.when('loading'), LoadingSpinner());
4338
+ *
4339
+ * // 3 arguments
4340
+ * ShowWhen(status, 'loading', LoadingSpinner());
4341
+ */
3848
4342
  const ShowWhen = function() {
3849
4343
  if(arguments.length === 2) {
3850
4344
  const [observer, target] = arguments;
@@ -3874,11 +4368,24 @@ var NativeDocument = (function (exports) {
3874
4368
  };
3875
4369
 
3876
4370
  /**
4371
+ * Displays different content based on the current value of an observable.
4372
+ * Like a switch statement for UI - shows the content corresponding to current value.
3877
4373
  *
3878
- * @param {ObservableItem|ObservableChecker} $condition
3879
- * @param {{[key]: *}} values
3880
- * @param {Boolean} shouldKeepInCache
3881
- * @returns {DocumentFragment}
4374
+ * @param {ObservableItem|ObservableChecker} $condition - Observable to watch
4375
+ * @param {Object<string|number, ValidChild>} values - Map of values to their corresponding content
4376
+ * @param {boolean} [shouldKeepInCache=true] - Whether to cache rendered views
4377
+ * @returns {AnchorDocumentFragment & {add: Function, remove: Function}} Fragment with dynamic methods
4378
+ * @example
4379
+ * const status = Observable('idle');
4380
+ * const view = Match(status, {
4381
+ * idle: Div({}, 'Ready'),
4382
+ * loading: Div({}, 'Loading...'),
4383
+ * error: Div({}, 'Error occurred')
4384
+ * });
4385
+ *
4386
+ * // Dynamic addition
4387
+ * view.add('success', Div({}, 'Success!'));
4388
+ * view.remove('idle');
3882
4389
  */
3883
4390
  const Match = function($condition, values, shouldKeepInCache = true) {
3884
4391
 
@@ -3935,11 +4442,19 @@ var NativeDocument = (function (exports) {
3935
4442
 
3936
4443
 
3937
4444
  /**
4445
+ * Displays one of two views based on a boolean observable condition.
4446
+ * Simplified version of Match for true/false cases.
3938
4447
  *
3939
- * @param {ObservableItem|ObservableChecker} $condition
3940
- * @param {*} onTrue
3941
- * @param {*} onFalse
3942
- * @returns {DocumentFragment}
4448
+ * @param {ObservableItem<boolean>|ObservableChecker<boolean>} $condition - Boolean observable to watch
4449
+ * @param {ValidChild} onTrue - Content to show when condition is true
4450
+ * @param {ValidChild} onFalse - Content to show when condition is false
4451
+ * @returns {AnchorDocumentFragment} Fragment managing the conditional content
4452
+ * @example
4453
+ * const isLoggedIn = Observable(false);
4454
+ * Switch(isLoggedIn,
4455
+ * Div({}, 'Welcome back!'),
4456
+ * Div({}, 'Please login')
4457
+ * );
3943
4458
  */
3944
4459
  const Switch = function ($condition, onTrue, onFalse) {
3945
4460
  if(!Validator.isObservable($condition)) {
@@ -3953,9 +4468,15 @@ var NativeDocument = (function (exports) {
3953
4468
  };
3954
4469
 
3955
4470
  /**
4471
+ * Provides a fluent API for conditional rendering with show/otherwise pattern.
3956
4472
  *
3957
- * @param {ObservableItem|ObservableChecker} $condition
3958
- * @returns {{show: Function, otherwise: (((*) => {}):DocumentFragment)}
4473
+ * @param {ObservableItem<boolean>|ObservableChecker<boolean>} $condition - Boolean observable to watch
4474
+ * @returns {{show: Function, otherwise: Function}} Object with fluent methods
4475
+ * @example
4476
+ * const isLoading = Observable(false);
4477
+ * When(isLoading)
4478
+ * .show(LoadingSpinner())
4479
+ * .otherwise(Content());
3959
4480
  */
3960
4481
  const When = function($condition) {
3961
4482
  if(!Validator.isObservable($condition)) {
@@ -3973,6 +4494,9 @@ var NativeDocument = (function (exports) {
3973
4494
  otherwise(onFalse) {
3974
4495
  $onFalse = onFalse;
3975
4496
  return Switch($condition, $onTrue, $onFalse);
4497
+ },
4498
+ toNdElement() {
4499
+ return Switch($condition, $onTrue, $onFalse);
3976
4500
  }
3977
4501
  }
3978
4502
  };
@@ -4312,11 +4836,16 @@ var NativeDocument = (function (exports) {
4312
4836
  };
4313
4837
 
4314
4838
  /**
4839
+ * Creates a new Route instance.
4315
4840
  *
4316
- * @param {string} $path
4317
- * @param {Function} $component
4318
- * @param {{name:?string, middlewares:Function[], shouldRebuild:Boolean, with: Object }}$options
4319
- * @class
4841
+ * @param {string} $path - URL pattern with optional parameters (e.g., '/user/{id:number}')
4842
+ * @param {Function} $component - Component function that returns HTMLElement or DocumentFragment
4843
+ * @param {Object} [$options={}] - Route configuration options
4844
+ * @param {string} [$options.name] - Unique name for the route (used for navigation)
4845
+ * @param {Function[]} [$options.middlewares] - Array of middleware functions
4846
+ * @param {boolean} [$options.shouldRebuild] - Whether to rebuild component on each navigation
4847
+ * @param {Object} [$options.with] - Custom parameter validation patterns
4848
+ * @param {Function} [$options.layout] - Layout component wrapper function
4320
4849
  */
4321
4850
  function Route($path, $component, $options = {}) {
4322
4851
 
@@ -4415,7 +4944,6 @@ var NativeDocument = (function (exports) {
4415
4944
  super(message);
4416
4945
  this.context = context;
4417
4946
  }
4418
-
4419
4947
  }
4420
4948
 
4421
4949
  const RouteGroupHelper = {
@@ -4574,7 +5102,7 @@ var NativeDocument = (function (exports) {
4574
5102
  window.history.pushState({ name: route.name(), params, path}, route.name() || path , path);
4575
5103
  this.handleRouteChange(route, params, query, path);
4576
5104
  } catch (e) {
4577
- DebugManager.error('HistoryRouter', 'Error in pushState', e);
5105
+ DebugManager$1.error('HistoryRouter', 'Error in pushState', e);
4578
5106
  }
4579
5107
  };
4580
5108
  /**
@@ -4587,7 +5115,7 @@ var NativeDocument = (function (exports) {
4587
5115
  window.history.replaceState({ name: route.name(), params, path}, route.name() || path , path);
4588
5116
  this.handleRouteChange(route, params, {}, path);
4589
5117
  } catch(e) {
4590
- DebugManager.error('HistoryRouter', 'Error in replaceState', e);
5118
+ DebugManager$1.error('HistoryRouter', 'Error in replaceState', e);
4591
5119
  }
4592
5120
  };
4593
5121
  this.forward = function() {
@@ -4614,7 +5142,7 @@ var NativeDocument = (function (exports) {
4614
5142
  }
4615
5143
  this.handleRouteChange(route, params, query, path);
4616
5144
  } catch(e) {
4617
- DebugManager.error('HistoryRouter', 'Error in popstate event', e);
5145
+ DebugManager$1.error('HistoryRouter', 'Error in popstate event', e);
4618
5146
  }
4619
5147
  });
4620
5148
  const { route, params, query, path } = this.resolve(defaultPath || (window.location.pathname+window.location.search));
@@ -4811,7 +5339,7 @@ var NativeDocument = (function (exports) {
4811
5339
  /**
4812
5340
  *
4813
5341
  * @param {{mode: 'memory'|'history'|'hash'}} $options
4814
- * @constructor
5342
+ * @class
4815
5343
  */
4816
5344
  function Router($options = {}) {
4817
5345
 
@@ -4839,7 +5367,7 @@ var NativeDocument = (function (exports) {
4839
5367
  listener(request);
4840
5368
  next && next(request);
4841
5369
  } catch (e) {
4842
- DebugManager.warn('Route Listener', 'Error in listener:', e);
5370
+ DebugManager$1.warn('Route Listener', 'Error in listener:', e);
4843
5371
  }
4844
5372
  }
4845
5373
  };
@@ -4869,11 +5397,20 @@ var NativeDocument = (function (exports) {
4869
5397
  };
4870
5398
 
4871
5399
  /**
5400
+ * Groups routes under a common path prefix with shared options.
4872
5401
  *
4873
- * @param {string} suffix
4874
- * @param {{ middlewares: Function[], name: string}} options
4875
- * @param {Function} callback
4876
- * @returns {this}
5402
+ * @param {string} suffix - Path prefix to prepend to all routes in the group
5403
+ * @param {Object} options - Group configuration options
5404
+ * @param {Function[]} [options.middlewares] - Middlewares applied to all routes in group
5405
+ * @param {string} [options.name] - Name prefix for all routes in group
5406
+ * @param {Function} [options.layout] - Layout component for all routes in group
5407
+ * @param {Function} callback - Function that defines routes within the group
5408
+ * @returns {this} Router instance for chaining
5409
+ * @example
5410
+ * router.group('/admin', { middlewares: [authMiddleware], layout: AdminLayout }, () => {
5411
+ * router.add('/users', UsersPage, { name: 'users' });
5412
+ * router.add('/settings', SettingsPage, { name: 'settings' });
5413
+ * });
4877
5414
  */
4878
5415
  this.group = function(suffix, options, callback) {
4879
5416
  if(!Validator.isFunction(callback)) {
@@ -4991,14 +5528,24 @@ var NativeDocument = (function (exports) {
4991
5528
  Router.routers = {};
4992
5529
 
4993
5530
  /**
5531
+ * Creates and initializes a new router instance.
4994
5532
  *
4995
- * @param {{mode: 'memory'|'history'|'hash', name?:string, entry?: string}} options
4996
- * @param {Function} callback
4997
- * @param {Element} container
5533
+ * @param {Object} options - Router configuration
5534
+ * @param {'memory'|'history'|'hash'} options.mode - Routing mode
5535
+ * @param {string} [options.name] - Router name for multi-router apps
5536
+ * @param {string} [options.entry] - Initial route path
5537
+ * @param {Function} callback - Setup function that receives the router instance
5538
+ * @returns {Router} The configured router instance with mount() method
5539
+ * @example
5540
+ * const router = Router.create({ mode: 'history' }, (r) => {
5541
+ * r.add('/home', HomePage, { name: 'home' });
5542
+ * r.add('/about', AboutPage, { name: 'about' });
5543
+ * });
5544
+ * router.mount('#app');
4998
5545
  */
4999
5546
  Router.create = function(options, callback) {
5000
5547
  if(!Validator.isFunction(callback)) {
5001
- DebugManager.error('Router', 'Callback must be a function', e);
5548
+ DebugManager$1.error('Router', 'Callback must be a function');
5002
5549
  throw new RouterError('Callback must be a function');
5003
5550
  }
5004
5551
  const router = new Router(options);
@@ -5165,16 +5712,22 @@ var NativeDocument = (function (exports) {
5165
5712
  };
5166
5713
  }
5167
5714
 
5168
- const Service = {
5169
- once: fn => autoOnce(fn),
5170
- memoize: fn => autoMemoize(fn)
5171
- };
5715
+ const once = fn => autoOnce(fn);
5716
+ const singleton = fn => once$1(fn);
5717
+ const memoize = fn => autoMemoize(fn);
5718
+
5719
+ var cache = /*#__PURE__*/Object.freeze({
5720
+ __proto__: null,
5721
+ memoize: memoize,
5722
+ once: once,
5723
+ singleton: singleton
5724
+ });
5172
5725
 
5173
5726
  var utils = /*#__PURE__*/Object.freeze({
5174
5727
  __proto__: null,
5175
- Filters: index,
5728
+ Cache: cache,
5176
5729
  NativeFetch: NativeFetch,
5177
- Service: Service
5730
+ filters: index
5178
5731
  });
5179
5732
 
5180
5733
  exports.$ = $;
@@ -5182,7 +5735,7 @@ var NativeDocument = (function (exports) {
5182
5735
  exports.HtmlElementWrapper = HtmlElementWrapper;
5183
5736
  exports.NDElement = NDElement;
5184
5737
  exports.Observable = Observable;
5185
- exports.PluginsManager = PluginsManager;
5738
+ exports.PluginsManager = PluginsManager$1;
5186
5739
  exports.SingletonView = SingletonView;
5187
5740
  exports.Store = Store;
5188
5741
  exports.TemplateCloner = TemplateCloner;
@@ -5193,10 +5746,10 @@ var NativeDocument = (function (exports) {
5193
5746
  exports.createTextNode = createTextNode;
5194
5747
  exports.cssPropertyAccumulator = cssPropertyAccumulator;
5195
5748
  exports.elements = elements;
5196
- exports.memoize = memoize;
5749
+ exports.memoize = memoize$1;
5197
5750
  exports.normalizeComponentArgs = normalizeComponentArgs;
5198
5751
  exports.obs = obs;
5199
- exports.once = once;
5752
+ exports.once = once$1;
5200
5753
  exports.router = router;
5201
5754
  exports.useCache = useCache;
5202
5755
  exports.useSingleton = useSingleton;