Class: Emjay::BodyComponent

Inherits:
Component show all
Defined in:
lib/emjay/body_component.rb

Overview

Base class for body components. Port of JS BodyComponent from createComponent.js.

Instance Attribute Summary

Attributes inherited from Component

#attributes, #context, #props

Instance Method Summary collapse

Methods inherited from Component

allowed_attributes, component_name, default_attributes, ending_tag?, #get_attribute, #get_content, #initialize, raw_element?

Constructor Details

This class inherits a constructor from Emjay::Component

Instance Method Details

#get_box_widthsObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/emjay/body_component.rb', line 68

def get_box_widths
  container_width = @context[:container_width]
  parsed_width = container_width.to_i

  paddings = get_shorthand_attr_value("padding", "right") +
    get_shorthand_attr_value("padding", "left")

  borders = get_shorthand_border_value("right") +
    get_shorthand_border_value("left")

  {
    total_width: parsed_width,
    borders: borders,
    paddings: paddings,
    box: parsed_width - paddings - borders
  }
end

#get_child_contextObject



86
87
88
# File 'lib/emjay/body_component.rb', line 86

def get_child_context
  @context
end

#get_shorthand_attr_value(attribute, direction) ⇒ Object



52
53
54
55
56
57
58
59
60
# File 'lib/emjay/body_component.rb', line 52

def get_shorthand_attr_value(attribute, direction)
  dir_attr = get_attribute("#{attribute}-#{direction}")
  return dir_attr.to_i if dir_attr

  base_attr = get_attribute(attribute)
  return 0 unless base_attr

  ShorthandParser.call(base_attr, direction)
end

#get_shorthand_border_value(direction, attribute = "border") ⇒ Object



62
63
64
65
66
# File 'lib/emjay/body_component.rb', line 62

def get_shorthand_border_value(direction, attribute = "border")
  border_direction = direction && get_attribute("#{attribute}-#{direction}")
  border = get_attribute(attribute)
  BorderParser.call(border_direction || border || "0")
end

#get_stylesObject



10
11
12
# File 'lib/emjay/body_component.rb', line 10

def get_styles
  {}
end

#html_attributes(attrs) ⇒ Object

Builds an HTML attribute string. The ‘style:` key auto-resolves through #styles.



40
41
42
43
44
45
46
47
48
49
50
# File 'lib/emjay/body_component.rb', line 40

def html_attributes(attrs)
  attrs.each_with_object(+"") do |(name, value), output|
    next if value.nil?
    resolved = if name.to_s == "style"
      styles(value)
    else
      value
    end
    output << " #{name}=\"#{resolved}\""
  end
end

#render_children(children = nil, opts = {}) ⇒ Object

Renders child components. Supports renderer: lambda, attributes: merge, raw_xml: mode.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/emjay/body_component.rb', line 91

def render_children(children = nil, opts = {})
  renderer = opts[:renderer] || ->(component) { component.render }
  extra_attributes = opts[:attributes] || {}
  extra_props = opts[:props] || {}

  children = children || @props[:children] || []

  sibling = children.length
  components = @context[:components] || {}

  raw_components = components.values.select { |c| c.raw_element? }
  non_raw_siblings = children.count { |child|
    !raw_components.any? { |c| c.component_name == child[:tag_name] }
  }

  output = +""
  children.each_with_index do |child, index|
    component_class = components[child[:tag_name]]
    next unless component_class

    child_data = {
      attributes: extra_attributes.merge(child[:attributes] || {}),
      children: child[:children] || [],
      content: child[:content] || "",
      context: get_child_context,
      global_attributes: child[:global_attributes] || {},
      raw_attrs: child[:raw_attrs] || {},
      props: extra_props.merge(
        first: index == 0,
        index: index,
        last: index + 1 == sibling,
        sibling: sibling,
        non_raw_siblings: non_raw_siblings
      )
    }

    component = component_class.new(child_data)

    if component.respond_to?(:head_style)
      @context[:add_head_style]&.call(child[:tag_name], component.method(:head_style))
    end
    if component.respond_to?(:component_head_style)
      @context[:add_component_head_style]&.call(component.method(:component_head_style))
    end

    output << renderer.call(component)
  end

  output
end

#styles(name_or_hash) ⇒ Object

Converts a style hash (or style name string) to an inline CSS string. Supports dot-notation for nested style lookups (e.g., “carousel.div”).



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/emjay/body_component.rb', line 16

def styles(name_or_hash)
  styles_object = if name_or_hash.is_a?(String)
    if name_or_hash.include?(".")
      parts = name_or_hash.split(".")
      result = get_styles
      parts.each { |part| result = result&.dig(part.to_sym) || result&.dig(part) }
      result
    else
      get_styles[name_or_hash.to_sym] || get_styles[name_or_hash]
    end
  elsif name_or_hash.is_a?(Symbol)
    get_styles[name_or_hash]
  else
    name_or_hash
  end

  return "" unless styles_object

  styles_object.each_with_object(+"") do |(name, value), output|
    output << "#{name}:#{value};" unless value.nil?
  end
end