rasti 3.0.0-alpha.3 → 3.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/dist/rasti.js CHANGED
@@ -323,15 +323,7 @@
323
323
  /*
324
324
  * These option keys will be extended on the view instance.
325
325
  */
326
- const viewOptions = {
327
- el : true,
328
- tag : true,
329
- attributes : true,
330
- events : true,
331
- model : true,
332
- template : true,
333
- onDestroy : true
334
- };
326
+ const viewOptions = ['el', 'tag', 'attributes', 'events', 'model', 'template', 'onDestroy'];
335
327
 
336
328
  /**
337
329
  * - Listens for changes and renders the UI.
@@ -354,6 +346,7 @@
354
346
  * @property {object|function} events Object in the format `{'event selector' : 'listener'}`. It will be used to bind delegated event listeners to the root element. If it is a function, it will be called to get the events object, bound to the view instance. See {@link module_view_delegateevents View.delegateEvents}.
355
347
  * @property {object} model A model or any object containing data and business logic.
356
348
  * @property {function} template A function that returns a string with the view's inner HTML. See {@link module_view__render View.render}.
349
+ * @property {number} uid Unique identifier for the view instance. This can be used to generate unique IDs for elements within the view. It is automatically generated and should not be set manually.
357
350
  * @example
358
351
  * import { View } from 'rasti';
359
352
  *
@@ -382,7 +375,7 @@
382
375
  this.preinitialize.apply(this, arguments);
383
376
  // Generate unique id.
384
377
  // Useful to generate element ids.
385
- this.uid = `uid${++View.uid}`;
378
+ this.uid = `rasti-${++View.uid}`;
386
379
  // Store delegated event listeners,
387
380
  // so they can be unbound later.
388
381
  this.delegatedEventListeners = [];
@@ -391,9 +384,9 @@
391
384
  this.children = [];
392
385
  // Mutable array to store handlers to be called on destroy.
393
386
  this.destroyQueue = [];
394
- // Extend "this" with options, mapping viewOptions keys.
395
- Object.keys(options).forEach(key => {
396
- if (viewOptions[key]) this[key] = options[key];
387
+ // Extend "this" with options.
388
+ viewOptions.forEach(key => {
389
+ if (key in options) this[key] = options[key];
397
390
  });
398
391
  // Ensure that the view has a root element at `this.el`.
399
392
  this.ensureElement();
@@ -622,17 +615,16 @@
622
615
  }
623
616
 
624
617
  /**
625
- * Renders the view.
618
+ * Renders the view.
626
619
  * This method should be overridden with custom logic.
627
620
  * The only convention is to manipulate the DOM within the scope of `this.el`,
628
- * and to return `this` for chaining.
629
- * If you add any child views, you should call `this.destroyChildren` before re-rendering.
630
- * The default implementation sets the innerHTML of `this.el` with the result
631
- * of calling `this.template`, passing `this.model` as an argument.
632
- * <br><br> &#9888; **Security Notice:** The default implementation utilizes `innerHTML` on the root element
633
- * for rendering, which may introduce Cross-Site Scripting (XSS) risks. Ensure that any user-generated
634
- * content is properly sanitized before inserting it into the DOM. You can use the @link{#module_view_sanitize View.sanitize}
635
- * static method to escape HTML entities in a string.
621
+ * and to return `this` for chaining.
622
+ * If you add any child views, you should call `this.destroyChildren` before re-rendering.
623
+ * The default implementation updates `this.el`'s innerHTML with the result
624
+ * of calling `this.template`, passing `this.model` as the argument.
625
+ * <br><br> &#9888; **Security Notice:** The default implementation utilizes `innerHTML`, which may introduce Cross-Site Scripting (XSS) risks.
626
+ * Ensure that any user-generated content is properly sanitized before inserting it into the DOM.
627
+ * You can use the {@link #module_view_sanitize View.sanitize} static method to escape HTML entities in a string.
636
628
  * For best practices on secure data handling, refer to the
637
629
  * [OWASP's XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html).<br><br>
638
630
  * @return {Rasti.View} Returns `this` for chaining.
@@ -645,9 +637,9 @@
645
637
 
646
638
  /**
647
639
  * Escape HTML entities in a string.
648
- * Use method to sanitize user-generated content before inserting it into the DOM.
640
+ * Use this method to sanitize user-generated content before inserting it into the DOM.
649
641
  * Override this method to provide a custom escape function.
650
- * This method is used by `Component` to escape template interpolations.
642
+ * This method is inherited by {@link #module_component Component} and used to escape template interpolations.
651
643
  * @static
652
644
  * @param {string} str String to escape.
653
645
  * @return {string} Escaped string.
@@ -663,8 +655,16 @@
663
655
  }
664
656
  }
665
657
 
666
- /*
667
- * Unique Id
658
+ /**
659
+ * Counter for generating unique IDs for view instances.
660
+ * This is primarily used to assign unique identifiers to each view instance (`this.uid`), which can be helpful for tasks like
661
+ * generating element IDs.
662
+ * {@link #module_component Component}s use `this.uid` to generate data attributes for their elements, to be looked up on hydration.
663
+ * For server-side rendering, this counter should be reset to `0` on every request to ensure that the generated
664
+ * unique IDs match those on the client, enabling seamless hydration of components.
665
+ * @static
666
+ * @type {number}
667
+ * @default 0
668
668
  */
