Class: Mayu::VDOM::VNode

Inherits:
T::Struct
  • Object
show all
Extended by:
T::Sig
Includes:
Interfaces::VNode
Defined in:
lib/mayu/vdom/vnode.rb

Constant Summary collapse

Children =
T.type_alias { T::Array[VNode] }
Id =
T.type_alias { IdGenerator::Type }
Writable =
T.type_alias do
  T.any(Async::HTTP::Body::Writable, Brotli::Writer, StringIO)
end

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Interfaces::VNode

#descriptor, #id

Class Method Details

.build(vtree, dom_parent_id, descriptor, task: Async::Task.current) ⇒ Object



28
29
30
31
32
33
34
35
# File 'lib/mayu/vdom/vnode.rb', line 28

def self.build(
  vtree,
  dom_parent_id,
  descriptor,
  task: Async::Task.current
)
  new(id: vtree.next_id!, vtree:, dom_parent_id:, descriptor:, task:)
end

Instance Method Details

#action(type, payload) ⇒ Object



126
127
128
# File 'lib/mayu/vdom/vnode.rb', line 126

def action(type, payload)
  @vtree.action(type, payload)
end

#assert_not_removed!Object



65
66
67
68
# File 'lib/mayu/vdom/vnode.rb', line 65

def assert_not_removed!
  return true unless removed?
  raise "VNode is marked as removed and should not be used!"
end

#dom?Boolean

Returns:

  • (Boolean)


57
# File 'lib/mayu/vdom/vnode.rb', line 57

def dom? = type.is_a?(Symbol)

#dom_idObject



55
# File 'lib/mayu/vdom/vnode.rb', line 55

def dom_id = (wrapper ? children.first&.dom_id || "root" : id)

#enqueue_update!Object



131
132
133
# File 'lib/mayu/vdom/vnode.rb', line 131

def enqueue_update!
  @vtree.enqueue_update!(self)
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


141
142
143
# File 'lib/mayu/vdom/vnode.rb', line 141

def eql?(other)
  self.class === other && other.id == id
end

#fetch(url, method: :GET, headers: {}, body: nil) ⇒ Object



108
109
110
# File 'lib/mayu/vdom/vnode.rb', line 108

def fetch(url, method: :GET, headers: {}, body: nil)
  @vtree.session.fetch(url, method:, headers:, body:)
end

#format_attr(attr, value) ⇒ Object



246
247
248
249
250
251
252
# File 'lib/mayu/vdom/vnode.rb', line 246

def format_attr(attr, value)
  format(
    %{ %<attr>s="%<value>s"},
    attr: attr.to_s,
    value: CGI.escape_html(value.to_s)
  )
end

#format_prop(prop, value) ⇒ Object



255
256
257
258
259
260
261
262
263
# File 'lib/mayu/vdom/vnode.rb', line 255

def format_prop(prop, value)
  value = prop.to_s if value == true

  prop = :value if prop == :initial_value

  attr = prop.to_s.sub(/^on_/, "on").tr("_", "-")

  format_attr(attr, value)
end

#format_props(&block) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/mayu/vdom/vnode.rb', line 221

def format_props(&block)
  props.each do |prop, value|
    next unless value
    next if prop == :children
    next if prop == :slot
    next if value == ""

    if value.is_a?(Hash)
      if prop == :style
        yield format_attr(prop, CSSAttributes.new(**value).to_s)
      else
        Utils
          .flatten_props(value, [prop.to_s])
          .each { yield format_prop(_1, _2) }
      end
      next
    end

    yield format_prop(prop, value)
  end
end

#id_treeObject



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/mayu/vdom/vnode.rb', line 146

def id_tree
  children = Array(self.children).flatten.compact

  return children.first&.id_tree if component

  if children.empty?
    { id:, type: type.to_s }
  else
    { id:, ch: children.map(&:id_tree), type: type.to_s }
  end
end

#init_componentObject



