lib/preact.rb in isomorfeus-preact-23.9.0.rc5 vs lib/preact.rb in isomorfeus-preact-23.9.0.rc6

- old
+ new

@@ -1,75 +1,72 @@ # backtick_javascript: true # helpers: hash_get, hash_put if RUBY_ENGINE == 'opal' - class VNode - # just a empty place holder to make is_a?(VNode) work - # internally using the js implementation + %x{ + let vnodeId = 0; + + function vnode_eql(me, other) { + for(let prop in me) { + if (prop === 'props' && !(me[prop]["$=="](other[prop]))) { + return false; + } else if (me[prop] != other[prop]) { + return false; + } + } + return true; + } + + class VNode { + constructor(type, props, key, ref, original) { + this.type = type; + this.props = props; + this.key = key; + this.ref = ref; + this._children = null; + this._parent = null; + this._depth = 0; + this._dom = null; + this._nextDom = undefined; + this._component = null; + this._hydrating = null; + this._original = (original == null || original == nil) ? ++vnodeId : original; + this.constructor = undefined; + } + "$=="(other) { return vnode_eql(this, other); } + "$eql?"(other) { return vnode_eql(this, other); } + "$is_a?"(t) { return t === Opal.VNode; } + "$type"() { return this.type; } + "$props"() { return this.props; } + "$key"() { return this.key; } + "$ref"() { return this.ref; } + } + } + class VNode < `VNode` + def self.new(type, props, key, ref, original) + return `new VNode(type, props, key, ref, original)` + end end end class Fragment def initialize(props, _context) @props = props end - if RUBY_ENGINE == 'opal' - def render - `Opal.Preact.render_buffer.pop()` - children = @props[:children] - `Opal.Preact.render_buffer.push(children.$$is_array ? children : [children])` - nil - end - else - def render - ::Preact.render_buffer.pop - ::Preact.render_buffer.push(@props[:children]) - end + def render + ::Preact.render_buffer.pop + ::Preact.render_buffer.push(@props[:children]) end end module Preact EMPTY_ARR = [] UNSAFE_NAME = /[\s\n\\\/='"\0<>]/ if RUBY_ENGINE == 'opal' %x{ - let vnodeId = 0; - - function vnode_eql(me, other) { - for(let prop in me) { - if (prop === 'props' && !(me[prop]["$=="](other[prop]))) { - return false; - } else if (me[prop] != other[prop]) { - return false; - } - } - return true; - } - - class VNode { - constructor(type, props, key, ref, original) { - this.type = type; - this.props = props; - this.key = key; - this.ref = ref; - this._children = null; - this._parent = null; - this._depth = 0; - this._dom = null; - this._nextDom = undefined; - this._component = null; - this._hydrating = null; - this._original = (original == null) ? ++vnodeId : original; - this.constructor = undefined; - } - "$=="(other) { return vnode_eql(this, other); } - "$eql?"(other) { return vnode_eql(this, other); } - "$is_a?"(t) { return t === Opal.VNode; } - } - function _catchError(error, vnode, oldVNode) { let component, ctor, handled; for (; (vnode = vnode._parent); ) { if ((component = vnode._component) && !component._processingException) { @@ -95,16 +92,16 @@ } throw error; } - const EMPTY_OBJ = new VNode(); + const EMPTY_VNODE = new VNode(); const EMPTY_ARR = []; const slice = EMPTY_ARR.slice; function hash_fetch(hash, key) { - let val = $hash_get(hash, key); + let val = hash.get(key); return (val === undefined || val === null) ? nil : val; } function assign(obj, props) { for (let i in props) obj[i] = props[i]; @@ -295,21 +292,21 @@ oldDom, isHydrating ) { let i, j, oldVNode, childVNode, newDom, firstChildDom, refs; - // This is a compression of oldParentVNode!=null && oldParentVNode != EMPTY_OBJ && oldParentVNode._children || EMPTY_ARR - // as EMPTY_OBJ._children should be `undefined`. + // This is a compression of oldParentVNode!=null && oldParentVNode != EMPTY_VNODE && oldParentVNode._children || EMPTY_ARR + // as EMPTY_VNODE._children should be `undefined`. let oldChildren = (oldParentVNode && oldParentVNode !== nil && oldParentVNode._children) ? oldParentVNode._children : EMPTY_ARR; let oldChildrenLength = oldChildren.length; newParentVNode._children = []; for (i = 0; i < renderResult.length; i++) { childVNode = renderResult[i]; - if (childVNode === nil || childVNode == null || typeof childVNode === 'boolean' || childVNode.$$is_boolean) { + if (childVNode === nil || childVNode == null || typeof childVNode === 'boolean' || childVNode.$$is_boolean || typeof childVNode === 'number' || childVNode.$$is_number) { childVNode = newParentVNode._children[i] = null; } // If this newVNode is being reused (e.g. <div>{reuse}{reuse}</div>) in the same diff, // or we are rendering a component (e.g. setState) copy the oldVNodes so it can have // it's own DOM & etc. pointers @@ -331,11 +328,11 @@ str ); } else if (childVNode.$$is_array) { childVNode = newParentVNode._children[i] = new VNode( Opal.Fragment, - #{{ children: `childVNode` }}, + new Map([['children', childVNode]]), null, null, null ); } else if (childVNode._depth > 0) { @@ -355,10 +352,11 @@ } if (childVNode === nil || childVNode == null) { continue; } + childVNode._parent = newParentVNode; childVNode._depth = newParentVNode._depth + 1; // Check if we find a corresponding element in oldChildren. // If found, delete the array item by setting to `undefined`. @@ -390,11 +388,11 @@ } oldVNode = null; } } - oldVNode = (oldVNode && oldVNode !== nil) ? oldVNode : EMPTY_OBJ; + oldVNode = (oldVNode && oldVNode !== nil) ? oldVNode : EMPTY_VNODE; // Morph the old element into the new one, but don't append it to the dom yet diff( parentDom, childVNode, @@ -790,11 +788,11 @@ function validate_props(newType, newProps) { if (newType.declared_props && newType.declared_props !== nil) { #{ `newType.declared_props`.each do |prop, value| - `if (value.has("default") && !newProps.has(prop)) { newProps.set(prop, value.get("default")) }` + `if (value.has("default") && !newProps.has(prop)) { newProps.set(prop, hash_fetch(value, "default")) }` nil end } if (Opal.Isomorfeus.development) { #{`newType`.validate_props(`newProps`)} } } @@ -971,11 +969,11 @@ c._pendingError = c._processingException = null; } c._force = false; - } else if ( + } else if ( excessDomChildren == null && newVNode._original === oldVNode._original ) { newVNode._children = oldVNode._children; newVNode._dom = oldVNode._dom; @@ -1019,12 +1017,10 @@ _catchError(e, c._vnode); } }); } - - self.createVNode = function(type, props, key, ref, original) { // V8 seems to be better at detecting type shapes if the object is allocated from the same call site // Do not inline into createElement and coerceToVNode! return new VNode(type, props, key, ref, original); } @@ -1033,11 +1029,11 @@ // We abuse the `replaceNode` parameter in `hydrate()` to signal if we are in // hydration mode or not by passing the `hydrate` function instead of a DOM // element.. let isHydrating = typeof replaceNode === 'function'; - let reno = (replaceNode !== nil && replaceNode); + let reno = (replaceNode && replaceNode !== nil); let nohy_reno = (!isHydrating && reno); // To be able to support calling `render()` multiple times on the same // DOM node, we need to obtain a reference to the previous tree. We do // this by assigning a new `_children` property to DOM nodes which points @@ -1047,27 +1043,35 @@ ? null : (reno && replaceNode._children) || parentDom._children; let ov = (oldVNode && oldVNode !== nil); - vnode = ( - nohy_reno || parentDom - )._children = self.$create_element(Opal.Fragment, nil, [vnode]); + vnode = (nohy_reno ? replaceNode : parentDom)._children = self.$create_element(Opal.Fragment, nil, [vnode]); // List of effects that need to be called after diffing. let commitQueue = []; + + // Determine the new vnode tree and store it on the DOM element on + // our custom `_children` property. diff( + // parentDom parentDom, - // Determine the new vnode tree and store it on the DOM element on - // our custom `_children` property. + // newVNode vnode, - ov ? oldVNode : EMPTY_OBJ, + // oldVNode + ov ? oldVNode : EMPTY_VNODE, + // globalContext new Map(), + // isSvg parentDom.ownerSVGElement !== undefined, + // excessDomChildren nohy_reno ? [replaceNode] : ov ? null : parentDom.firstChild ? slice.call(parentDom.childNodes) : null, + // commitQueue commitQueue, + // oldDom nohy_reno ? replaceNode : ov ? oldVNode._dom : parentDom.firstChild, + // isHydrating isHydrating ); // Flush all queued effects commitRoot(commitQueue, vnode); @@ -1117,21 +1121,22 @@ } } self.process = function() { let queue; - while ((self.process._rerenderCount = self.rerender_queue.length)) { + while ((self.rerender_count = self.rerender_queue.length)) { queue = self.rerender_queue.sort((a, b) => a._vnode._depth - b._vnode._depth); self.rerender_queue = []; // Don't update `renderCount` yet. Keep its value non-zero to prevent unnecessary // process() calls from getting scheduled while `queue` is still being consumed. queue.some(c => { if (c._dirty) renderComponent(c); }); } } - self.process._rerenderCount = 0; + self.rerender_count = 0; + self.rerender_queue = []; } else IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|^--/i.freeze JS_TO_CSS = {} ENCODED_ENTITIES = /[&<>"]/.freeze @@ -1151,15 +1156,11 @@ "__cC#{self._ctxi += 1}" end def clone_element(vnode, props = nil, children = nil) normalized_props = {} - if RUBY_ENGINE == 'opal' - normalized_props.merge!(`vnode.props`) - else - normalized_props.merge!(vnode.props) - end + normalized_props.merge!(vnode.props) if props normalized_props.merge!(props) key = normalized_props.delete(:key) ref = normalized_props.delete(:ref) @@ -1168,15 +1169,11 @@ ref = nil end normalized_props[:children] = children unless children.nil? - if RUBY_ENGINE == 'opal' - `self.createVNode(vnode.type, normalized_props, #{key || `vnode.key`}, #{ref || `vnode.ref`}, null)` - else - ::VNode.new(vnode.type, normalized_props, key || vnode.key, ref || vnode.ref) - end + ::VNode.new(vnode.type, normalized_props, key || vnode.key, ref || vnode.ref) end def create_context(const_name, default_value = nil) context = ::Preact::Context.new(default_value) ::Object.const_set(const_name, context) @@ -1189,13 +1186,14 @@ end if RUBY_ENGINE == 'opal' attr_accessor :render_buffer attr_accessor :rerender_queue + attr_accessor :rerender_count def is_renderable?(res) - `(res !== null && res !== undefined && res !== nil && res.$$is_string)` + `!!(res && res !== nil && res.$$is_string)` end def create_element(type, props = nil, children = nil) if props if props.JS['$$is_hash'] @@ -1223,17 +1221,24 @@ if (c.length > 0) { children = c; } } end %x{ - if (children !== nil && children !== null) { normalized_props.set("children", children); } + if (children !== nil && children !== null) { + normalized_props.set("children", children.$$is_array ? children : [children]); + } else { + children = normalized_props.get("children"); + if (children && !children.$$is_array) { + normalized_props.set("children", [children]); + } + } return self.createVNode(type, normalized_props, key, ref, null); } end def _enqueue_render(c) - if ((`!c._dirty` && (`c._dirty = true`) && (rerender_queue << c) && `!self.process._rerenderCount++`)) + if ((`!c._dirty` && (`c._dirty = true`) && (self.rerender_queue << c) && `!self.rerender_count++`)) `setTimeout(self.process)` end end def _render_element(component, props, &block) @@ -1244,30 +1249,30 @@ else { opr.push(self.$create_element(component, props, nil)); } } nil end - def hydrate(vnode, container_node) - render(vnode, container_node, `self.render`) - end - def element_or_query_to_n(element_or_query) if `!element_or_query || element_or_query === nil` return `null` elsif `(element_or_query instanceof HTMLElement)` return element_or_query - elsif `(typeof element_or_query === 'string')` || element_or_query.is_a?(String) + elsif `(typeof element_or_query === 'string') || element_or_query.$$is_string` return `document.body.querySelector(element_or_query)` elsif `(typeof element_or_query === 'function')` return element_or_query elsif element_or_query.is_a?(::Browser::Element) return element_or_query.to_n else return element_or_query end end + def hydrate(vnode, container_node) + render(vnode, container_node, `self.render`) + end + def render(vnode, container_node, replace_node = nil) _init_render container_node = element_or_query_to_n(container_node) replace_node = element_or_query_to_n(replace_node) `self.render(vnode, container_node, replace_node)` @@ -1320,6 +1325,5 @@ _render_to_string(vnode, context) end end # RUBY_ENGINE end end -