module Hyalite::Mount

Constants

CHECKSUM_ATTR_NAME
ID_ATTR_NAME

Public Class Methods

container_for_id(id) click to toggle source

cf. ReactMount#findReactContainerForID

# File lib/hyalite/mount.rb, line 202
def container_for_id(id)
  root_id = InstanceHandles.root_id_from_node_id(id)
  @containers_by_root_id[root_id]
end
contains_node(outer_node, inner_node) click to toggle source
# File lib/hyalite/mount.rb, line 268
def contains_node(outer_node, inner_node)
  case
  when outer_node.nil? || inner_node.nil?
    false
  when outer_node == inner_node
    true
  when outer_node.text?
    false
  else
    contains_node(outer_node, inner_node.parent)
  end
end
find_component_root(ancestor_node, target_id) click to toggle source
# File lib/hyalite/mount.rb, line 227
def find_component_root(ancestor_node, target_id)
  deepest_ancestor = find_deepest_cached_ancestor(target_id) || ancestor_node

  first_children = [ deepest_ancestor.children.first ]

  while first_children.length > 0
    child = first_children.shift

    while child
      child_id = node_id(child)
      if child_id
        if target_id == child_id
          return child
        elsif InstanceHandles.is_ancestor_id_of(child_id, target_id)
          first_children = [ child.children.first ]
        end
      else
        first_children.push(child.children.first)
      end

      child = child.next_sibling
    end
  end

  raise "can't find component_root ancestor_node: #{ancestor_node}, target_id: #{target_id}"
end
find_deepest_cached_ancestor(target_id) click to toggle source
# File lib/hyalite/mount.rb, line 254
def find_deepest_cached_ancestor(target_id)
  deepest_node_so_far = nil
  InstanceHandles.traverse_ancestors(target_id) do |ancestor_id|
    ancestor = node_cache[ancestor_id]
    if ancestor && is_valid(ancestor, ancestor_id)
      deepest_node_so_far = ancestor;
    else
      false
    end
  end

  deepest_node_so_far
end
find_first_hyalite_dom(node) click to toggle source
# File lib/hyalite/mount.rb, line 281
def find_first_hyalite_dom(node)
  while node && node.parent
    unless node.element? && node_id = internal_id(node)
      node = node.parent
      next
    end

    root_id = InstanceHandles.root_id_from_node_id(node_id)

    current = node
    loop do
      last_id = internal_id(current)
      return nil unless current = current.parent
      break if last_id == root_id
    end

    return node if current == @containers_by_root_id[root_id]

    node = node.parent
  end

  nil
end
has_non_root_child(node) click to toggle source
# File lib/hyalite/mount.rb, line 98
def has_non_root_child(node)
  root_id = root_id(container)
  root_id ? root_id != InstanceHandles.root_id_from_node_id(root_id) : false
end
instances_by_root_id(root_id) click to toggle source
# File lib/hyalite/mount.rb, line 21
def instances_by_root_id(root_id)
  @instances_by_root_id[root_id]
end
internal_id(node) click to toggle source
# File lib/hyalite/mount.rb, line 195
def internal_id(node)
  if node.element?
    node.attributes[ID_ATTR_NAME]
  end
end
is_rendered(node) click to toggle source
# File lib/hyalite/mount.rb, line 59
def is_rendered(node)
  return false unless node.element?

  id = node_id(node)
  id ? id[0] == Hyalite::Reconciler::SEPARATOR : false
end
is_valid(node, id) click to toggle source
# File lib/hyalite/mount.rb, line 216
def is_valid(node, id)
  if node
    container = @containers_by_root_id[id]
    if container && contains_node(container, node)
      return true
    end
  end

  false
end
mount_component_into_node(component_instance, root_id, container, should_reuse_markup) click to toggle source
# File lib/hyalite/mount.rb, line 133
def mount_component_into_node(component_instance, root_id, container, should_reuse_markup)
  Hyalite.updates.reconcile_transaction.perform do |transaction|
    markup = Reconciler.mount_component(component_instance, root_id, transaction.mount_ready, {})
    component_instance.rendered_component.top_level_wrapper = component_instance
    mount_image_into_node(markup, container, should_reuse_markup)
  end
end
mount_image_into_node(markup, container, should_reuse_markup) click to toggle source
# File lib/hyalite/mount.rb, line 141
def mount_image_into_node(markup, container, should_reuse_markup)
  if should_reuse_markup
    root_element = root_element_in_container(container)
    checksum = Adler32.checksum markup
    checksum_attr = root_element.attributes[CHECKSUM_ATTR_NAME]
    if checksum == checksum_attr
      return
    end

    root_element.attributes.remove(CHECKSUM_ATTR_NAME)
    root_element.attributes[CHECKSUM_ATTR_NAME] = checksum
  end

  container.inner_dom = markup
