module Beryl class VirtualDOM attr_reader :dom def initialize(layout) @layout = layout @dom = convert(layout) end private def convert(layout) layout.each_with_object([]) do |element, dom| case element[:type] when :column width = width(element[:props]) height = height(element[:props]) klass = "#{height[:class]} s c #{width[:class]} ct cl" style = "#{width[:style]}#{height[:style]}" props = { class: klass, style: style } dom << node('div', props, element[:children] ? convert(element[:children]) : []) when :row width = width(element[:props]) height = height(element[:props]) klass = "#{height[:class]} s r #{width[:class]} cl ccy" style = "#{width[:style]}#{height[:style]}" props = { class: klass, style: style } dom << node('div', props, element[:children] ? convert(element[:children]) : []) when :text width = width(element[:props]) height = height(element[:props]) klass = "#{height[:class]} s e #{width[:class]}" style = "#{width[:style]}#{height[:style]}" props = { class: klass, style: style } dom << node('div', props, [node('text', { nodeValue: element[:value] })]) end end end def height(props) type = height_type(props) { type: type, class: height_class(type), style: height_style(props, type) } end def width(props) type = width_type(props) { type: type, class: width_class(type), style: width_style(props, type) } end def width_type(props) props = [props] unless props.is_a?(Array) return :content unless props return :fill if props.include?(:fill_width) hash = props.select { |p| p.is_a?(Hash) }.first return :content unless hash return :fixed if hash[:width].is_a?(Integer) return :proportional if hash[:proportional_width].is_a?(Integer) :content end def height_type(props) props = [props] unless props.is_a?(Array) return :content unless props return :fill if props.include?(:fill_height) hash = props.select { |p| p.is_a?(Hash) }.first return :content unless hash return :fixed if hash[:height].is_a?(Integer) return :proportional if hash[:proportional_height].is_a?(Integer) :content end def width_class(type) case type when :content 'wc' when :fill 'wf' when :fixed '' when :proportional 'wfp' end end def height_class(type) case type when :content 'hc' when :fill 'hf' when :fixed '' when :proportional 'hfp' end end def width_style(props, type) case type when :content '' when :fill '' when :fixed width = props.select { |p| p.is_a?(Hash) }.first[:width] "width: #{width}px;" when :proportional portion = props.select { |p| p.is_a?(Hash) }.first[:proportional_width] "flex-grow: #{100000 * portion};" end end def height_style(props, type) case type when :content '' when :fill '' when :fixed height = props.select { |p| p.is_a?(Hash) }.first[:height] "height: #{height}px;" when :proportional portion = props.select { |p| p.is_a?(Hash) }.first[:proportional_height] "flex-grow: #{100000 * portion};" end end def node(type, props = {}, children = []) { type: type, props: props, children: children } end end end