UNPKG

39 kBJavaScriptView Raw
1/** @license MIT https://github.com/jhdrn/myra/blob/master/LICENSE - Copyright (c) 2016-2021 Jonathan Hedrén */
2(function (global, factory) {
3 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4 typeof define === 'function' && define.amd ? define(['exports'], factory) :
5 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.myra = {}));
6}(this, (function (exports) { 'use strict';
7
8 /**
9 * The renderingContext is used to obtain context varibles from within "hooks".
10 */
11 var renderingContext;
12 function getRenderingContext() {
13 return renderingContext;
14 }
15 /**
16 * Renders the component and handles it's "lifecycle" by triggering any effects.
17 */
18 function renderComponent(parentElement, newVNode, oldVNode, isSvg) {
19 var oldNode;
20 var newProps = newVNode.props, rendition = newVNode.rendition;
21 if (rendition !== undefined) {
22 oldNode = rendition.domRef;
23 }
24 if (renderingContext === undefined) {
25 try {
26 renderingContext = {
27 vNode: newVNode,
28 isSvg: isSvg,
29 parentElement: parentElement,
30 hookIndex: 0
31 };
32 var newView = newVNode.view(newProps);
33 if (newView._ === 5 /* Memo */) {
34 if (oldVNode !== undefined && newView.compare(newVNode.props, oldVNode.props)) {
35 newVNode.domRef = oldNode;
36 renderingContext = undefined;
37 return;
38 }
39 newView = newView.view(newProps);
40 }
41 renderingContext = undefined;
42 render(parentElement, newView, newVNode.rendition, oldNode, isSvg);
43 newVNode.rendition = newView;
44 newVNode.domRef = newView.domRef;
45 // Trigger synchronous effects (useLayoutEffect)
46 triggerEffects(newVNode, parentElement, isSvg, true);
47 // Trigger asynchronous effects (useEffect)
48 triggerEffects(newVNode, parentElement, isSvg, false);
49 }
50 catch (err) {
51 tryHandleComponentError(parentElement, newVNode, isSvg, err);
52 }
53 }
54 }
55 /**
56 * Triggers all invokeable effects.
57 */
58 function triggerEffects(newVNode, parentElement, isSvg, sync) {
59 var effects = newVNode.effects;
60 if (effects !== undefined) {
61 var _loop_1 = function (i) {
62 var t = effects[i];
63 if (t.invoke) {
64 if (t.sync && sync) {
65 attemptEffectCleanup(t);
66 t.cleanup = t.effect();
67 t.invoke = false;
68 }
69 else if (!sync) {
70 setTimeout(function () {
71 try {
72 attemptEffectCleanup(t);
73 t.cleanup = t.effect();
74 }
75 catch (err) {
76 tryHandleComponentError(parentElement, newVNode, isSvg, err);
77 }
78 }, 0);
79 t.invoke = false;
80 }
81 }
82 };
83 for (var i in effects) {
84 _loop_1(i);
85 }
86 }
87 }
88 /**
89 * Calls the cleanup function if it's set and then removes it from the wrapper
90 */
91 function attemptEffectCleanup(t) {
92 if (t.cleanup !== undefined) {
93 try {
94 t.cleanup();
95 }
96 catch (err) {
97 console.error('An error occured during effect cleanup: ' + err);
98 }
99 t.cleanup = undefined;
100 }
101 }
102 /**
103 * Calls the error handler (if any) and renders the returned view.
104 */
105 function tryHandleComponentError(parentElement, vNode, isSvg, err) {
106 // Do nothing if the parentElement is not longer connected to the DOM
107 if (parentElement.parentNode === null) {
108 return;
109 }
110 if (vNode.errorHandler !== undefined) {
111 renderingContext = undefined;
112 var oldNode = void 0;
113 if (vNode.rendition !== undefined) {
114 oldNode = vNode.rendition.domRef;
115 }
116 var errorView = vNode.errorHandler(err);
117 render(parentElement, errorView, vNode.rendition, oldNode, isSvg);
118 vNode.rendition = errorView;
119 vNode.domRef = errorView.domRef;
120 }
121 else {
122 throw err;
123 }
124 }
125 /**
126 * Traverses the virtual node hierarchy and unmounts any components in the
127 * hierarchy.
128 */
129 function findAndUnmountComponentsRec(vNode) {
130 if (vNode === undefined) {
131 return;
132 }
133 if (vNode._ === 4 /* Component */) {
134 // Attempt to call any "cleanup" function for all effects before unmount.
135 var effects = vNode.effects;
136 if (effects !== undefined) {
137 for (var i in effects) {
138 attemptEffectCleanup(effects[i]);
139 }
140 }
141 findAndUnmountComponentsRec(vNode.rendition);
142 }
143 else if (vNode._ === 2 /* Element */ || vNode._ === 3 /* Fragment */) {
144 for (var _i = 0, _a = vNode.props.children; _i < _a.length; _i++) {
145 var c = _a[_i];
146 findAndUnmountComponentsRec(c);
147 }
148 }
149 }
150 /**
151 * Renders the view by traversing the virtual node tree recursively
152 */
153 function render(parentDomNode, newVNode, oldVNode, existingDomNode, isSvg, action) {
154 if (isSvg === void 0) { isSvg = false; }
155 if (action === void 0) { action = null; }
156 if (action === null) {
157 // Decide what action to take
158 if (newVNode._ === 3 /* Fragment */ && oldVNode !== undefined && oldVNode._ === 3 /* Fragment */) {
159 action = 4 /* UPDATE */;
160 }
161 else if (oldVNode === undefined || oldVNode.domRef === undefined && oldVNode._ !== 3 /* Fragment */ && oldVNode._ !== 4 /* Component */) {
162 action = 1 /* APPEND */;
163 }
164 else if (oldVNode.domRef !== undefined && existingDomNode === undefined) {
165 action = 2 /* INSERT */;
166 }
167 else if (newVNode._ !== oldVNode._) {
168 action = 3 /* REPLACE */;
169 }
170 else if (newVNode._ === 2 /* Element */ && oldVNode._ === 2 /* Element */ &&
171 newVNode.tagName !== oldVNode.tagName) {
172 action = 3 /* REPLACE */;
173 }
174 else if (newVNode._ === 4 /* Component */ && oldVNode._ === 4 /* Component */ &&
175 newVNode.view !== oldVNode.view) {
176 action = 3 /* REPLACE */;
177 }
178 else {
179 action = 4 /* UPDATE */;
180 }
181 }
182 if (newVNode.tagName === 'svg') {
183 isSvg = true;
184 }
185 switch (action) {
186 case 1 /* APPEND */:
187 case 2 /* INSERT */:
188 case 3 /* REPLACE */:
189 renderCreate(parentDomNode, newVNode, oldVNode, existingDomNode, isSvg, action);
190 break;
191 case 4 /* UPDATE */:
192 renderUpdate(parentDomNode, newVNode, oldVNode, existingDomNode, isSvg);
193 break;
194 }
195 }
196 /**
197 * Creates a new DOM node from a VNode and then renders all it's children.
198 */
199 function renderCreate(parentDomNode, newVNode, oldVNode, existingDomNode, isSvg, action) {
200 if (isSvg === void 0) { isSvg = false; }
201 if (action === void 0) { action = undefined; }
202 if (newVNode._ === 4 /* Component */) {
203 renderComponent(parentDomNode, newVNode, undefined, isSvg);
204 var domNode = newVNode.domRef;
205 if (domNode !== undefined) {
206 if (action === 1 /* APPEND */) {
207 parentDomNode.appendChild(domNode);
208 }
209 else if (action === 2 /* INSERT */) {
210 parentDomNode.insertBefore(domNode, oldVNode.domRef);
211 }
212 else if (action === 3 /* REPLACE */) {
213 // If it's a component node or an element node and it should be
214 // replaced, unmount any components in the tree.
215 if (oldVNode._ === 4 /* Component */ || oldVNode._ === 2 /* Element */ || oldVNode._ === 3 /* Fragment */) {
216 findAndUnmountComponentsRec(oldVNode);
217 }
218 // When using fragments, we can have cases where several "steps" in
219 // the hierarchy is skipped.
220 if (oldVNode.domRef === undefined && (oldVNode._ === 3 /* Fragment */ || oldVNode._ === 4 /* Component */)) {
221 replaceAndRemoveFragmentNodes(parentDomNode, oldVNode, domNode);
222 }
223 else {
224 parentDomNode.replaceChild(domNode, existingDomNode);
225 }
226 }
227 }
228 }
229 else if (newVNode._ === 3 /* Fragment */) {
230 // Skip creating a node for the fragment, instead render the children
231 // directly to the parent DOM node
232 for (var _i = 0, _a = newVNode.props.children; _i < _a.length; _i++) {
233 var c = _a[_i];
234 if (c !== undefined) {
235 render(parentDomNode, c, undefined, undefined, isSvg);
236 }
237 }
238 }
239 else if (newVNode._ !== 5 /* Memo */) {
240 var newNode = createNode(newVNode, isSvg);
241 newVNode.domRef = newNode;
242 if (action === 1 /* APPEND */) {
243 parentDomNode.appendChild(newNode);
244 }
245 else if (action === 2 /* INSERT */) {
246 parentDomNode.insertBefore(newNode, oldVNode.domRef);
247 }
248 else { // action === ACTION_REPLACE
249 // If it's a component node or an element node and it should be
250 // replaced, unmount any components in the tree.
251 if (oldVNode._ === 4 /* Component */ || oldVNode._ === 2 /* Element */ || oldVNode._ === 3 /* Fragment */) {
252 findAndUnmountComponentsRec(oldVNode);
253 }
254 // When using fragments, we can have cases where several "steps" in
255 // the hierarchy is skipped, thus we might need to remove multiple
256 // DOM nodes in addition to replacing one.
257 if (oldVNode.domRef === undefined && (oldVNode._ === 3 /* Fragment */ || oldVNode._ === 4 /* Component */)) {
258 var fragmentNode = oldVNode;
259 if (oldVNode._ === 4 /* Component */) {
260 fragmentNode = oldVNode.rendition;
261 }
262 replaceAndRemoveFragmentNodes(parentDomNode, fragmentNode, newNode);
263 }
264 else {
265 // If it's an element node remove old event listeners before
266 // replacing the node.
267 if (oldVNode._ === 2 /* Element */) {
268 for (var attr in oldVNode.props) {
269 if (attr.indexOf('on') === 0) {
270 removeAttr(attr, existingDomNode);
271 }
272 }
273 }
274 parentDomNode.replaceChild(newNode, existingDomNode);
275 }
276 }
277 // If it's an element node set attributes and event listeners
278 if (newVNode._ === 2 /* Element */) {
279 var props = newVNode.props;
280 for (var name_1 in props) {
281 if (name_1 === 'children' || name_1 === 'key') {
282 continue;
283 }
284 else if (name_1 === 'ref') {
285 props[name_1].current = newNode;
286 }
287 var attributeValue = props[name_1];
288 if (attributeValue !== undefined) {
289 setAttr(newNode, name_1, attributeValue);
290 }
291 }
292 for (var _b = 0, _c = props.children; _b < _c.length; _b++) {
293 var c = _c[_b];
294 if (c !== undefined) {
295 render(newNode, c, undefined, undefined, isSvg);
296 }
297 }
298 }
299 }
300 }
301 /**
302 * Finds fragment child nodes, replaces the first one with newNode and removes
303 * the rest.
304 */
305 function replaceAndRemoveFragmentNodes(parentDomNode, fragmentVNode, newNode) {
306 var childNodes = getFragmentChildDomNodesRec(fragmentVNode);
307 for (var i = 0; i < childNodes.length; i++) {
308 var child = childNodes[i];
309 if (i === 0) {
310 parentDomNode.replaceChild(newNode, child);
311 }
312 else {
313 parentDomNode.removeChild(child);
314 }
315 }
316 }
317 /**
318 * Updates a DOM not from a new VNode.
319 */
320 function renderUpdate(parentDomNode, newVNode, oldVNode, existingDomNode, isSvg) {
321 // if (!nodesEqual(oldVNode.node, existingDomNode)) {
322 // TODO: "debug mode" with warnings?
323 // console.error('The view is not matching the DOM. Are outside forces tampering with it?')
324 // }
325 if (isSvg === void 0) { isSvg = false; }
326 // update existing node
327 switch (newVNode._) {
328 case 2 /* Element */:
329 updateElementAttributes(newVNode, oldVNode, existingDomNode);
330 updateElementVNode(newVNode, oldVNode, existingDomNode, isSvg);
331 break;
332 case 1 /* Text */:
333 if (existingDomNode.textContent !== newVNode.value) {
334 existingDomNode.textContent = newVNode.value;
335 }
336 break;
337 case 4 /* Component */:
338 newVNode.rendition = oldVNode.rendition;
339 newVNode.data = oldVNode.data;
340 newVNode.effects = oldVNode.effects;
341 newVNode.errorHandler = oldVNode.errorHandler;
342 newVNode.link = oldVNode.link;
343 newVNode.link.vNode = newVNode;
344 renderComponent(parentDomNode, newVNode, oldVNode, isSvg);
345 break;
346 case 3 /* Fragment */:
347 updateElementVNode(newVNode, oldVNode, parentDomNode, // Fragments doesn´t reference any DOM node, instead pass the parent
348 isSvg);
349 break;
350 }
351 if (newVNode.domRef === undefined) {
352 // add a reference to the node
353 newVNode.domRef = existingDomNode;
354 }
355 if (newVNode !== oldVNode) {
356 // clean up
357 oldVNode.domRef = undefined;
358 }
359 }
360 /**
361 * Creates a DOM Node from a (non component or fragment) VNode.
362 */
363 function createNode(vNode, isSvg) {
364 switch (vNode._) {
365 case 2 /* Element */:
366 if (isSvg) {
367 return document.createElementNS('http://www.w3.org/2000/svg', vNode.tagName);
368 }
369 return document.createElement(vNode.tagName);
370 case 1 /* Text */:
371 return document.createTextNode(vNode.value);
372 case 0 /* Nothing */:
373 return document.createComment('Nothing');
374 }
375 }
376 /**
377 * Updates an existing HTMLElement DOM node from a new VNode.
378 */
379 function updateElementVNode(newVNode, oldVNode, existingDomNode, isSvg) {
380 var _a;
381 if (isSvg === void 0) { isSvg = false; }
382 var newChildVNodes = newVNode.props.children;
383 var oldChildVNodes = oldVNode.props.children;
384 var diffNoOfChildNodes = oldChildVNodes.length - newChildVNodes.length;
385 if (newChildVNodes.length > 0) {
386 // Create a map holding references to all the old child
387 // VNodes indexed by key
388 var keyMap = {};
389 // Node "pool" for reuse
390 var unkeyedNodes = [];
391 // Prepare the map with the keys from the new nodes
392 for (var i = 0; i < newChildVNodes.length; i++) {
393 var newChildVNode = newChildVNodes[i];
394 var props = newChildVNode.props;
395 if (props !== undefined && props !== null && props.key !== undefined) {
396 keyMap[props.key] = undefined;
397 }
398 }
399 // Go through the old child VNodes to see if there are any old ones matching the new VNodes
400 var matchingKeyedNodes = false;
401 for (var i = 0; i < oldChildVNodes.length; i++) {
402 var oldChildVNode = oldChildVNodes[i];
403 var props = oldChildVNode.props;
404 if (props !== undefined && props !== null && props.key !== undefined) {
405 // If the key has been added (from a new VNode), update it's value
406 if (props.key in keyMap) {
407 keyMap[props.key] = [oldChildVNode, oldChildVNode.domRef];
408 matchingKeyedNodes = true;
409 }
410 // else save the DOM node for reuse or removal
411 else if (existingDomNode.contains(oldChildVNode.domRef)) {
412 unkeyedNodes.push(oldChildVNode.domRef);
413 }
414 }
415 }
416 // If there was no matching keyed nodes, remove all old
417 // DOM nodes
418 if (!matchingKeyedNodes && Object.keys(keyMap).length > 0) {
419 existingDomNode.innerHTML = '';
420 unkeyedNodes.length = 0;
421 for (var i = newChildVNodes.length + diffNoOfChildNodes - 1; i > -1; i--) {
422 var oldChildVNode = oldChildVNodes[i];
423 oldChildVNode.domRef = undefined;
424 // Make sure any sub-components are "unmounted"
425 findAndUnmountComponentsRec(oldChildVNode);
426 }
427 }
428 var domNodeAtIndex = existingDomNode.firstChild;
429 var nextDomNode = null;
430 // Start iterating over the new nodes and render them
431 for (var i = 0; i < newChildVNodes.length; i++) {
432 var newChildVNode = newChildVNodes[i];
433 var oldChildVNode = oldChildVNodes[i];
434 var matchingChildDomNode = void 0;
435 var childAction = void 0;
436 if (domNodeAtIndex !== null) {
437 nextDomNode = domNodeAtIndex.nextSibling;
438 }
439 // If there is an old VNode, it's DOM ref should be
440 // treated as the current/matching DOM node
441 if (oldChildVNode !== undefined) {
442 matchingChildDomNode = oldChildVNode.domRef;
443 }
444 var newProps = newChildVNode.props;
445 // Check if the new VNode is "keyed"
446 if (newProps !== undefined
447 && oldChildVNodes.length > 0) {
448 var newChildVNodeKey = newProps.key;
449 if (newChildVNodeKey !== undefined) {
450 // Fetch the old keyed item from the key map
451 var keyMapEntry = keyMap[newChildVNodeKey];
452 // If there was no old matching key, reuse an old unkeyed node
453 if (keyMapEntry === undefined) {
454 matchingChildDomNode = unkeyedNodes.shift();
455 if (matchingChildDomNode !== undefined) {
456 // Make sure that the DOM node will be
457 // recreated when rendered
458 childAction = 3 /* REPLACE */;
459 }
460 }
461 // If there was a matching key, use the old vNodes dom ref
462 else {
463 oldChildVNode = keyMapEntry[0], matchingChildDomNode = keyMapEntry[1];
464 }
465 // Move the matching dom node to it's new position
466 if (matchingChildDomNode !== undefined && matchingChildDomNode !== domNodeAtIndex) {
467 // If there is no DOM node at the current index,
468 // the matching DOM node should be appended.
469 if (domNodeAtIndex === null) {
470 existingDomNode.appendChild(matchingChildDomNode);
471 }
472 // Move the node by replacing the node at the current index
473 else if (existingDomNode.contains(matchingChildDomNode)) {
474 existingDomNode.replaceChild(matchingChildDomNode, domNodeAtIndex);
475 nextDomNode = matchingChildDomNode.nextSibling;
476 }
477 else {
478 existingDomNode.insertBefore(matchingChildDomNode, domNodeAtIndex);
479 }
480 }
481 }
482 }
483 render(existingDomNode, newChildVNode, oldChildVNode, matchingChildDomNode, isSvg, childAction);
484 domNodeAtIndex = nextDomNode;
485 }
486 }
487 if (diffNoOfChildNodes > 0) {
488 // Remove old unused DOM nodes backwards from the end
489 for (var i = newChildVNodes.length + diffNoOfChildNodes - 1; i > newChildVNodes.length - 1; i--) {
490 var oldChildVNode = oldChildVNodes[i];
491 // Make sure any sub-components are "unmounted"
492 findAndUnmountComponentsRec(oldChildVNode);
493 if (oldChildVNode._ === 3 /* Fragment */) {
494 removeFragmentChildNodes(oldChildVNode, existingDomNode);
495 }
496 else if (oldChildVNode._ === 4 /* Component */ && ((_a = oldChildVNode.rendition) === null || _a === void 0 ? void 0 : _a._) === 3 /* Fragment */) {
497 removeFragmentChildNodes(oldChildVNode.rendition, existingDomNode);
498 }
499 else {
500 var oldChildDomNode = oldChildVNode.domRef;
501 if (oldChildDomNode !== undefined && existingDomNode.contains(oldChildDomNode)) {
502 existingDomNode.removeChild(oldChildDomNode);
503 }
504 }
505 }
506 }
507 }
508 /**
509 * Removes any Fragment child DOM nodes from parentDomElement
510 */
511 function removeFragmentChildNodes(fragmentNode, parentDomElement) {
512 var childNodes = getFragmentChildDomNodesRec(fragmentNode);
513 for (var _i = 0, childNodes_1 = childNodes; _i < childNodes_1.length; _i++) {
514 var c = childNodes_1[_i];
515 parentDomElement.removeChild(c);
516 }
517 }
518 /**
519 * Recursively traverses the vNode tree and removes any Fragment child DOM nodes
520 * from domElement
521 */
522 function getFragmentChildDomNodesRec(fragmentNode, parentDomElement) {
523 var _a;
524 var nodes = [];
525 for (var _i = 0, _b = fragmentNode.props.children; _i < _b.length; _i++) {
526 var fragmentChild = _b[_i];
527 if (fragmentChild._ === 3 /* Fragment */) {
528 nodes.push.apply(nodes, getFragmentChildDomNodesRec(fragmentChild));
529 }
530 else if (fragmentChild._ === 4 /* Component */ && ((_a = fragmentChild.rendition) === null || _a === void 0 ? void 0 : _a._) === 3 /* Fragment */) {
531 nodes.push.apply(nodes, getFragmentChildDomNodesRec(fragmentChild.rendition));
532 }
533 var childNode = fragmentChild.domRef;
534 if (childNode !== undefined) {
535 nodes.push(childNode);
536 }
537 }
538 return nodes;
539 }
540 /**
541 * Sets an attribute or event listener on an HTMLElement.
542 */
543 function setAttr(element, attributeName, attributeValue) {
544 // The the "value" attribute shoud be set explicitly (and only if it has
545 // changed) to prevent jumping cursors in some browsers (Safari)
546 if (attributeName === 'value' && (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA' || element.tagName === 'SELECT')) {
547 if (element.value !== attributeValue) {
548 element.value = attributeValue;
549 }
550 }
551 else if (attributeName in element) {
552 try {
553 element[attributeName] = attributeValue;
554 return;
555 }
556 catch (_) {
557 /** Ignore and use setAttribute instead */
558 }
559 }
560 var attrValueType = typeof attributeValue;
561 if (attrValueType !== 'function' && attrValueType !== 'object') {
562 element.setAttribute(attributeName, attributeValue);
563 }
564 }
565 /**
566 * Removes an attribute or event listener from an HTMLElement.
567 */
568 function removeAttr(a, node) {
569 if (a.indexOf('on') === 0) {
570 node[a] = null;
571 }
572 else if (node.hasAttribute(a)) {
573 node.removeAttribute(a);
574 }
575 }
576 /**
577 * Sets/removes attributes on an DOM element node
578 */
579 function updateElementAttributes(newVNode, oldVNode, existingDomNode) {
580 var newProps = newVNode.props;
581 var oldProps = oldVNode.props;
582 // remove any attributes that was added with the old virtual node but does
583 // not exist in the new virtual node or should be removed anyways (event listeners).
584 for (var attributeName in oldProps) {
585 if (attributeName === 'children' || attributeName === 'key' || attributeName === 'ref') {
586 continue;
587 }
588 if (newProps[attributeName] === undefined || attributeName.indexOf('on') === 0) {
589 removeAttr(attributeName, existingDomNode);
590 }
591 }
592 var attributeValue;
593 var oldAttributeValue;
594 var hasAttr;
595 // update any attribute where the attribute value has changed
596 for (var name_2 in newProps) {
597 if (name_2 === 'children' || name_2 === 'key') {
598 continue;
599 }
600 else if (name_2 === 'ref') {
601 newProps[name_2].current = existingDomNode;
602 continue;
603 }
604 attributeValue = newProps[name_2];
605 hasAttr = existingDomNode.hasAttribute(name_2);
606 // We need to check the actual DOM value of the "value" property
607 // otherwise it may not be updated if the new prop value equals the old
608 // prop value
609 if (name_2 === 'value' && name_2 in existingDomNode) {
610 oldAttributeValue = existingDomNode.value;
611 }
612 else {
613 oldAttributeValue = oldProps[name_2];
614 }
615 if ((name_2.indexOf('on') === 0 || attributeValue !== oldAttributeValue ||
616 !hasAttr) && attributeValue !== undefined) {
617 setAttr(existingDomNode, name_2, attributeValue);
618 }
619 else if (attributeValue === undefined && hasAttr) {
620 existingDomNode.removeAttribute(name_2);
621 }
622 }
623 }
624
625 function Fragment(props) {
626 var _a;
627 return {
628 _: 3 /* Fragment */,
629 props: {
630 children: (_a = props.children) !== null && _a !== void 0 ? _a : [],
631 key: props.key
632 }
633 };
634 }
635
636 function flattenChildren(children) {
637 var flattenedChildren = [];
638 for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
639 var child = children_1[_i];
640 if (child === null || child === undefined || typeof child === 'boolean') {
641 flattenedChildren.push({ _: 0 /* Nothing */ });
642 }
643 else if (Array.isArray(child)) {
644 for (var _a = 0, _b = flattenChildren(child); _a < _b.length; _a++) {
645 var c = _b[_a];
646 flattenedChildren.push(c);
647 }
648 }
649 else if (child._ === undefined) {
650 // Any node which is not a vNode will be converted to a TextVNode
651 flattenedChildren.push({
652 _: 1 /* Text */,
653 value: child
654 });
655 }
656 else {
657 flattenedChildren.push(child);
658 }
659 }
660 return flattenedChildren;
661 }
662 /**
663 * Creates a JSX.Element/VNode from a JSX tag.
664 */
665 function h(tagNameOrComponent, props) {
666 var children = [];
667 for (var _i = 2; _i < arguments.length; _i++) {
668 children[_i - 2] = arguments[_i];
669 }
670 if (tagNameOrComponent === 'nothing' ||
671 tagNameOrComponent === undefined ||
672 tagNameOrComponent === null ||
673 typeof tagNameOrComponent === 'boolean') {
674 return { _: 0 /* Nothing */ };
675 }
676 if (props === null) {
677 props = {};
678 }
679 props.children = flattenChildren(children);
680 if (typeof tagNameOrComponent === 'string') {
681 return {
682 _: 2 /* Element */,
683 tagName: tagNameOrComponent,
684 props: props
685 };
686 }
687 else if (tagNameOrComponent === Fragment) {
688 return tagNameOrComponent(props);
689 }
690 var vNode = {
691 _: 4 /* Component */,
692 debounceRender: false,
693 props: props,
694 view: tagNameOrComponent
695 };
696 vNode.link = {
697 vNode: vNode
698 };
699 return vNode;
700 }
701
702 /**
703 * Better "typeof" which identifies arrays.
704 */
705 function typeOf(obj) {
706 var objType = typeof obj;
707 if (objType === 'string' || objType === 'number' || objType === 'boolean' || objType === 'function') {
708 return objType;
709 }
710 if (obj === undefined) {
711 return 'undefined';
712 }
713 if (obj === null) {
714 return 'null';
715 }
716 return ({}).toString.call(obj).slice(8, -1).toLowerCase();
717 }
718 var basicEqualityTypes = ['string', 'number', 'boolean', 'undefined', 'null', 'function'];
719 /**
720 * Does a deep equality check.
721 */
722 function equal(a, b) {
723 var typeOfA = typeOf(a);
724 var typeOfB = typeOf(b);
725 if (basicEqualityTypes.indexOf(typeOfA) >= 0) {
726 return a === b;
727 }
728 else if (typeOfA === 'object' && typeOfB === 'object') {
729 if (a === b) {
730 return true;
731 }
732 if (Object.keys(a).length !== Object.keys(b).length) {
733 return false;
734 }
735 for (var k in a) {
736 if (a.hasOwnProperty(k)) {
737 if (!equal(a[k], b[k])) {
738 return false;
739 }
740 }
741 }
742 return true;
743 }
744 else if (typeOfA === 'array' && typeOfB === 'array') {
745 if (a === b) {
746 return true;
747 }
748 if (a.length !== b.length) {
749 return false;
750 }
751 for (var i in a) {
752 if (!equal(a[i], b[i])) {
753 return false;
754 }
755 }
756 return true;
757 }
758 else if (typeOfA === 'date' && typeOfB === 'date') {
759 return a.getTime() === b.getTime();
760 }
761 else if (typeOfA === 'regexp' && typeOfB === 'regexp') {
762 return a.toString() === b.toString();
763 }
764 else if (typeOfA === typeOfB) {
765 return a === b;
766 }
767 return false;
768 }
769
770 /**
771 *
772 * @param initialState the initial state
773 */
774 function useState(initialState) {
775 var renderingContext = getRenderingContext();
776 var _a = renderingContext, hookIndex = _a.hookIndex, isSvg = _a.isSvg, parentElement = _a.parentElement, vNode = _a.vNode;
777 if (vNode.data === undefined) {
778 vNode.data = [];
779 }
780 if (vNode.data[hookIndex] === undefined) {
781 var link_1 = vNode.link;
782 var evolve_1 = function (update) {
783 var currentVNode = link_1.vNode;
784 try {
785 if (typeof update === 'function') {
786 update = update(currentVNode.data[hookIndex][0]);
787 }
788 currentVNode.data[hookIndex] = [update, evolve_1];
789 if (!currentVNode.debounceRender) {
790 requestAnimationFrame(function () {
791 link_1.vNode.debounceRender = false;
792 renderComponent(parentElement, link_1.vNode, undefined, isSvg);
793 });
794 }
795 currentVNode.debounceRender = true;
796 }
797 catch (err) {
798 requestAnimationFrame(function () {
799 tryHandleComponentError(parentElement, currentVNode, isSvg, err);
800 });
801 }
802 return currentVNode.data[hookIndex][0];
803 };
804 if (typeof initialState === 'function') {
805 initialState = initialState();
806 }
807 vNode.data[hookIndex] = [initialState, evolve_1];
808 }
809 var state = vNode.data[hookIndex];
810 renderingContext.hookIndex++;
811 return state;
812 }
813 /**
814 *
815 * @param current an optional value
816 */
817 function useRef(current) {
818 var renderingContext = getRenderingContext();
819 var _a = renderingContext, hookIndex = _a.hookIndex, vNode = _a.vNode;
820 if (vNode.data === undefined) {
821 vNode.data = [];
822 }
823 if (vNode.data[hookIndex] === undefined) {
824 var link_2 = vNode.link;
825 vNode.data[hookIndex] = {
826 current: current,
827 get node() {
828 return link_2.vNode.domRef;
829 }
830 };
831 }
832 renderingContext.hookIndex++;
833 return vNode.data[hookIndex];
834 }
835 /**
836 *
837 * @param handler
838 */
839 function useErrorHandler(handler) {
840 var renderingContext = getRenderingContext();
841 var vNode = renderingContext.vNode;
842 vNode.errorHandler = handler;
843 }
844 /**
845 *
846 * @param effect
847 * @param arg
848 */
849 function useLayoutEffect(effect, arg) {
850 useEffectInternal(true, effect, arg);
851 }
852 /**
853 *
854 * @param effect
855 * @param arg
856 */
857 function useEffect(effect, arg) {
858 useEffectInternal(false, effect, arg);
859 }
860 function useEffectInternal(sync, effect, arg) {
861 var renderingContext = getRenderingContext();
862 var _a = renderingContext, hookIndex = _a.hookIndex, vNode = _a.vNode;
863 if (vNode.effects === undefined) {
864 vNode.effects = [];
865 }
866 var t = vNode.effects[hookIndex];
867 if (t === undefined) {
868 vNode.effects[hookIndex] = {
869 arg: arg,
870 sync: sync,
871 invoke: true,
872 effect: effect,
873 };
874 }
875 else if (arg === undefined || !equal(t.arg, arg)) {
876 t.arg = arg;
877 t.effect = effect;
878 t.invoke = true;
879 }
880 renderingContext.hookIndex++;
881 }
882 /**
883 *
884 * @param fn
885 * @param inputs
886 */
887 function useMemo(fn, inputs) {
888 var renderingContext = getRenderingContext();
889 var _a = renderingContext, hookIndex = _a.hookIndex, vNode = _a.vNode;
890 if (vNode.data === undefined) {
891 vNode.data = [];
892 }
893 var res;
894 if (vNode.data[hookIndex] === undefined) {
895 res = fn(inputs);
896 vNode.data[hookIndex] = [res, inputs];
897 }
898 else {
899 var _b = vNode.data[hookIndex], prevRes = _b[0], prevInputs = _b[1];
900 if (equal(prevInputs, inputs)) {
901 res = prevRes;
902 }
903 else {
904 res = fn(inputs);
905 vNode.data[hookIndex] = [res, inputs];
906 }
907 }
908 renderingContext.hookIndex++;
909 return res;
910 }
911
912 function shallowCompareProps(newProps, oldProps) {
913 var newPropsKeys = Object.keys(newProps);
914 if (newPropsKeys.length !== Object.keys(oldProps).length) {
915 return false;
916 }
917 for (var _i = 0, newPropsKeys_1 = newPropsKeys; _i < newPropsKeys_1.length; _i++) {
918 var k = newPropsKeys_1[_i];
919 if (k === 'children') {
920 continue;
921 }
922 if (newProps[k] !== oldProps[k]) {
923 return false;
924 }
925 }
926 return true;
927 }
928 /**
929 * Memoizes a component view, preventing unnecessary renders.
930 *
931 * If no custom compare function is supplied, a shallow comparison of the props'
932 * properties will decide whether the component will be rerendered or not.
933 *
934 * @param factory A component factory function
935 * @param compare An optional props equality comparer function. If true is
936 * returned the memoized view will be kept, otherwise the view
937 * will be rerendered.
938 */
939 function memo(factory, compare) {
940 return function (props) {
941 return {
942 _: 5 /* Memo */,
943 compare: compare || shallowCompareProps,
944 view: factory,
945 props: props
946 };
947 };
948 }
949
950 /**
951 * Convenience function for type hinting
952 *
953 * @param fn
954 */
955 function define(fn) {
956 return fn;
957 }
958 /**
959 * Mounts a virtual DOM node onto the supplied element.
960 */
961 function mount(vNode, element) {
962 requestAnimationFrame(function () {
963 render(element, vNode, undefined, undefined);
964 });
965 }
966
967 exports.Fragment = Fragment;
968 exports.define = define;
969 exports.equal = equal;
970 exports.h = h;
971 exports.memo = memo;
972 exports.mount = mount;
973 exports.typeOf = typeOf;
974 exports.useEffect = useEffect;
975 exports.useErrorHandler = useErrorHandler;
976 exports.useLayoutEffect = useLayoutEffect;
977 exports.useMemo = useMemo;
978 exports.useRef = useRef;
979 exports.useState = useState;
980
981 Object.defineProperty(exports, '__esModule', { value: true });
982
983})));