669
669
  View.uid = 0;
670
670
 
@@ -679,8 +679,12 @@
679
679
  return acc;
680
680
  }, []);
681
681
 
682
- /*
682
+ /**
683
683
  * Wrapper class for HTML strings marked as safe.
684
+ * @class SafeHTML
685
+ * @param {string} value The HTML string to be marked as safe.
686
+ * @property {string} value The HTML string.
687
+ * @private
684
688
  */
685
689
  class SafeHTML {
686
690
  constructor(value) {
@@ -692,20 +696,22 @@
692
696
  }
693
697
  }
694
698
 
695
- /*
699
+ /**
696
700
  * Same as getResult, but pass context as argument to the expression.
697
701
  * Used to evaluate expressions in the context of a component.
698
702
  * @param {any} expression The expression to be evaluated.
699
703
  * @param {any} context The context to call the expression with.
700
704
  * @return {any} The result of the evaluated expression.
705
+ * @private
701
706
  */
702
707
  const getExpressionResult = (expression, context) => getResult(expression, context, context);
703
708
 
704
- /*
709
+ /**
705
710
  * Generate string with placeholders for interpolated expressions.
706
711
  * @param strings {array} Array of strings.
707
712
  * @param expressions {array} Array of expressions.
708
713
  * @return {string} String with placeholders.
714
+ * @private
709
715
  */
710
716
  const addPlaceholders = (strings, expressions) =>
711
717
  strings.reduce((out, string, i) => {
@@ -718,11 +724,12 @@
718
724
  return out;
719
725
  }, []).join('');
720
726
 
721
- /*
727
+ /**
722
728
  * Generate one dimensional array with strings and expressions.
723
729
  * @param main {string} The main template containing placeholders.
724
730
  * @param expressions {array} Array of expressions to replace placeholders.
725
731
  * @return {array} Array containing strings and expressions.
732
+ * @private
726
733
  */
727
734
  const splitPlaceholders = (main, expressions) => {
728
735
  const PH = Component.PLACEHOLDER_EXPRESSION('(\\d+)');
@@ -742,7 +749,7 @@
742
749
  return out;
743
750
  };
744
751
 
745
- /*
752
+ /**
746
753
  * Expand attributes.
747
754
  * @param attributes {array} Array of attributes as key, value pairs.
748
755
  * @param getExpressionResult {function} Function to render expressions.
@@ -750,6 +757,7 @@
750
757
  * @property {object} all All attributes.
751
758
  * @property {object} events Event listeners.
752
759
  * @property {object} attributes Attributes.
760
+ * @private
753
761
  */