116
117
118
# File 'lib/mayu/vdom/vnode.rb', line 116

def init_component
  @wrapper ||= Component.wrap(self, type, props)
end

#inspectObject



159
160
161
# File 'lib/mayu/vdom/vnode.rb', line 159

def inspect
  "<#VNode:#{id} type=#{descriptor.type} key=#{descriptor.key}>"
end

#keyObject



52
# File 'lib/mayu/vdom/vnode.rb', line 52

def key = descriptor.key

#marshal_dumpObject



71
72
73
74
# File 'lib/mayu/vdom/vnode.rb', line 71

def marshal_dump
  assert_not_removed!
  [@id, @dom_parent_id, @wrapper, @children, @descriptor]
end

#marshal_load(a) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/mayu/vdom/vnode.rb', line 77

def marshal_load(a)
  @id, @dom_parent_id, @wrapper, @children, @descriptor = a
  @removed = false

  if @wrapper
    @wrapper.instance_variable_set(:@vnode, self)
    instance = descriptor.component_class.new(@wrapper)
    @wrapper.instance_variable_set(:@instance, instance)
    instance.instance_variable_set(:@wrapper, @wrapper)
    @wrapper.instance_variable_set(
      :@helpers,
      Component::Helpers.new(@wrapper)
    )
  end
end


121
122
123
# File 'lib/mayu/vdom/vnode.rb', line 121

def navigate(path)
  @vtree.navigate(path)
end

#propsObject



50
# File 'lib/mayu/vdom/vnode.rb', line 50

def props = descriptor.props

#remove!Object



62
# File 'lib/mayu/vdom/vnode.rb', line 62

def remove! = @removed = true

#removed?Boolean

Returns:

  • (Boolean)


60
# File 'lib/mayu/vdom/vnode.rb', line 60

def removed? = @removed

#same?(descriptor) ⇒ Boolean

Returns:

  • (Boolean)


136
137
138
# File 'lib/mayu/vdom/vnode.rb', line 136

def same?(descriptor)
  self.descriptor.eql?(descriptor)
end

#to_htmlObject



164
165
166
# File 'lib/mayu/vdom/vnode.rb', line 164

def to_html
  T.must(StringIO.new.tap { write_html(_1) }.tap(&:rewind).read)
end

#traverse {|_self| ... } ⇒ Object

Yields:

  • (_self)

Yield Parameters:



94
95
96
97
98
# File 'lib/mayu/vdom/vnode.rb', line 94

def traverse(&block)
  yield self

  children.each { |child| child.traverse(&block) }
end

#typeObject



48
# File 'lib/mayu/vdom/vnode.rb', line 48

def type = descriptor.type

#write_html(io, **opts) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/mayu/vdom/vnode.rb', line 174

def write_html(io, **opts)
  type = descriptor.type

  case type
  when Mayu::VDOM::Interfaces::Descriptor::TEXT
    text = descriptor.text
    if text.empty?
      # A zero-width-space will generate a text node in the DOM.
      io.write("&ZeroWidthSpace;")
    else
      io.write(CGI.escape_html(descriptor.text))
    end
    return
  when Mayu::VDOM::Interfaces::Descriptor::COMMENT
    io.write("<!--mayu-id=#{@id}-->")
    return
  when :__mayu_links
    io.write(opts[:links].to_s)
    return
  when :__mayu_scripts
    io.write(opts[:scripts].to_s)
    return
  end

  cleaned_children = children

  if descriptor.component?
    cleaned_children.each { _1.write_html(io, **opts) }
    return
  end

  io.write("<#{type}")

  io.write(%< data-mayu-id="#{@id.to_s}">)

  format_props { |formatted_prop| io.write(formatted_prop) }

  io.write(">")

  return if type.is_a?(Symbol) && Mayu::HTML.void_tag?(type)

  cleaned_children.each { _1.write_html(io, **opts) }

  io.write("</#{type}>")
end