rasti 4.0.0 → 4.0.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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <p align="center">
2
2
  <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.0/docs/logo-dark.svg">
4
- <img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.0/docs/logo.svg" height="120">
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.1/docs/logo-dark.svg">
4
+ <img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.1/docs/logo.svg" height="120">
5
5
  </picture>
6
6
  </p>
7
7
 
@@ -58,8 +58,8 @@ import { Model, Component } from 'https://esm.run/rasti';
58
58
 
59
59
  Include **Rasti** directly in your HTML using a CDN. Available UMD builds:
60
60
 
61
- - [https://cdn.jsdelivr.net/npm/rasti/dist/rasti.js](https://cdn.jsdelivr.net/npm/rasti/dist/rasti.js)
62
- - [https://cdn.jsdelivr.net/npm/rasti/dist/rasti.min.js](https://cdn.jsdelivr.net/npm/rasti/dist/rasti.min.js)
61
+ - [rasti.js](https://cdn.jsdelivr.net/npm/rasti/dist/rasti.js)
62
+ - [rasti.min.js](https://cdn.jsdelivr.net/npm/rasti/dist/rasti.min.js)
63
63
 
64
64
  ```html
65
65
  <script src="https://cdn.jsdelivr.net/npm/rasti"></script>
@@ -123,7 +123,7 @@ const Navigation = Component.create`
123
123
  // Create a Main component that includes the Navigation and displays the current route's label as the title.
124
124
  const Main = Component.create`
125
125
  <main>
126
- <${Navigation} routes=${({ props }) => props.routes} />
126
+ <${Navigation} routes="${({ props }) => props.routes}" />
127
127
  <section>
128
128
  <h1>
129
129
  ${({ model, props }) => props.routes.find(
@@ -179,7 +179,7 @@ Counter.mount({ model }, document.body);
179
179
  - **Lightweight and Efficient**
180
180
  Minimal footprint with optimized performance, ensuring smooth updates.
181
181
  - **Just the Right Abstraction**
182
- Keeps you close to the DOM with no over-engineering. Fully hackable—if you're curious about how something works, just check the source code.
182
+ Keeps you close to the DOM with no over-engineering. Fully hackable, if you're curious about how something works, just check the source code.
183
183
 
184
184
  ## Example
185
185
 
package/dist/rasti.js CHANGED
@@ -1031,12 +1031,17 @@
1031
1031
  if (this.delegatedEventListeners.length) this.undelegateEvents();
1032
1032
 
1033
1033
  // Store events by type i.e.: "click", "submit", etc.
1034
- let eventTypes = {};
1034
+ const eventTypes = {};
1035
1035
 
1036
1036
  Object.keys(events).forEach(key => {
1037
- const keyParts = key.split(' ');
1038
- const type = keyParts.shift();
1039
- const selector = keyParts.join(' ');
1037
+ const match = key.match(/^(\w+)(?:\s+(.+))*$/);
1038
+
1039
+ if (!match) {
1040
+ const message = `Invalid event format: ${key}`;
1041
+ throw new Error(createDevelopmentErrorMessage(message) );
1042
+ }
1043
+ // Extract type and selector from the event key.
1044
+ const [,type, selector] = match;
1040
1045
 
1041
1046
  let listener = events[key];
1042
1047
  // Listener may be a string representing a method name on the view, or a function.
@@ -1046,32 +1051,30 @@
1046
1051
 
1047
1052
  if (!eventTypes[type]) eventTypes[type] = [];
1048
1053
 
1049
- eventTypes[type].push({ selector, listener });
1054
+ eventTypes[type].push([selector, listener]);
1050
1055
  });
1051
1056
 
1052
1057
  Object.keys(eventTypes).forEach(type => {
1053
1058
  // Listener for the type of event.
1054
1059
  const typeListener = (event) => {
1055
- // Iterate and run every individual listener if the selector matches.
1056
- eventTypes[type].forEach(({ selector, listener }) => {
1057
- // No selector provided: invoke listener once with root element.
1058
- if (!selector) {
1059
- listener.call(this, event, this, this.el);
1060
- return;
1061
- }
1062
-
1063
- let node = event.target;
1064
- // Traverse ancestors until reaching the view root (`this.el`).
1065
- while (node && node !== this.el) {
1066
- if (node.matches && node.matches(selector)) {
1067
- listener.call(this, event, this, node);
1068
- }
1069
- node = node.parentElement;
1060
+ let node = event.target;
1061
+ // Traverse ancestors until reaching the view root (`this.el`).
1062
+ while (node) {
1063
+ if (node.matches) {
1064
+ // Iterate and run every individual listener if the selector matches.
1065
+ eventTypes[type].forEach(([selector, listener]) => {
1066
+ if ((node === this.el && !selector) || (node !== this.el && node.matches(selector))) {
1067
+ listener.call(this, event, this, node);
1068
+ }
1069
+ });
1070
1070
  }
1071
- });
1071
+ // Continue traversing ancestors until reaching the view root (`this.el`) or stopping propagation.
1072
+ node = node === this.el || event.cancelBubble ? null : node.parentElement;
1073
+ }
1072
1074
  };
1073
-
1074
- this.delegatedEventListeners.push({ type, listener : typeListener });
1075
+ // Store the type and listener in the delegated event listeners array.
1076
+ this.delegatedEventListeners.push([type, typeListener]);
1077
+ // Add the event listener to the element.
1075
1078
  this.el.addEventListener(type, typeListener);
1076
1079
  });
1077
1080
  // Return `this` for chaining.
@@ -1085,7 +1088,7 @@
1085
1088
  * @return {View} Return `this` for chaining.
1086
1089
  */
1087
1090
  undelegateEvents() {
1088
- this.delegatedEventListeners.forEach(({ type, listener }) => {
1091
+ this.delegatedEventListeners.forEach(([type, listener]) => {
1089
1092
  this.el.removeEventListener(type, listener);
1090
1093
  });
1091
1094
 
@@ -1944,15 +1947,12 @@
1944
1947
  const isComponent = (el) => !!(el && el.dataset && el.dataset[Component.DATASET_ELEMENT] && el.dataset[Component.DATASET_ELEMENT].endsWith('-1'));
1945
1948
 
1946
1949
  /**
1947
- * Check if an element contains a component.
1948
- * It checks if the element is a component root element or if it contains a component.
1950
+ * Check if an element contains (or is) a dynamic element.
1949
1951
  * @param {Element} el The element to check.
1950
- * @return {boolean} True if the element contains a component.
1952
+ * @return {boolean} True if the element contains (or is) a dynamic element.
1951
1953
  * @private
1952
1954
  */
1953
- const containsElement = (el) => !!(
1954
- el && ((el.dataset && el.dataset[Component.DATASET_ELEMENT]) || (el.querySelector && el.querySelector(`[${Component.ATTRIBUTE_ELEMENT}]`)))
1955
- );
1955
+ const containsElement = (el) => !!(el && ((el.dataset && el.dataset[Component.DATASET_ELEMENT]) || (el.querySelector && el.querySelector(`[${Component.ATTRIBUTE_ELEMENT}]`))));
1956
1956
 
1957
1957
  /**
1958
1958
  * Generate string with placeholders for interpolated expressions.
@@ -2525,12 +2525,12 @@
2525
2525
  element.hydrate(this.el);
2526
2526
  }
2527
2527
  });
2528
- // Delegate events.
2529
- this.delegateEvents();
2530
2528
  // Get references for interpolation marker comments
2531
2529
  this.template.interpolations.forEach(interpolation => interpolation.hydrate(this.el));
2532
2530
  this.children.forEach(child => child.hydrate(this.el));
2533
2531
  }
2532
+ // Delegate events.
2533
+ this.delegateEvents();
2534
2534
  // Call `onHydrate` lifecycle method.
2535
2535
  this.onHydrate.call(this);
2536
2536
  // Return `this` for chaining.
@@ -2693,7 +2693,7 @@
2693
2693
 
2694
2694
  /**
2695
2695
  * Render the component as a string.
2696
- * Used internally on the render process.
2696
+ * Used internally on the render process.
2697
2697
  * Use it for server-side rendering or static site generation.
2698
2698
  * @return {string} The rendered component.
2699
2699
  * @example
@@ -2710,10 +2710,10 @@
2710
2710
  * const app = new App();
2711
2711
  *
2712
2712
  * console.log(app.toString());
2713
- * // <div data-rasti-el="r1-1"><!--rasti-s-r1-1--><button class="button" data-rasti-el="r2-1">Click me</button><!--rasti-e-r1-1--></div>
2713
+ * // <div data-rst-el="r1-1"><!--rst-s-r1-1--><button class="button" data-rst-el="r2-1">Click me</button><!--rst-e-r1-1--></div>
2714
2714
  *
2715
2715
  * console.log(`${app}`);
2716
- * // <div data-rasti-el="r1-1"><!--rasti-s-r1-1--><button class="button" data-rasti-el="r2-1">Click me</button><!--rasti-e-r1-1--></div>
2716
+ * // <div data-rst-el="r1-1"><!--rst-s-r1-1--><button class="button" data-rst-el="r2-1">Click me</button><!--rst-e-r1-1--></div>
2717
2717
  */
2718
2718
  toString() {
2719
2719
  // Normally there won't be any children, but if there are, destroy them.
@@ -2875,10 +2875,10 @@
2875
2875
  } else {
2876
2876
  // Update elements attributes.
2877
2877
  this.template.elements.forEach(element => element.update());
2878
- // If there are pending event types, delegate events again.
2879
- if (this.eventsManager.hasPendingTypes()) {
2880
- this.delegateEvents();
2881
- }
2878
+ }
2879
+ // If there are pending event types, delegate events again.
2880
+ if (this.eventsManager.hasPendingTypes()) {
2881
+ this.delegateEvents();
2882
2882
  }
2883
2883
  // Call onUpdate lifecycle method.
2884
2884
  this.onUpdate.call(this);
@@ -3283,9 +3283,9 @@
3283
3283
  * // Increment `model.seconds` every second.
3284
3284
  * setInterval(() => model.seconds++, 1000);
3285
3285
  */
3286
- var Component$1 = Component.create`<div></div>`;
3286
+ var Component_default = Component.create`<div></div>`;
3287
3287
 
3288
- exports.Component = Component$1;
3288
+ exports.Component = Component_default;
3289
3289
  exports.Emitter = Emitter;
3290
3290
  exports.Model = Model;
3291
3291
  exports.View = View;