754
762
  const expandAttributes = (attributes, getExpressionResult) => {
755
763
  const out = attributes.reduce((out, pair) => {
@@ -787,7 +795,7 @@
787
795
  return out;
788
796
  };
789
797
 
790
- /*
798
+ /**
791
799
  * Replace component tags with expressions.
792
800
  * `<${Component} />` or `<${Component}></${Component}>` will be replaced
793
801
  * by a function that mounts the component.
@@ -796,6 +804,7 @@
796
804
  * @param main {string} The main template.
797
805
  * @return {string} The template with components tags replaced by expressions
798
806
  * placeholders.
807
+ * @private
799
808
  */
800
809
  const expandComponents = (main, expressions) => {
801
810
  const PH = Component.PLACEHOLDER_EXPRESSION('(\\d+)');
@@ -835,7 +844,7 @@
835
844
  );
836
845
  };
837
846
 
838
- /*
847
+ /**
839
848
  * Parse match data to get tag, attributes, inner html and close tag.
840
849
  * @param match {array}
841
850
  * @return {object}
@@ -844,6 +853,7 @@
844
853
  * @property {string} close The closing tag.
845
854
  * @property {array} attributes Array of attributes as key, value pairs.
846
855
  * @property {string} raw The whole match.
856
+ * @private
847
857
  */
848
858
  const parseMatch = (match, expressions) => {
849
859
  const PH = Component.PLACEHOLDER_EXPRESSION('(\\d+)');
@@ -896,13 +906,7 @@
896
906
  /*
897
907
  * These option keys will be extended on the component instance.
898
908
  */
899
- const componentOptions = {
900
- key : true,
901
- state : true,
902
- onCreate : true,
903
- onChange : true,
904
- onRender : true
905
- };
909
+ const componentOptions = ['key', 'state', 'onCreate', 'onChange', 'onRender'];
906
910
 
907
911
  /**
908
912
  * Components are a special kind of `View` that is designed to be easily composable,
@@ -935,12 +939,14 @@
935
939
  class Component extends View {
936
940
  constructor(options = {}) {
937
941
  super(...arguments);
938
- // Extend "this" with options, mapping componentOptions keys.
939
- Object.keys(options).forEach(key => {
940
- if (componentOptions[key]) this[key] = options[key];
942
+ // Extend "this" with options.
943
+ componentOptions.forEach(key => {
944
+ if (key in options) this[key] = options[key];
941
945
  });
942
946
  // Store options by default.
943
947
  this.options = options;
948
+ // Bind `partial` method to `this`.
949
+ this.partial = this.partial.bind(this);
944
950
  // Call lifecycle method.
945
951
  this.onCreate.apply(this, arguments);
946
952
  }
@@ -970,20 +976,22 @@
970
976
  return this;
971
977
  }
972
978
 
973
- /*
974
- * Tell if component is a container.
979
+ /**
980
+ * Tell if `Component` is a container.
975
981
  * In which case, it will not have an element by itself.
976
982
  * It will render a single expression which is expected to return a single component as child.
977
983
  * `this.el` will be a reference to that child component's element.
978
984
  * @return {boolean}
985
+ * @private
979
986
  */
980
987
  isContainer() {
981
988
  return !!(!this.tag && this.template);
982
989
  }
983
990
 
984
- /*
985
- * Override. We don't want to ensure an element on instantiation.
991
+ /**
992
+ * Override super method. We don't want to ensure an element on instantiation.
986
993
  * We will provide it later.
994
+ * @private
987
995
  */
988
996
  ensureElement() {
989
997
  // If el is provided, delegate events.
@@ -994,18 +1002,25 @@
994
1002
  }
995
1003
  }
996
1004
 
997
- /*
998
- * Find view's element on parent node, using its data attribute.
999
- * @param parent {node} The parent node.
1000
- * @return {node} The component's element.
1005
+ /**
1006
+ * Locate the root element of the `Component` within a specified parent node.
1007
+ * This is achieved by searching for the element using the unique data attribute assigned to the `Component`.
1008
+ * @param {Node} parent - The parent node to search within.
1009
+ * @return {Node} The root element of the component, or `null` if not found.
1010
+ * @private
1001
1011
  */
1002
1012
  findElement(parent) {
1003
1013
  return (parent || document).querySelector(`[${Component.DATA_ATTRIBUTE_UID}="${this.uid}"]`);
1004
1014
  }
1005
1015
 
1006
- /*
1007
- * Eval attributes expressions.
1008
- * @return {object} Object containing add, remove and html properties.
1016
+ /**
1017
+ * Retrieve the attributes to be applied to the element.
1018
+ * This includes attributes to be added, removed, and their HTML representation.
1019
+ * @return {object} An object containing the following properties:
1020
+ * @property {object} add - Attributes to be added to the element, with their values.
1021
+ * @property {object} remove - Attributes to be removed from the element.
1022
+ * @property {string} html - A string representation of the attributes for use in HTML.
1023
+ * @private
1009
1024
  */
1010
1025
  getAttributes() {
1011
1026
  const add = {};
@@ -1044,11 +1059,13 @@
1044
1059
  return { add, remove, html : html.join(' ') };
1045
1060
  }
1046
1061
 
1047
- /*
1062
+ /**
1048
1063
  * Used internally on the render process.
1049
- * Attach the view to the dom element.
1064
+ * Attach the `Component` to the dom element providing `this.el`, delegate events,
1065
+ * subscribe to model changes and call `onRender` lifecycle method with `Component.RENDER_TYPE_HYDRATE` as argument.
1050
1066
  * @param parent {node} The parent node.
1051
1067
  * @return {Rasti.Component} The component instance.
1068
+ * @private
1052
1069
  */
1053
1070
  hydrate(parent) {
1054
1071
  // Listen to model changes and call onChange.
@@ -1056,13 +1073,13 @@
1056
1073
  // Listen to state changes and call onChange.
1057
1074
  if (this.state) this.subscribe(this.state);
1058
1075
 
1059
- if (!this.isContainer()) {
1076
+ if (this.isContainer()) {
1077
+ this.children[0].hydrate(parent);
1078
+ this.el = this.children[0].el;
1079
+ } else {
1060
1080
  this.el = this.findElement(parent);
1061
1081
  this.delegateEvents();
1062
1082
  this.children.forEach(child => child.hydrate(this.el));
1063
- } else {
1064
- this.children[0].hydrate(parent);
1065
- this.el = this.children[0].el;
1066
1083
  }
1067
1084
  // Call `onRender` lifecycle method.
1068
1085
  this.onRender.call(this, Component.RENDER_TYPE_HYDRATE);
@@ -1070,30 +1087,38 @@
1070
1087
  return this;
1071
1088
  }
1072
1089
 
1073
- /*
1074
- * Used internally in the render process.
1075
- * Reuse a view that has `key` when its parent is rendered.
1090
+ /**
1091
+ * Used internally on the render process.
1092
+ * Reuse a `Component` that has `key` when its parent is rendered.
1093
+ * Call `onRender` lifecycle method with `Component.RENDER_TYPE_RECYCLE` as argument.
1076
1094
  * @param parent {node} The parent node.
1077
1095
  * @return {Rasti.Component} The component instance.
1096
+ * @private
1078
1097
  */
1079
1098
  recycle(parent) {
1080
1099
  // If component is a container, call recycle on its child.
1081
- if (this.isContainer()) return this.children[0].recycle(parent);
1082
- // Find placeholder element to be replaced. It has same data attribute as this component.
1083
- const toBeReplaced = this.findElement(parent);
1084
- // Replace it with this.el.
1085
- toBeReplaced.replaceWith(this.el);
1100
+ if (this.isContainer()) {
1101
+ this.children[0].recycle(parent);
1102
+ } else {
1103
+ // Find placeholder element to be replaced. It has same data attribute as this component.
1104
+ const toBeReplaced = this.findElement(parent);
1105
+ // Replace it with this.el.
1106
+ toBeReplaced.replaceWith(this.el);
1107
+ }
1086
1108
  // Call `onRender` lifecycle method.
1087
1109
  this.onRender.call(this, Component.RENDER_TYPE_RECYCLE);
1088
1110
  // Return `this` for chaining.
1089
1111
  return this;
1090
1112
  }
1091
1113
 
1092
- /*
1093
- * Override. Add some custom logic to super `destroy` method.
1114
+ /**
1115
+ * Destroy the `Component`.
1116
+ * Destroy children components if any, undelegate events, stop listening to events, call `onDestroy` lifecycle method.
1094
1117
  * @param {object} options Options object or any arguments passed to `destroy` method will be passed to `onDestroy` method.
1118
+ * @return {Rasti.View} Return `this` for chaining.
1095
1119
  */
1096
1120
  destroy() {
1121
+ // Call super destroy method.
1097
1122
  super.destroy.apply(this, arguments);
1098
1123
  // Set destroyed flag to prevent a last render after destroyed.
1099
1124
  this.destroyed = true;
@@ -1122,8 +1147,11 @@
1122
1147
  }
1123
1148
 
1124
1149
  /**
1125
- * Lifecycle method. Called when the view is rendered.
1126
- * @param type {string} The render type. Can be `render`, `hydrate` or `recycle`.
1150
+ * Lifecycle method. Called after the component is rendered.
1151
+ * - When the component is rendered for the first time, this method is called with `Component.RENDER_TYPE_HYDRATE` as the argument.
1152
+ * - When the component is updated or re-rendered, this method is called with `Component.RENDER_TYPE_RENDER` as the argument.
1153
+ * - When the component is recycled (reused with the same key), this method is called with `Component.RENDER_TYPE_RECYCLE` as the argument.
1154
+ * @param {string} type - The render type. Possible values are: `Component.RENDER_TYPE_HYDRATE`, `Component.RENDER_TYPE_RENDER` and `Component.RENDER_TYPE_RECYCLE`.
1127
1155
  */
1128
1156
  onRender() {}
1129
1157
 
@@ -1137,7 +1165,9 @@
1137
1165
  * Tagged template helper method.
1138
1166
  * Used to create a partial template.
1139
1167
  * It will return a one-dimensional array with strings and expressions.
1140
- * Components will be added as children by the parent component. Template strings will be marked as safe HTML to be rendered.
1168
+ * Components will be added as children by the parent component. Template strings literals
1169
+ * will be marked as safe HTML to be rendered.
1170
+ * This method is bound to the component instance by default.
1141
1171
  * @param {TemplateStringsArray} strings - Template strings.
1142
1172
  * @param {...any} expressions - Template expressions.
1143
1173
  * @return {Array} Array containing strings and expressions.
@@ -1158,7 +1188,7 @@
1158
1188
  * renderHeader() {
1159
1189
  * return this.partial`
1160
1190
  * <header>
1161
- * <${Title}>${self => self.model.title}</${Title}>
1191
+ * <${Title}>${({ model }) => model.title}</${Title}>
1162
1192
  * </header>
1163
1193
  * `;
1164
1194
  * }
@@ -1203,22 +1233,26 @@
1203
1233
  `<${tag} ${attributes} />`;
1204
1234
  }
1205
1235
 
1206
- /*
1207
- * View render method.
1236
+ /**
1237
+ * Render the `Component`.
1238
+ * - If `this.el` is not present, the `Component` will be rendered as a string inside a `DocumentFragment` and hydrated, making `this.el` available. The `onRender` lifecycle method will be called with `Component.RENDER_TYPE_HYDRATE` as an argument.
1239
+ * - If `this.el` is present, the method will update the attributes and inner HTML of the element, or recreate its child component in the case of a container. The `onRender` lifecycle method will be called with `Component.RENDER_TYPE_RENDER` as an argument.
1240
+ * - When rendering child components, if the new children have the same key as the previous ones, they will be recycled. A recycled `Component` will call the `onRender` lifecycle method with `Component.RENDER_TYPE_RECYCLE` as an argument.
1241
+ * - If the active element is inside the component, it will retain focus after the render.
1242
+ * @return {Rasti.Component} The component instance.
1208
1243
  */
1209
1244
  render() {
1210
1245
  // Prevent a last re render if view is already destroyed.
1211
1246
  if (this.destroyed) return this;
1212
-
1247
+ // If `this.el` is not present, render the view as a string and hydrate it.
1248
+ if (!this.el) {
1249
+ const fragment = this.createElement('template');
1250
+ fragment.innerHTML = this;
1251
+ this.hydrate(fragment.content);
1252
+ return this;
1253
+ }
1254
+ // Update attributes.
1213
1255
  if (!this.isContainer()) {
1214
- // If `this.el` is not present, render the view as a string and hydrate it.
1215
- if (!this.el) {
1216
- const fragment = this.createElement('template');
1217
- fragment.innerHTML = this;
1218
- this.hydrate(fragment.content);
1219
- return this;
1220
- }
1221
- // Set `this.el` attributes.
1222
1256
  const attributes = this.getAttributes();
1223
1257
  // Remove attributes.
1224
1258
  Object.keys(attributes.remove).forEach(key => {
@@ -1229,7 +1263,7 @@
1229
1263
  this.el.setAttribute(key, attributes.add[key]);
1230
1264
  });
1231
1265
  }
1232
- // Check for `template` to see if view has innerHTML.
1266
+ // Check for `template` to see if view has innerHTML or a child component.
1233
1267
  if (this.template) {
1234
1268
  // Store active element.
1235
1269
  const activeElement = document.activeElement;
@@ -1270,8 +1304,8 @@
1270
1304
  this.addChild(nextChildren[0]).hydrate(fragment.content);
1271
1305
  // Get next child element.
1272
1306
  const nextEl = fragment.content.children[0];
1273
- // If `this.el` is present, replace it with nextEl.
1274
- if (this.el) this.el.replaceWith(nextEl);
1307
+ // Replace `this.el` with nextEl.
1308
+ this.el.replaceWith(nextEl);
1275
1309
  // Set `this.el` to nextEl.
1276
1310
  this.el = nextEl;
1277
1311
  } else if (recycledChildren[0]) {
@@ -1307,10 +1341,10 @@
1307
1341
  }
1308
1342
 
1309
1343
  /**
1310
- * Mark a string as safe HTML to be rendered.
1311
- * Normally you don't need to use this method, as Rasti will automatically mark strings
1312
- * as safe HTML when the component is @link{#module_component_create created} and when
1313
- * using the @link{#module_component__partial Component.partial} method.
1344
+ * Mark a string as safe HTML to be rendered.
1345
+ * Normally you don't need to use this method, as Rasti will automatically mark string literals
1346
+ * as safe HTML when the component is {@link #module_component_create created} and when
1347
+ * using the {@link #module_component__partial Component.partial} method.
1314
1348
  * Be sure that the string is safe to be rendered, as it will be inserted into the DOM without any sanitization.
1315
1349
  * @static
1316
1350
  * @param {string} value
@@ -1371,89 +1405,89 @@
1371
1405
  * Takes a tagged template string or a function that returns another component, and returns a new `Component` class.
1372
1406
  * - The template outer tag and attributes will be used to create the view's root element.
1373
1407
  * - The template inner HTML will be used as the view's template.
1374
- * ```javascript
1375
- * const Button = Component.create`<button class="button">Click me</button>`;
1376
- * ```
1408
+ * ```javascript
1409
+ * const Button = Component.create`<button class="button">Click me</button>`;
1410
+ * ```
1377
1411
  * - Template interpolations that are functions will be evaluated during the render process, receiving the view instance as an argument and being bound to it. If the function returns `null`, `undefined`, `false`, or an empty string, the interpolation won't render any content.
1378
- * ```javascript
1379
- * const Button = Component.create`
1380
- * <button class="${({ options }) => options.className}">
1381
- * ${({ options }) => options.renderChildren()}
1382
- * </button>
1383
- * `;
1384
- * ```
1412
+ * ```javascript
1413
+ * const Button = Component.create`
1414
+ * <button class="${({ options }) => options.className}">
1415
+ * ${({ options }) => options.renderChildren()}
1416
+ * </button>
1417
+ * `;
1418
+ * ```
1385
1419
  * - Event handlers should be passed, at the root element as camelized attributes, in the format `onEventName=${{'selector' : listener }}`. They will be transformed to an event object and delegated to the root element. See {@link #module_view__delegateevents View.delegateEvents}.
1386
- * - Boolean attributes should be passed in the form of `attribute="${() => true}"`. `false` attributes won't be rendered. `true` attributes will be rendered without a value.
1387
- * ```javascript
1388
- * const Input = Component.create`
1389
- * <input type="text" disabled=${({ options }) => options.disabled} />
1390
- * `;
1391
- * ```
1420
+ * - Boolean attributes should be passed in the format `attribute="${() => true}"`. `false` attributes won't be rendered. `true` attributes will be rendered without a value.
1421
+ * ```javascript
1422
+ * const Input = Component.create`
1423
+ * <input type="text" disabled=${({ options }) => options.disabled} />
1424
+ * `;
1425
+ * ```
1392
1426
  * - If the interpolated function returns a component instance, it will be added as a child component.
1393
1427
  * - If the interpolated function returns an array, each item will be evaluated as above.
1394
- * ```javascript
1395
- * // Create a button component.
1396
- * const Button = Component.create`
1397
- * <button class="button">
1398
- * ${({ options }) => options.renderChildren()}
1399
- * </button>
1400
- * `;
1401
- * // Create a navigation component. Add buttons as children. Iterate over items.
1402
- * const Navigation = Component.create`
1403
- * <nav>
1404
- * ${({ options }) => options.items.map(
1405
- * item => Button.mount({ renderChildren: () => item.label })
1406
- * )}
1407
- * </nav>
1408
- * `;
1409
- * // Create a header component. Add navigation as a child.
1410
- * const Header = Component.create`
1411
- * <header>
1412
- * ${({ options }) => Navigation.mount({ items : options.items})}
1413
- * </header>
1414
- * `;
1415
- * ```
1428
+ * ```javascript
1429
+ * // Create a button component.
1430
+ * const Button = Component.create`
1431
+ * <button class="button">
1432
+ * ${({ options }) => options.renderChildren()}
1433
+ * </button>
1434
+ * `;
1435
+ * // Create a navigation component. Add buttons as children. Iterate over items.
1436
+ * const Navigation = Component.create`
1437
+ * <nav>
1438
+ * ${({ options }) => options.items.map(
1439
+ * item => Button.mount({ renderChildren: () => item.label })
1440
+ * )}
1441
+ * </nav>
1442
+ * `;
1443
+ * // Create a header component. Add navigation as a child.
1444
+ * const Header = Component.create`
1445
+ * <header>
1446
+ * ${({ options }) => Navigation.mount({ items : options.items})}
1447
+ * </header>
1448
+ * `;
1449
+ * ```
1416
1450
  * - Child components can be added using a component tag.
1417
- * ```javascript
1418
- * // Create a button component.
1419
- * const Button = Component.create`
1420
- * <button class="button">
1421
- * ${({ options }) => options.renderChildren()}
1422
- * </button>
1423
- * `;
1424
- * // Create a navigation component. Add buttons as children. Iterate over items.
1425
- * const Navigation = Component.create`
1426
- * <nav>
1427
- * ${self => self.options.items.map(
1428
- * item => self.partial`<${Button}>${item.label}</${Button}>`
1429
- * )}
1430
- * </nav>
1431
- * `;
1432
- * // Create a header component. Add navigation as a child.
1433
- * const Header = Component.create`
1434
- * <header>
1435
- * <${Navigation} items="${({ options }) => options.items}" />
1436
- * </header>
1437
- * `;
1438
- * ```
1451
+ * ```javascript
1452
+ * // Create a button component.
1453
+ * const Button = Component.create`
1454
+ * <button class="button">
1455
+ * ${({ options }) => options.renderChildren()}
1456
+ * </button>
1457
+ * `;
1458
+ * // Create a navigation component. Add buttons as children. Iterate over items.
1459
+ * const Navigation = Component.create`
1460
+ * <nav>
1461
+ * ${({ options, partial }) => options.items.map(
1462
+ * item => partial`<${Button}>${item.label}</${Button}>`
1463
+ * )}
1464
+ * </nav>
1465
+ * `;
1466
+ * // Create a header component. Add navigation as a child.
1467
+ * const Header = Component.create`
1468
+ * <header>
1469
+ * <${Navigation} items="${({ options }) => options.items}" />
1470
+ * </header>
1471
+ * `;
1472
+ * ```
1439
1473
  * - If the tagged template contains only one expression that mounts a component, or the tags are references to a component, the component will be considered a <b>container</b>. It will render a single component as a child. `this.el` will be a reference to that child component's element.
1440
- * ```javascript
1441
- * // Create a button component.
1442
- * const Button = Component.create`
1443
- * <button class="${({ options }) => options.className}">
1444
- * ${self => self.renderChildren()}
1445
- * </button>
1446
- * `;
1447
- * // Create a container using the button component
1448
- * const ButtonOk = Component.create`
1449
- * <${Button} className="ok">Ok</${Button}>
1450
- * `;
1451
- * // Create a button component using a function
1452
- * const ButtonCancel = Component.create(() => Button.mount({
1453
- * className: 'cancel',
1454
- * renderChildren: () => 'Cancel'
1455
- * }));
1456
- * ```
1474
+ * ```javascript
1475
+ * // Create a button component.
1476
+ * const Button = Component.create`
1477
+ * <button class="${({ options }) => options.className}">
1478
+ * ${self => self.renderChildren()}
1479
+ * </button>
1480
+ * `;
1481
+ * // Create a container that renders a Button component.
1482
+ * const ButtonOk = Component.create`
1483
+ * <${Button} className="ok">Ok</${Button}>
1484
+ * `;
1485
+ * // Create a container that renders a Button component, using a function.
1486
+ * const ButtonCancel = Component.create(() => Button.mount({
1487
+ * className: 'cancel',
1488
+ * renderChildren: () => 'Cancel'
1489
+ * }));
1490
+ * ```
1457
1491
  * @static
1458
1492
  * @param {string|function} strings - HTML template for the component or a function that mounts a sub component.
1459
1493
  * @param {...*} expressions - The expressions to be interpolated within the template.
@@ -1508,7 +1542,7 @@
1508
1542
  if (typeof item !== 'undefined' && item !== null && item !== false && item !== true) {
1509
1543
  if (item instanceof SafeHTML) return item;
1510
1544
  if (item instanceof Component) return addChild(item);
1511
- return Component.sanitize(`${item}`);
1545
+ return Component.sanitize(item);
1512
1546
  }
1513
1547
  return '';
1514
1548
  }).join('');