UNPKG

29.3 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var constants = require('./chunk/constants.js');
6var utils = require('./chunk/utils.js');
7
8function update(hook, type) {
9 hook[0] && (hook[1] = hook[0](hook[1], type));
10}
11
12function updateAll(hooks, type) {
13 for (let i in hooks) update(hooks[i], type);
14}
15
16function useHook(reducer, initialState) {
17 if (constants.HOOK_CURRENT.ref.hook) {
18 return constants.HOOK_CURRENT.ref.hook.use(reducer, initialState)[1];
19 }
20}
21
22function useRender() {
23 return constants.HOOK_CURRENT.ref.render;
24}
25
26function useHost() {
27 return useHook(0, { current: constants.HOOK_CURRENT.ref.host });
28}
29
30function createHookCollection(render, host) {
31 let hooks = {};
32 let mounted;
33 let hook = {
34 use,
35 load,
36 updated,
37 unmount
38 };
39
40 let ref = { hook, host, render };
41
42 function load(callback, param) {
43 constants.HOOK_CURRENT.index = 0;
44 constants.HOOK_CURRENT.ref = ref;
45 let resolve = callback(param);
46 constants.HOOK_CURRENT.ref = 0;
47 return resolve;
48 }
49 function use(reducer, state) {
50 let index = constants.HOOK_CURRENT.index++;
51 let mount;
52 // record the hook and the initial state of this
53 if (!hooks[index]) {
54 hooks[index] = [null, state];
55 mount = 1;
56 }
57 // The hook always receives the last reduce.
58 hooks[index][0] = reducer;
59 update(hooks[index], mount ? constants.HOOK_MOUNT : constants.HOOK_UPDATE);
60 return hooks[index];
61 }
62 function updated() {
63 let type = mounted ? constants.HOOK_UPDATED : constants.HOOK_MOUNTED;
64 mounted = 1;
65 updateAll(hooks, type);
66 }
67 function unmount() {
68 updateAll(hooks, constants.HOOK_UNMOUNT);
69 }
70 return hook;
71}
72
73function useState(initialState) {
74 let render = useRender();
75 return useHook((state, type) => {
76 if (constants.HOOK_MOUNT == type) {
77 state[0] = utils.isFunction(initialState) ? initialState() : initialState;
78 state[1] = nextState => {
79 nextState = utils.isFunction(nextState)
80 ? nextState(state[0])
81 : nextState;
82 if (nextState != state[0]) {
83 state[0] = nextState;
84 render();
85 }
86 };
87 }
88 return state;
89 }, []);
90}
91
92function useEffect(callback, args) {
93 // define whether the effect in the render cycle should be regenerated
94 let executeEffect;
95 useHook((state, type) => {
96 if (executeEffect == null) {
97 executeEffect =
98 args && state[0] ? !utils.isEqualArray(args, state[0]) : true;
99 state[0] = args;
100 }
101
102 switch (type) {
103 case constants.HOOK_UPDATE:
104 case constants.HOOK_UNMOUNT:
105 // save the current args, for comparison
106 if ((executeEffect || type == constants.HOOK_UNMOUNT) && state[1]) {
107 // compare the previous snapshot with the generated state
108 state[1]();
109 // clean the effect collector
110 state[1] = 0;
111 }
112 // delete the previous argument for a hook
113 // run if the hook is inserted in a new node
114 // Why? ... to perform again dom operations associated with the parent
115 if (type == constants.HOOK_UNMOUNT) {
116 state[0] = null;
117 }
118 break;
119 case constants.HOOK_MOUNTED:
120 case constants.HOOK_UPDATED:
121 // save the current args, for comparison, repeats due to additional type HOOK_MOUNTED
122 if (executeEffect || type == constants.HOOK_MOUNTED) {
123 // save the effect collector
124 state[1] = callback();
125 }
126 // save the comparison argument
127 break;
128 }
129 return state;
130 }, []);
131}
132
133function useRef(current) {
134 return useHook(0, { current });
135}
136
137function useMemo(callback, args = constants.ARRAY_EMPTY) {
138 let state = useHook(0, []);
139
140 if (!state[0] || (state[0] && !utils.isEqualArray(state[0], args))) {
141 state[1] = callback();
142 }
143 state[0] = args;
144 return state[1];
145}
146
147function useReducer(reducer, initialState) {
148 let render = useRender();
149 let hook = useHook((state, type) => {
150 if (constants.HOOK_MOUNT == type) {
151 state[0] = initialState;
152 state[1] = action => {
153 let nextState = state[2](state[0], action);
154 if (nextState != state[0]) {
155 state[0] = nextState;
156 render();
157 }
158 };
159 }
160 return state;
161 }, []);
162 // allows the reduce to always access the scope of the component
163 hook[2] = reducer;
164
165 return hook;
166}
167/**
168 * @todo add test use callback
169 */
170function useCallback(callback, args = constants.ARRAY_EMPTY) {
171 return useMemo(() => callback, args);
172}
173
174/**
175 *
176 * @param {import("./render").HTMLNode} node
177 * @param {Object} props
178 * @param {Object} nextProps
179 * @param {boolean} isSvg
180 * @param {Object} handlers
181 **/
182function diffProps(node, props, nextProps, isSvg, handlers) {
183 props = props || {};
184
185 for (let key in props) {
186 if (!(key in nextProps)) {
187 setProperty(node, key, props[key], null, isSvg, handlers);
188 }
189 }
190 let ignoreChildren;
191 for (let key in nextProps) {
192 setProperty(node, key, props[key], nextProps[key], isSvg, handlers);
193 ignoreChildren = ignoreChildren || constants.IGNORE_CHILDREN[key];
194 }
195 return ignoreChildren;
196}
197
198function setProperty(node, key, prevValue, nextValue, isSvg, handlers) {
199 key = key == "class" && !isSvg ? "className" : key;
200 // define empty value
201 prevValue = prevValue == null ? null : prevValue;
202 nextValue = nextValue == null ? null : nextValue;
203
204 if (key in node && constants.HYDRATE_PROPS[key]) {
205 prevValue = node[key];
206 }
207
208 if (nextValue === prevValue) return;
209
210 if (
211 key[0] == "o" &&
212 key[1] == "n" &&
213 (utils.isFunction(nextValue) || utils.isFunction(prevValue))
214 ) {
215 setEvent(node, key, nextValue, handlers);
216 return;
217 }
218
219 switch (key) {
220 /**
221 * add support {@link https://developer.mozilla.org/es/docs/Web/API/CSSStyleSheet}
222 */
223 case "styleSheet":
224 if (constants.SUPPORT_STYLE_SHEET)
225 node.shadowRoot.adoptedStyleSheets = []
226 .concat(nextValue)
227 .map(cssText => {
228 if (cssText instanceof CSSStyleSheet) {
229 return cssText;
230 }
231 if (!constants.CACHE_STYLE_SHEET[cssText]) {
232 constants.CACHE_STYLE_SHEET[cssText] = new CSSStyleSheet();
233 constants.CACHE_STYLE_SHEET[cssText].replace(cssText);
234 }
235
236 return constants.CACHE_STYLE_SHEET[cssText];
237 });
238
239 break;
240 case "ref":
241 if (nextValue) nextValue.current = node;
242 break;
243 case "style":
244 setStyle(node, prevValue || "", nextValue || "");
245 break;
246 case "key":
247 node[constants.KEY] = nextValue;
248 break;
249 default:
250 if (!isSvg && key != "list" && key in node) {
251 node[key] = nextValue == null ? "" : nextValue;
252 } else if (nextValue == null) {
253 node.removeAttribute(key);
254 } else {
255 node.setAttribute(
256 key,
257 typeof nextValue == "object"
258 ? JSON.stringify(nextValue)
259 : nextValue
260 );
261 }
262 }
263}
264
265/**
266 *
267 * @param {import("./render").HTMLNode} node
268 * @param {string} type
269 * @param {function} [nextHandler]
270 * @param {object} handlers
271 */
272function setEvent(node, type, nextHandler, handlers) {
273 // get the name of the event to use
274 type = type.slice(type[2] == "-" ? 3 : 2);
275 // add handleEvent to handlers
276 if (!handlers.handleEvent) {
277 /**
278 * {@link https://developer.mozilla.org/es/docs/Web/API/EventTarget/addEventListener#The_value_of_this_within_the_handler}
279 **/
280 handlers.handleEvent = event => handlers[event.type].call(node, event);
281 }
282 if (nextHandler) {
283 // create the subscriber if it does not exist
284 if (!handlers[type]) {
285 node.addEventListener(type, handlers);
286 }
287 // update the associated event
288 handlers[type] = nextHandler;
289 } else {
290 // delete the associated event
291 if (handlers[type]) {
292 node.removeEventListener(type, handlers);
293 delete handlers[type];
294 }
295 }
296}
297/**
298 * define style as string inline,this generates less mutation
299 * to the sun and cleans the previously defined properties.
300 * @param {import("./render").HTMLNode} node
301 * @param {(string|object)} prevValue
302 * @param {(string|object)} nextValue
303 */
304function setStyle(node, prevValue, nextValue) {
305 let style = node.style,
306 prevIsObject;
307 if (typeof prevValue == "object") {
308 prevIsObject = true;
309 for (let key in prevValue) {
310 if (!(key in nextValue)) setPropertyStyle(style, key, null);
311 }
312 }
313 if (typeof nextValue == "object") {
314 for (let key in nextValue) {
315 let value = nextValue[key];
316 if (prevIsObject && prevValue[key] === value) continue;
317 setPropertyStyle(style, key, value);
318 }
319 } else {
320 style.cssText = nextValue;
321 }
322}
323
324function setPropertyStyle(style, key, value) {
325 let method = "setProperty";
326 if (value == null) {
327 method = "removeProperty";
328 value = null;
329 }
330 if (~key.indexOf("-")) {
331 style[method](key, value);
332 } else {
333 style[key] = value;
334 }
335}
336
337let vNodeEmpty = createElement(null, { children: "" });
338
339/**
340 * @param {VnodeType} nodeType
341 * @param {VnodeProps} [props]
342 * @param {Vnode|Vnode[]} [children]
343 * @returns {Vnode}
344 **/
345function createElement(nodeType, props, ...children) {
346 let vnode = { children, ...props, nodeType: nodeType || null };
347 return vnode;
348}
349/**
350 * toVnode, processes the object for correct use within the diff process.
351 **/
352function toVnode(value) {
353 if (isVnodeValue(value)) {
354 return value;
355 } else {
356 if (!value[constants.META_MAP_CHILDREN]) {
357 let scan = mapChildren(value.children);
358 value.children = scan.children;
359 if (scan.keyes) {
360 value[constants.META_KEYES] = scan.keyes;
361 }
362 value[constants.META_MAP_CHILDREN] = true;
363 }
364 if (value.styleSheet && !constants.SUPPORT_STYLE_SHEET) {
365 if (!value[constants.META_STYLE_SHEET]) {
366 value.children.unshift(
367 toVnode(
368 createElement(
369 "style",
370 value[constants.META_KEYES] ? { key: constants.STYLE_SHEET_KEY } : {},
371 value.styleSheet
372 )
373 )
374 );
375 if (value[constants.META_KEYES]) {
376 value[constants.META_KEYES].unshift(constants.STYLE_SHEET_KEY);
377 }
378 }
379 value[constants.META_STYLE_SHEET] = true;
380 }
381 }
382 return value;
383}
384
385function mapChildren(children, scan = { children: [] }, deep = 0) {
386 if (utils.isArray(children)) {
387 let length = children.length;
388 for (let i = 0; i < length; i++) {
389 mapChildren(children[i], scan, deep + 1);
390 }
391 } else {
392 if (children == null && !deep) return scan;
393
394 let vnode = toVnode(children);
395
396 if (vnode != null && typeof vnode == "object") {
397 if (utils.isFunction(vnode.nodeType)) {
398 let { nodeType, ...props } = vnode;
399 return mapChildren(nodeType(props), scan, deep + 1);
400 }
401 if ("key" in vnode) {
402 scan.keyes = scan.keyes || [];
403 if (!~scan.keyes.indexOf(vnode.key)) {
404 scan.keyes.push(vnode.key);
405 }
406 }
407 }
408
409 scan.children.push(vnode);
410 }
411 return scan;
412}
413
414function isVnodeEmpty(value) {
415 let type = typeof value;
416 return value == null || type == "boolean" || type == "function";
417}
418
419function fillVnodeValue(value) {
420 return isVnodeEmpty(value)
421 ? vNodeEmpty
422 : createElement(null, { children: "" + value });
423}
424
425function isVnodeValue(value) {
426 let type = typeof value;
427 return (
428 value == null ||
429 type == "string" ||
430 type == "number" ||
431 type == "function" ||
432 type == "boolean"
433 );
434}
435
436/**
437 * @typedef {(Object<string,any>)} VnodeProps;
438 *
439 * @typedef {(Function|string)} VnodeType;
440 *
441 * @typedef {{type:VnodeType,props:VnodeProps}} Vnode
442 **/
443
444/**
445 *
446 * @param {import("./render").ConfigRender} config
447 * @param {import("./render").HTMLNode} node
448 * @param {import("./vnode").Vnode} nextVnode
449 * @param {boolean} isSvg
450 * @param {Function} currentUpdateComponent
451 * @return {import("./render").HTMLNode}
452 **/
453function diff(id, node, nextVnode, isSvg) {
454 let { vnode, handlers = {} } = (node && node[id]) || {};
455
456 if (vnode == nextVnode && vnode != null) return node;
457
458 nextVnode = isVnodeValue(nextVnode) ? fillVnodeValue(nextVnode) : nextVnode;
459
460 let { nodeType, shadowDom, children, ...props } = vnode || {};
461
462 let {
463 nodeType: nextNodeType,
464 shadowDom: nextShadowDom,
465 children: nextChildren,
466 ...nextProps
467 } = nextVnode;
468
469 isSvg = isSvg || nextNodeType == "svg";
470
471 if (nextNodeType != constants.NODE_HOST && getNodeName(node) !== nextNodeType) {
472 let nextNode = createNode(nextNodeType, isSvg);
473 let parent = node && node.parentNode;
474
475 if (parent) {
476 parent.replaceChild(nextNode, node);
477 }
478
479 node = nextNode;
480 handlers = {};
481 }
482 if (constants.JOIN_CHILDREN[nextNodeType]) {
483 nextNodeType = null;
484 nextChildren = nextChildren.join("");
485 }
486 if (nextNodeType == null) {
487 if (node.textContent != nextChildren) {
488 node.textContent = nextChildren;
489 }
490 } else {
491 if (shadowDom != nextShadowDom) {
492 let { shadowRoot } = node;
493 let mode =
494 nextShadowDom && !shadowRoot
495 ? "open"
496 : !nextShadowDom && shadowRoot
497 ? "closed"
498 : 0;
499 if (mode) node.attachShadow({ mode });
500 }
501
502 let ignoreChildren = diffProps(
503 node,
504 props,
505 nextProps,
506 isSvg,
507 handlers);
508 if (!ignoreChildren && children != nextChildren) {
509 diffChildren(
510 id,
511 nextShadowDom ? node.shadowRoot : node,
512 nextChildren,
513 nextProps[constants.META_KEYES],
514 isSvg
515 );
516 }
517 }
518 node[id] = { vnode: nextVnode, handlers };
519 return node;
520}
521/**
522 *
523 * @param {import("./render").ConfigRender} config
524 * @param {import("./render").HTMLNode} parent
525 * @param {import("./vnode").Vnode[]} [nextChildren]
526 * @param {boolean} isSvg
527 */
528function diffChildren(id, parent, children, keyes, isSvg) {
529 let childrenLenght = children.length;
530 let { childNodes } = parent;
531 let childNodesKeyes = {};
532 let childNodesLength = childNodes.length;
533 let index = keyes
534 ? 0
535 : childNodesLength > childrenLenght
536 ? childrenLenght
537 : childNodesLength;
538
539 for (; index < childNodesLength; index++) {
540 let childNode = childNodes[index];
541 let key = index;
542 if (keyes) {
543 key = childNode[constants.KEY];
544 if (keyes.indexOf(key) > -1) {
545 childNodesKeyes[key] = childNode;
546 continue;
547 }
548 }
549 index--;
550 childNodesLength--;
551 parent.removeChild(childNode);
552 }
553 for (let i = 0; i < childrenLenght; i++) {
554 let child = children[i];
555 let indexChildNode = childNodes[i];
556 let key = keyes ? child.key : i;
557 let childNode = keyes ? childNodesKeyes[key] : indexChildNode;
558
559 if (keyes && childNode) {
560 if (childNode != indexChildNode) {
561 parent.insertBefore(childNode, indexChildNode);
562 }
563 }
564
565 let nextChildNode = diff(id, childNode, child, isSvg);
566
567 if (!childNode) {
568 if (childNodes[i]) {
569 parent.insertBefore(nextChildNode, childNodes[i]);
570 } else {
571 parent.appendChild(nextChildNode);
572 }
573 }
574 }
575}
576
577/**
578 *
579 * @param {string} type
580 * @param {boolean} isSvg
581 * @returns {import("./render").HTMLNode}
582 */
583function createNode(type, isSvg) {
584 let doc = document;
585 let nextNode;
586 if (type != null) {
587 nextNode = isSvg
588 ? doc.createElementNS("http://www.w3.org/2000/svg", type)
589 : doc.createElement(type);
590 } else {
591 nextNode = doc.createTextNode("");
592 }
593 return nextNode;
594}
595
596/**
597 * returns the localName of the node
598 * @param {import("./render").HTMLNode} node
599 */
600function getNodeName(node) {
601 if (!node) return;
602 if (!node[constants.NODE_TYPE]) {
603 node[constants.NODE_TYPE] = node.nodeName.toLowerCase();
604 }
605 let localName = node[constants.NODE_TYPE];
606 return localName == "#text" ? null : localName;
607}
608
609function render(vnode, node, id = "vnode") {
610 if (
611 vnode != null &&
612 typeof vnode == "object" &&
613 vnode.nodeType != constants.NODE_HOST
614 ) {
615 vnode = createElement(constants.NODE_HOST, { children: vnode });
616 }
617 vnode = toVnode(vnode);
618 diff(id, node, vnode);
619 return node;
620}
621
622function setAttr(node, attr, value) {
623 if (value == null) {
624 node.removeAttribute(attr);
625 } else {
626 node.setAttribute(
627 attr,
628 typeof value == "object" ? JSON.stringify(value) : value
629 );
630 }
631}
632
633function formatType(value, type = String) {
634 try {
635 if (type == Boolean) {
636 value = constants.ELEMENT_TRUE_VALUES.indexOf(value) > -1;
637 } else if (typeof value == "string") {
638 value =
639 type == Number
640 ? Number(value)
641 : type == Object || type == Array
642 ? JSON.parse(value)
643 : type == Date
644 ? new Date(value)
645 : value;
646 }
647 if ({}.toString.call(value) == `[object ${type.name}]`) {
648 return { value, error: type == Number && Number.isNaN(value) };
649 }
650 } catch (e) {}
651
652 return { value, error: true };
653}
654
655function propToAttr(prop) {
656 return prop.replace(/([A-Z])/g, "-$1").toLowerCase();
657}
658
659function attrToProp(attr) {
660 return attr.replace(/-(\w)/g, (all, letter) => letter.toUpperCase());
661}
662
663function dispatchEvent(node, type, customEventInit) {
664 node.dispatchEvent(
665 new CustomEvent(
666 type,
667 typeof customEventInit == "object" ? customEventInit : null
668 )
669 );
670}
671
672let defer = Promise.resolve();
673let queue = [];
674let running;
675
676let maxFps = 1000 / 60;
677
678const IMPORTANT = Symbol("important");
679
680function clearQueue() {
681 let time = performance.now();
682
683 let length = queue.length;
684 let current = queue;
685
686 queue = [];
687
688 while (length--) {
689 let callback = current[length];
690 if (callback[IMPORTANT] || performance.now() - time < maxFps) {
691 callback();
692 } else {
693 queue = queue.concat(current.slice(0, length + 1));
694 break;
695 }
696 }
697
698 if (queue.length) {
699 requestAnimationFrame(clearQueue);
700 return;
701 }
702 running = false;
703}
704/**
705 * add a task to the queue
706 * @param {Function} callback
707 * @returns {Promise} Generate a promise that show if the queue is complete
708 */
709function addQueue(callback) {
710 if (!running) {
711 running = true;
712 defer.then(clearQueue);
713 }
714 if (!queue.includes(callback)) queue.push(callback);
715}
716
717function load(self) {
718 if (self.mount) return;
719
720 let id = Symbol("vnode");
721
722 let isPrevent;
723 let isUnmount;
724
725 self[constants.ELEMENT_PROPS] = {};
726
727 let isMounted;
728
729 let resolveUpdate;
730
731 let rerender = () => {
732 // disables blocking, allowing the cycle to be regenerate
733 isPrevent = false;
734 // After the first render it disables the important condition
735 if (rerender[IMPORTANT]) rerender[IMPORTANT] = false;
736 try {
737 render(
738 hooks.load(self.render, { ...self[constants.ELEMENT_PROPS] }),
739 self,
740 id
741 );
742
743 resolveUpdate();
744 } catch (e) {
745 self.error(e);
746 }
747 };
748 // mark the first render as important, self speeds up the rendering
749 rerender[IMPORTANT] = true;
750
751 self.update = () => {
752 if (isUnmount) return;
753 let rendered = self.rendered;
754 if (!isPrevent) {
755 isPrevent = true;
756 // create a promise to observe the status of the update
757 rendered = utils.promise(resolve => (resolveUpdate = resolve)).then(
758 // the UPDATED state is only propagated through
759 // the resolution of the promise
760 // Why? ... to improve communication between web-component parent and children
761 hooks.updated
762 );
763
764 // if the component is already mounted, avoid using self.mounted,
765 // to speed up the microtask
766 isMounted
767 ? addQueue(rerender)
768 : self.mounted.then(() => {
769 isMounted = true;
770 addQueue(rerender);
771 });
772 }
773
774 return (self.rendered = rendered);
775 };
776
777 // any update from hook is added to a separate queue
778 let hooks = createHookCollection(() => addQueue(self.update), self);
779
780 // creates a collection of microtask
781 // associated with the mounted of the component
782
783 self.mounted = utils.promise(
784 resolve =>
785 (self.mount = () => {
786 isMounted = false;
787 // allows the reuse of the component when it is isUnmounted and mounted
788 if (isUnmount == true) {
789 isUnmount = false;
790 self.mounted = self.update();
791 }
792 resolve();
793 })
794 );
795 /**
796 * creates a collection of microtask
797 * associated with the unmounted of the component
798 */
799 self.unmounted = utils.promise(
800 resolve =>
801 (self.unmount = () => {
802 isUnmount = true;
803 hooks.unmount();
804 resolve();
805 })
806 );
807
808 self.initialize();
809
810 self.update();
811}
812
813class AtomicoElement extends HTMLElement {
814 constructor() {
815 super();
816 /**
817 * identifier to store the virtual-dom state,
818 * this is unique between instances of the
819 * component to securely consider the host status
820 */
821 load(this);
822 }
823 connectedCallback() {
824 load(this);
825 this.mount();
826 }
827 disconnectedCallback() {
828 this.unmount();
829 }
830 attributeChangedCallback(attr, oldValue, value) {
831 if (attr === this[constants.ELEMENT_IGNORE_ATTR] || oldValue === value) return;
832 this[attrToProp(attr)] = value;
833 }
834}
835
836/**
837 * register the component, be it a class or function
838 * @param {string} nodeType
839 * @param {Function} component
840 * @return {Function} returns a jsx component
841 */
842function customElement(nodeType, component) {
843 if (utils.isFunction(nodeType)) {
844 component = nodeType;
845
846 let CustomElement = class extends AtomicoElement {};
847 let prototype = CustomElement.prototype;
848
849 let props = component.props;
850
851 prototype.error = component.error || console.error;
852 prototype.render = component;
853 // allows to define the default values of the props
854 prototype.initialize = function() {
855 let length = initialize.length;
856 while (length--) initialize[length](this);
857 };
858 /**@type {Function[]}*/
859 let initialize = [];
860 /**@type {string[]} */
861 let attrs = [];
862
863 for (let prop in props)
864 setProperty$1(prototype, initialize, attrs, prop, props[prop]);
865
866 CustomElement.observedAttributes = attrs;
867
868 return CustomElement;
869 } else {
870 customElements.define(nodeType, customElement(component));
871 return props => createElement(nodeType, props);
872 }
873}
874
875function setProperty$1(prototype, initialize, attrs, prop, schema) {
876 let attr = propToAttr(prop);
877
878 schema = schema.name ? { type: schema } : schema;
879
880 // avoid rewriting the prototype
881 if (prop in prototype) return;
882
883 function set(nextValue) {
884 let prevValue = this[constants.ELEMENT_PROPS][prop];
885
886 if (utils.isFunction(nextValue)) {
887 nextValue = nextValue(prevValue);
888 }
889 let { value, error } = formatType(nextValue, schema.type);
890
891 if (error && value != null) {
892 throw `the observable [${prop}] must be of the type [${schema.type.name}]`;
893 }
894
895 if (prevValue == value) return;
896
897 this[constants.ELEMENT_PROPS][prop] = value;
898
899 let rendered = this.update();
900
901 if (schema.event) {
902 // The event is only dispatched if the component has finished
903 // the rendering cycle, this is useful to observe the changes
904 rendered.then(() =>
905 dispatchEvent(this, schema.event.type || prop, schema.event)
906 );
907 }
908
909 if (schema.reflect) {
910 // the default properties are only reflected once the web-component is mounted
911 this.mounted.then(() => {
912 this[constants.ELEMENT_IGNORE_ATTR] = attr; //update is prevented
913 setAttr(
914 this,
915 attr,
916 schema.type == Boolean && !value ? null : value //
917 );
918 this[constants.ELEMENT_IGNORE_ATTR] = false; // an upcoming update is allowed
919 });
920 }
921 }
922
923 function get() {
924 return this[constants.ELEMENT_PROPS][prop];
925 }
926
927 Object.defineProperty(prototype, prop, { set, get });
928
929 if ("value" in schema) {
930 initialize.push(self => (self[prop] = schema.value));
931 }
932 attrs.push(attr);
933}
934
935function useProp(name) {
936 let ref = useHost();
937 if (name in ref.current) {
938 if (!ref[name]) {
939 ref[name] = [null, nextValue => (ref.current[name] = nextValue)];
940 }
941 ref[name][0] = ref.current[name];
942 return ref[name];
943 }
944}
945
946function useEvent(type, customEventInit) {
947 let ref = useHost();
948 if (!ref[type]) {
949 ref[type] = detail =>
950 dispatchEvent(
951 ref.current,
952 type,
953 detail ? { ...customEventInit, detail } : customEventInit
954 );
955 }
956 return ref[type];
957}
958
959function usePublic(name, value) {
960 let { current } = useHost();
961 if (current[name] != value) {
962 current[name] = value;
963 }
964 return current[name];
965}
966
967exports.createHookCollection = createHookCollection;
968exports.customElement = customElement;
969exports.h = createElement;
970exports.render = render;
971exports.toVnode = toVnode;
972exports.useCallback = useCallback;
973exports.useEffect = useEffect;
974exports.useEvent = useEvent;
975exports.useHook = useHook;
976exports.useHost = useHost;
977exports.useMemo = useMemo;
978exports.useProp = useProp;
979exports.usePublic = usePublic;
980exports.useReducer = useReducer;
981exports.useRef = useRef;
982exports.useRender = useRender;
983exports.useState = useState;
984//# sourceMappingURL=core.js.map