end
node(id) click to toggle source
# File lib/hyalite/mount.rb, line 207
def node(id)
  node =  node_cache[id]
  unless node && is_valid(node, id)
    root_id = InstanceHandles.root_id_from_node_id(id)
    node = node_cache[id] = find_component_root(@containers_by_root_id[root_id], id)
  end
  node
end
node_cache() click to toggle source
# File lib/hyalite/mount.rb, line 170
def node_cache
  @node_cache ||= {}
end
node_id(node) click to toggle source
# File lib/hyalite/mount.rb, line 178
def node_id(node)
  id = internal_id(node)
  if id
    if node_cache.has_key?(id)
      cached = node_cache[id]
      if cached != node
        #raise "Mount: Two valid but unequal nodes with the same `#{ID_ATTR_NAME}`: #{id}"
        node_cache[id] = node
      end
    else
      node_cache[id] = node
    end
  end

  id
end
purge_id(id) click to toggle source
# File lib/hyalite/mount.rb, line 174
def purge_id(id)
  @node_cache.delete(id)
end
register_component(next_component, container) click to toggle source
# File lib/hyalite/mount.rb, line 111
def register_component(next_component, container)
  #ReactBrowserEventEmitter.ensureScrollValueMonitoring();

  root_id = register_container(container)
  @instances_by_root_id[root_id] = next_component;
  root_id
end
register_container(container) click to toggle source
# File lib/hyalite/mount.rb, line 119
def register_container(container)
  root_id = root_id(container)
  if root_id
    root_id = InstanceHandles.root_id_from_node_id(root_id)
  end

  unless root_id
    root_id = InstanceHandles.create_root_id
  end

  @containers_by_root_id[root_id] = container
  root_id
end
render_new_root_component(next_element, container, should_reuse_markup) click to toggle source
# File lib/hyalite/mount.rb, line 66
def render_new_root_component(next_element, container, should_reuse_markup)
  component_instance = Hyalite.instantiate_component(next_element)
  root_id = register_component(component_instance, container)

  Hyalite.updates.batched_updates do
    mount_component_into_node(component_instance, root_id, container, should_reuse_markup)
  end

  component_instance
end
render_subtree_into_container(next_element, container) { |component| ... } click to toggle source
# File lib/hyalite/mount.rb, line 25
def render_subtree_into_container(next_element, container, &block)
  next_wrapped_element = ElementObject.new(TopLevelWrapper, nil, nil, nil, next_element)
  prev_component = @instances_by_root_id[root_id(container)]
  if prev_component
    prev_wrapped_element = prev_component.current_element
    prev_element = prev_wrapped_element.props;
    if Reconciler.should_update_component(prev_element, next_element)
      return update_root_component(
        prev_component,
        next_wrapped_element,
        &block
      ).rendered_component.public_instance
    else
      unmount_component_at_node(container)
    end
  end

  root_element = root_element_in_container(container)
  container_has_markup = root_element && is_rendered(root_element)
  should_reuse_markup = container_has_markup && prev_component.nil?

  component = render_new_root_component(
    next_wrapped_element,
    container,
    should_reuse_markup
  ).rendered_component.public_instance

  if block_given?
    yield component
  end

  component
end
root_element_in_container(container) click to toggle source
# File lib/hyalite/mount.rb, line 157
def root_element_in_container(container)
  if container.document?
    container
  else
    container.children.first
  end
end
root_id(container) click to toggle source
# File lib/hyalite/mount.rb, line 165
def root_id(container)
  root_element = root_element_in_container(container)
  root_element && node_id(root_element)
end
unmount_component_at_node(container) click to toggle source
# File lib/hyalite/mount.rb, line 77
def unmount_component_at_node(container)
  root_id = root_id(container)
  component = @instances_by_root_id[root_id]
  unless component
    container_has_non_root_child = has_non_root_child(container)

    container_id = internal_id(container)
    container_id &&
    container_id == InstanceHandles.root_id_from_node_id(container_id)
    return false
  end

  Hyalite.updates.batched_updates do
    unmount_component_from_node(component, container)
  end

  @instances_by_root_id.delete(root_id)
  @containers_by_root_id.delete(root_id)
  true
end
unmount_component_from_node(instance, container) click to toggle source
# File lib/hyalite/mount.rb, line 103
def unmount_component_from_node(instance, container)
  Reconciler.unmount_component(instance)

  while container.children.length > 0
    container.children.last.remove
  end
end
update_root_component(prev_component, next_element, &callback) click to toggle source
# File lib/hyalite/mount.rb, line 305
def update_root_component(prev_component, next_element, &callback)
  UpdateQueue.enqueue_element_internal(prev_component, next_element)
  if block_given?
    UpdateQueue.enqueue_callback_internal(prev_component, &callback)
  end

  prev_component
end