rasti 2.0.0 → 2.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/rasti.js +2 -2
- package/dist/rasti.min.js +1 -1
- package/es/Component.js +143 -79
- package/es/View.js +1 -0
- package/lib/Component.js +143 -79
- package/lib/View.js +1 -0
- package/package.json +1 -1
- package/src/Component.js +152 -76
- package/src/View.js +1 -0
package/es/Component.js
CHANGED
|
@@ -94,11 +94,22 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/*
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
* Tell if component is a container.
|
|
98
|
+
* In which case, it will not have an element by itself.
|
|
99
|
+
* It will render a single expression which is expected to return a single component as child.
|
|
100
|
+
* `this.el` will be a reference to that child component's element.
|
|
101
|
+
* @return {boolean}
|
|
102
|
+
*/
|
|
100
103
|
_inheritsLoose(Component, _View);
|
|
101
104
|
var _proto = Component.prototype;
|
|
105
|
+
_proto.isContainer = function isContainer() {
|
|
106
|
+
return !!(!this.tag && this.template && this.template.inner && this.template.expressions && this.template.expressions.length === 1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* Override. We don't want to ensure an element on instantiation.
|
|
111
|
+
* We will provide it later.
|
|
112
|
+
*/;
|
|
102
113
|
_proto.ensureElement = function ensureElement() {
|
|
103
114
|
// If el is provided, delegate events.
|
|
104
115
|
if (this.el) {
|
|
@@ -106,7 +117,7 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
106
117
|
this.id = this.el.id;
|
|
107
118
|
}
|
|
108
119
|
// Ensure id.
|
|
109
|
-
if (!this.id) {
|
|
120
|
+
if (!this.isContainer() && !this.id) {
|
|
110
121
|
this.id = this.attributes && this.attributes.id ?
|
|
111
122
|
// If id is provided, evaluate it.
|
|
112
123
|
evalExpression(this.attributes.id, this, this) :
|
|
@@ -117,6 +128,8 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
117
128
|
|
|
118
129
|
/*
|
|
119
130
|
* Find view's element on parent node, using id.
|
|
131
|
+
* @param parent {node} The parent node.
|
|
132
|
+
* @return {node} The component's element.
|
|
120
133
|
*/;
|
|
121
134
|
_proto.findElement = function findElement(parent) {
|
|
122
135
|
return (parent || document).querySelector("#" + this.id);
|
|
@@ -124,6 +137,7 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
124
137
|
|
|
125
138
|
/*
|
|
126
139
|
* Eval attributes expressions.
|
|
140
|
+
* @return {object} Object containing add, remove and html properties.
|
|
127
141
|
*/;
|
|
128
142
|
_proto.getAttributes = function getAttributes() {
|
|
129
143
|
var _this2 = this;
|
|
@@ -161,40 +175,57 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
161
175
|
/*
|
|
162
176
|
* Used internally on the render process.
|
|
163
177
|
* Attach the view to the dom element.
|
|
178
|
+
* @param parent {node} The parent node.
|
|
179
|
+
* @return {Rasti.Component} The component instance.
|
|
164
180
|
*/;
|
|
165
181
|
_proto.hydrate = function hydrate(parent) {
|
|
166
182
|
var _this3 = this;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
183
|
+
if (!this.isContainer()) {
|
|
184
|
+
this.el = this.findElement(parent);
|
|
185
|
+
this.delegateEvents();
|
|
186
|
+
this.children.forEach(function (child) {
|
|
187
|
+
return child.hydrate(_this3.el);
|
|
188
|
+
});
|
|
189
|
+
} else {
|
|
190
|
+
this.children[0].hydrate(parent);
|
|
191
|
+
this.el = this.children[0].el;
|
|
192
|
+
}
|
|
193
|
+
// Call `onRender` lifecycle method.
|
|
172
194
|
this.onRender.call(this, 'hydrate');
|
|
195
|
+
// Return `this` for chaining.
|
|
196
|
+
return this;
|
|
173
197
|
}
|
|
174
198
|
|
|
175
199
|
/*
|
|
176
200
|
* Used internally on the render process.
|
|
177
201
|
* Reuse a view that has `key` when its parent is rendered.
|
|
202
|
+
* @param parent {node} The parent node.
|
|
203
|
+
* @return {Rasti.Component} The component instance.
|
|
178
204
|
*/;
|
|
179
205
|
_proto.recycle = function recycle(parent) {
|
|
180
206
|
// Find element to be replaced. It has same id.
|
|
181
|
-
var toBeReplaced = this.findElement(parent);
|
|
207
|
+
var toBeReplaced = (this.isContainer() ? this.children[0] : this).findElement(parent);
|
|
182
208
|
// Replace it with this.el.
|
|
183
209
|
toBeReplaced.replaceWith(this.el);
|
|
184
210
|
// Call `onRender` lifecycle method.
|
|
185
211
|
this.onRender.call(this, 'recycle');
|
|
212
|
+
// Return `this` for chaining.
|
|
213
|
+
return this;
|
|
186
214
|
}
|
|
187
215
|
|
|
188
216
|
/*
|
|
189
217
|
* Override. Add some custom logic to super `destroy` method.
|
|
218
|
+
* @param {object} options Options object or any arguments passed to `destroy` method will be passed to `onDestroy` method.
|
|
190
219
|
*/;
|
|
191
220
|
_proto.destroy = function destroy() {
|
|
192
221
|
_View.prototype.destroy.apply(this, arguments);
|
|
193
222
|
// Stop listening to `change`.
|
|
194
|
-
// Set destroyed flag to prevent a last render after destroyed. TODO: Review
|
|
195
223
|
if (this.model && this.model.off) this.model.off('change', this.onChange);
|
|
196
224
|
if (this.state && this.state.off) this.state.off('change', this.onChange);
|
|
225
|
+
// Set destroyed flag to prevent a last render after destroyed.
|
|
197
226
|
this.destroyed = true;
|
|
227
|
+
// Return `this` for chaining.
|
|
228
|
+
return this;
|
|
198
229
|
}
|
|
199
230
|
|
|
200
231
|
/**
|
|
@@ -257,12 +288,12 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
257
288
|
return out + parsed;
|
|
258
289
|
}, '');
|
|
259
290
|
})
|
|
260
|
-
// Replace `attribute="true"` with `attribute`
|
|
261
|
-
.replace(new RegExp("([
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
// Replace rest of false expressions with empty string.
|
|
265
|
-
.replace(new RegExp(Component.FALSE_PLACEHOLDER, 'g'), '');
|
|
291
|
+
// Replace `attribute="true"` with `attribute` and `attribute="false"` with empty string.
|
|
292
|
+
.replace(new RegExp("([\\w|data-]+)=([\"'])?(" + Component.TRUE_PLACEHOLDER + "|" + Component.FALSE_PLACEHOLDER + ")\\2", 'g'), function (match, attribute, quote, placeholder) {
|
|
293
|
+
return placeholder === Component.TRUE_PLACEHOLDER ? attribute : '';
|
|
294
|
+
})
|
|
295
|
+
// Replace rest of true or false expressions with empty string.
|
|
296
|
+
.replace(new RegExp(Component.TRUE_PLACEHOLDER + "|" + Component.FALSE_PLACEHOLDER, 'g'), '');
|
|
266
297
|
}
|
|
267
298
|
|
|
268
299
|
/*
|
|
@@ -272,17 +303,20 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
272
303
|
var _this5 = this;
|
|
273
304
|
// Normally there won't be any children, but if there are, destroy them.
|
|
274
305
|
this.destroyChildren();
|
|
275
|
-
// Get tag name.
|
|
276
|
-
var tag = this.tag || 'div';
|
|
277
|
-
// Get attributes.
|
|
278
|
-
var attributes = this.getAttributes().html;
|
|
279
306
|
// Replace expressions of inner template.
|
|
280
|
-
var
|
|
307
|
+
var close = this.template && this.template.inner;
|
|
308
|
+
var inner = close && this.replaceExpressions(this.template.inner, function (component) {
|
|
281
309
|
// Add child component.
|
|
282
310
|
return _this5.addChild(component);
|
|
283
311
|
});
|
|
312
|
+
// If component is a container, return inner which will be an expression thats return a single component.
|
|
313
|
+
if (this.isContainer()) return inner;
|
|
314
|
+
// Get tag name.
|
|
315
|
+
var tag = this.tag || 'div';
|
|
316
|
+
// Get attributes.
|
|
317
|
+
var attributes = this.getAttributes().html;
|
|
284
318
|
// Generate outer template.
|
|
285
|
-
return
|
|
319
|
+
return close ? "<" + tag + " " + attributes + ">" + inner + "</" + tag + ">" : "<" + tag + " " + attributes + " />";
|
|
286
320
|
}
|
|
287
321
|
|
|
288
322
|
/*
|
|
@@ -292,31 +326,33 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
292
326
|
var _this6 = this;
|
|
293
327
|
// Prevent a last re render if view is already destroyed.
|
|
294
328
|
if (this.destroyed) return this;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
329
|
+
if (!this.isContainer()) {
|
|
330
|
+
// If `this.el` is not present, create a new `this.tag` element.
|
|
331
|
+
if (!this.el) {
|
|
332
|
+
this.el = this.createElement(this.tag);
|
|
333
|
+
this.delegateEvents();
|
|
334
|
+
}
|
|
335
|
+
// Set `this.el` attributes.
|
|
336
|
+
var attributes = this.getAttributes();
|
|
337
|
+
// Remove attributes.
|
|
338
|
+
Object.keys(attributes.remove).forEach(function (key) {
|
|
339
|
+
_this6.el.removeAttribute(key);
|
|
340
|
+
});
|
|
341
|
+
// Add attributes.
|
|
342
|
+
Object.keys(attributes.add).forEach(function (key) {
|
|
343
|
+
_this6.el.setAttribute(key, attributes.add[key]);
|
|
344
|
+
});
|
|
299
345
|
}
|
|
300
|
-
// Set `this.el` attributes.
|
|
301
|
-
var attributes = this.getAttributes();
|
|
302
|
-
// Remove attributes.
|
|
303
|
-
Object.keys(attributes.remove).forEach(function (key) {
|
|
304
|
-
_this6.el.removeAttribute(key);
|
|
305
|
-
});
|
|
306
|
-
// Add attributes.
|
|
307
|
-
Object.keys(attributes.add).forEach(function (key) {
|
|
308
|
-
_this6.el.setAttribute(key, attributes.add[key]);
|
|
309
|
-
});
|
|
310
346
|
// Check for `template.inner` to see if view has innerHTML.
|
|
311
347
|
if (this.template && this.template.inner) {
|
|
312
|
-
var previousChildren = this.children;
|
|
313
|
-
this.children = [];
|
|
314
|
-
var nextChildren = [];
|
|
315
|
-
var recycledChildren = [];
|
|
316
348
|
// Store active element.
|
|
317
349
|
var activeElement = document.activeElement;
|
|
350
|
+
var nextChildren = [];
|
|
351
|
+
var recycledChildren = [];
|
|
352
|
+
var previousChildren = this.children;
|
|
353
|
+
this.children = [];
|
|
318
354
|
// Replace expressions. Set html inside of `this.el`.
|
|
319
|
-
|
|
355
|
+
var inner = this.replaceExpressions(this.template.inner, function (component) {
|
|
320
356
|
var out = component;
|
|
321
357
|
// Check if child already exists.
|
|
322
358
|
var found = component.key && previousChildren.find(function (previousChild) {
|
|
@@ -338,14 +374,32 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
338
374
|
// Component html.
|
|
339
375
|
return out;
|
|
340
376
|
});
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
377
|
+
if (this.isContainer()) {
|
|
378
|
+
if (nextChildren[0]) {
|
|
379
|
+
var fragment = this.createElement('template');
|
|
380
|
+
fragment.innerHTML = inner;
|
|
381
|
+
// Add new child to dom fragment and hydrate it.
|
|
382
|
+
this.addChild(nextChildren[0]).hydrate(fragment.content);
|
|
383
|
+
// Get next child element.
|
|
384
|
+
var nextEl = fragment.content.children[0];
|
|
385
|
+
// If `this.el` is present, replace it with nextEl.
|
|
386
|
+
if (this.el) this.el.replaceWith(nextEl);
|
|
387
|
+
// Set `this.el` to nextEl.
|
|
388
|
+
this.el = nextEl;
|
|
389
|
+
} else if (recycledChildren[0]) {
|
|
390
|
+
this.addChild(recycledChildren[0]);
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
this.el.innerHTML = inner;
|
|
394
|
+
// Add new children. Hydrate them.
|
|
395
|
+
nextChildren.forEach(function (nextChild) {
|
|
396
|
+
_this6.addChild(nextChild).hydrate(_this6.el);
|
|
397
|
+
});
|
|
398
|
+
// Replace children root elements with recycled components.
|
|
399
|
+
recycledChildren.forEach(function (recycledChild) {
|
|
400
|
+
_this6.addChild(recycledChild).recycle(_this6.el);
|
|
401
|
+
});
|
|
402
|
+
}
|
|
349
403
|
// Destroy unused children.
|
|
350
404
|
previousChildren.forEach(function (previousChild) {
|
|
351
405
|
var found = recycledChildren.indexOf(previousChild) > -1;
|
|
@@ -399,11 +453,11 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
399
453
|
if (hydrate) {
|
|
400
454
|
view.toString();
|
|
401
455
|
} else {
|
|
402
|
-
var fragment =
|
|
456
|
+
var fragment = this.prototype.createElement('template');
|
|
403
457
|
// Add html text into element inner html.
|
|
404
458
|
fragment.innerHTML = view;
|
|
405
459
|
// Add to dom.
|
|
406
|
-
el.appendChild(fragment.content);
|
|
460
|
+
el.appendChild(fragment.content.children[0]);
|
|
407
461
|
}
|
|
408
462
|
view.hydrate(el);
|
|
409
463
|
}
|
|
@@ -423,13 +477,19 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
423
477
|
* - If the function returns a component instance, it will be added as a child component.
|
|
424
478
|
* - If the function returns an array, each item will be evaluated as above.
|
|
425
479
|
* @static
|
|
426
|
-
* @param {string} HTML template for the component.
|
|
480
|
+
* @param {string} HTML template for the component. Or a function that mounts a sub component.
|
|
427
481
|
* @return {Rasti.Component}
|
|
428
482
|
*/;
|
|
429
483
|
Component.create = function create(strings) {
|
|
430
484
|
for (var _len2 = arguments.length, expressions = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
|
431
485
|
expressions[_key2 - 1] = arguments[_key2];
|
|
432
486
|
}
|
|
487
|
+
// Containers can be created using create as a functions instead of a tagged template.
|
|
488
|
+
if (typeof strings === 'function') {
|
|
489
|
+
expressions = [strings];
|
|
490
|
+
strings = ['', ''];
|
|
491
|
+
}
|
|
492
|
+
var tag, attributes, inner, events;
|
|
433
493
|
var parts = [];
|
|
434
494
|
// Replace functions and objects interpolations with `{number}`.
|
|
435
495
|
// Where `number` is the index on expressions array.
|
|
@@ -444,31 +504,35 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
444
504
|
// Create output text for main template.
|
|
445
505
|
var main = parts.join('').trim().replace(/\n/g, '');
|
|
446
506
|
// Extract outer tag, attributes and inner html.
|
|
447
|
-
var result = main.match(/^<([a-z]+)(.*?)>(.*)<\/\1>$/) || main.match(/^<([a-z]+)(.*?)\/>$/);
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
//
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
var
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
507
|
+
var result = main.match(/^<([a-z]+[1-6]?)(.*?)>(.*)<\/\1>$/) || main.match(/^<([a-z]+)(.*?)\/>$/);
|
|
508
|
+
if (result) {
|
|
509
|
+
tag = result[1];
|
|
510
|
+
inner = result[3];
|
|
511
|
+
// Parse attributes from html text into an object.
|
|
512
|
+
attributes = extractAttributes(result[2]);
|
|
513
|
+
// Events to be delegated.
|
|
514
|
+
events = {};
|
|
515
|
+
// Filter events. To generate events object.
|
|
516
|
+
// Generate attributes object, replace placeholders with expressions.
|
|
517
|
+
attributes = Object.keys(attributes).reduce(function (out, key) {
|
|
518
|
+
// Is Event?
|
|
519
|
+
var matchKey = key.match(/on(([A-Z]{1}[a-z]+)+)/);
|
|
520
|
+
// Is placeholder for function or object?
|
|
521
|
+
// Get expression or value.
|
|
522
|
+
var value = getExpression(attributes[key], expressions);
|
|
523
|
+
// Is event handler. Add to events object.
|
|
524
|
+
if (matchKey && matchKey[1]) {
|
|
525
|
+
var eventType = matchKey[1].toLowerCase();
|
|
526
|
+
Object.keys(value).forEach(function (selector) {
|
|
527
|
+
return events["" + eventType + (selector === '&' ? '' : " " + selector)] = value[selector];
|
|
528
|
+
});
|
|
529
|
+
return out;
|
|
530
|
+
}
|
|
531
|
+
// Is attribute. Add to attributes object.
|
|
532
|
+
out[key] = value;
|
|
466
533
|
return out;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
out[key] = value;
|
|
470
|
-
return out;
|
|
471
|
-
}, {});
|
|
534
|
+
}, {});
|
|
535
|
+
} else inner = main;
|
|
472
536
|
var Current = this;
|
|
473
537
|
// Create subclass for this component.
|
|
474
538
|
return Current.extend({
|
|
@@ -479,12 +543,12 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
479
543
|
// Set template.
|
|
480
544
|
template: {
|
|
481
545
|
// Template for innerHTML of root element.
|
|
482
|
-
inner:
|
|
546
|
+
inner: inner,
|
|
483
547
|
// Template expressions.
|
|
484
548
|
expressions: expressions
|
|
485
549
|
},
|
|
486
550
|
// Set root element tag.
|
|
487
|
-
tag:
|
|
551
|
+
tag: tag
|
|
488
552
|
});
|
|
489
553
|
};
|
|
490
554
|
return Component;
|
package/es/View.js
CHANGED
|
@@ -111,6 +111,7 @@ var View = /*#__PURE__*/function (_Emitter) {
|
|
|
111
111
|
/**
|
|
112
112
|
* Destroy the view.
|
|
113
113
|
* Destroy children views if any, undelegate events, stop listening to events, call `onDestroy` lifecycle method.
|
|
114
|
+
* @param {object} options Options object or any arguments passed to `destroy` method will be passed to `onDestroy` method.
|
|
114
115
|
* @return {Rasti.View} Return `this` for chaining.
|
|
115
116
|
*/;
|
|
116
117
|
_proto.destroy = function destroy() {
|