Class: Dommy::CustomElementRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/dommy/custom_elements.rb

Overview

‘window.customElements` — registry mapping custom element tag names to Ruby classes that extend `HTMLElement`. Lifecycle callbacks (`connected_callback` / `disconnected_callback` / `attribute_changed_callback` / `adopted_callback`) are invoked by the document’s mutation pipeline when registered elements are added, removed, or have observed attributes mutated.

Names must contain a hyphen per the HTML spec (e.g., ‘my-button`).

Constant Summary collapse

NAME_RE =
/\A[a-z][a-z0-9-]*-[a-z0-9-]*\z/

Instance Method Summary collapse

Constructor Details

#initialize(window) ⇒ CustomElementRegistry

Returns a new instance of CustomElementRegistry.



15
16
17
18
19
20
21
# File 'lib/dommy/custom_elements.rb', line 15

def initialize(window)
  @window = window
  # name → klass
  @definitions = {}
  # name → Array<{ resolve, reject }>
  @pending_promises = {}
end

Instance Method Details

#__js_call__(method, args) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/dommy/custom_elements.rb', line 86

def __js_call__(method, args)
  case method
  when "define"
    define(args[0], args[1], args[2])
  when "get"
    get(args[0])
  when "whenDefined"
    when_defined(args[0])
  when "upgrade"
    upgrade(args[0])
  end
end

#__js_get__(_key) ⇒ Object



82
83
84
# File 'lib/dommy/custom_elements.rb', line 82

def __js_get__(_key)
  nil
end

#define(name, klass, _options = nil) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/dommy/custom_elements.rb', line 23

def define(name, klass, _options = nil)
  key = name.to_s
  unless key.match?(NAME_RE)
    raise DOMException::SyntaxError, "name must be a hyphenated string, got #{name.inspect}"
  end

  raise DOMException::NotSupportedError, "#{key} already defined" if @definitions.key?(key)

  @definitions[key] = klass
  # Resolve any pending whenDefined() promises and re-wrap
  # already-existing nodes (upgrade).
  resolve_pending(key, klass)
  upgrade_existing(key)
  nil
end

#get(name) ⇒ Object



39
40
41
# File 'lib/dommy/custom_elements.rb', line 39

def get(name)
  @definitions[name.to_s]
end

#get_name(klass) ⇒ Object



43
44
45
46
# File 'lib/dommy/custom_elements.rb', line 43

def get_name(klass)
  @definitions.each { |k, v| return k if v == klass }
  nil
end

#upgrade(root) ⇒ Object

Walk ‘root`’s subtree and re-wrap any nodes whose tag is now registered; fires ‘connectedCallback` for each upgraded node that’s currently attached to a document tree.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/dommy/custom_elements.rb', line 66

def upgrade(root)
  return nil unless root.respond_to?(:__node__)

  walk_descendants(root.__node__) do |nk|
    next unless nk.element?
    next unless @definitions.key?(nk.name)

    # Force re-wrap by clearing the document's cached wrapper.
    @window.document.__reset_wrapper__(nk)
    wrapped = @window.document.wrap_node(nk)
    @window.document.__notify_connected__(wrapped) if wrapped
  end

  nil
end

#when_defined(name) ⇒ Object

Returns a Dommy::PromiseValue that resolves with the registered constructor when ‘name` is defined (immediately if already so).



50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/dommy/custom_elements.rb', line 50

def when_defined(name)
  key = name.to_s
  promise = PromiseValue.new(@window)
  if (klass = @definitions[key])
    promise.fulfill(klass)
  else
    @pending_promises[key] ||= []
    @pending_promises[key] << promise
  end

  promise
end