Skip to content

Instantly share code, notes, and snippets.

@serradura
Last active February 24, 2019 02:17
Show Gist options
  • Save serradura/88582612c18925de9d5e1d768488c435 to your computer and use it in GitHub Desktop.
Save serradura/88582612c18925de9d5e1d768488c435 to your computer and use it in GitHub Desktop.
u-tags
# frozen_string_literal: true
module Tags
require 'singleton'
end
module Tags
module Utils
Dasherize = -> (value) do
str = String(value)
str.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1-\2')
str.gsub!(/([a-z\d])([A-Z])/, '\1-\2')
str.tr!('_', '-')
str.downcase!
str
end
class AttributesBuilder
require 'json'
require 'bigdecimal'
include Singleton
BOOLEANS = %w[
allowfullscreen async autofocus autoplay checked
compact controls declare default defaultchecked
defaultmuted defaultselected defer disabled
enabled formnovalidate hidden indeterminate inert
ismap itemscope loop multiple muted nohref
noresize noshade novalidate nowrap open
pauseonexit readonly required reversed scoped
seamless selected sortable truespeed typemustmatch
visible
].map{ |attribute| [attribute, attribute.to_sym] }.flatten.freeze
PREFIXES = ['aria', 'data', :aria, :data].freeze
def self.call(options)
instance.call(options)
end
def call(options)
return if options.empty?
output = +''
sep = ' '
options.each_pair do |key, value|
if PREFIXES.include?(key) && value.is_a?(Hash)
value.each_pair do |k, v|
next if v.nil?
output << sep
output << prefix_attribute(key, k, v)
end
elsif BOOLEANS.include?(key) && value
output << sep
output << boolean_attribute(key)
elsif !value.nil?
output << sep
output << attribute(key, value)
end
end
output unless output.empty?
end
def boolean_attribute(key)
"#{key}=\"#{key}\""
end
def attribute(key, value)
value = value.is_a?(Array) ? value.join(" ") : value.to_s.dup
value.gsub!('"', "&quot;")
"#{key}=\"#{value}\""
end
def prefix_attribute(prefix, key, value)
key = "#{prefix}-#{Utils::Dasherize.(key)}"
unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
value = value.to_json
end
attribute(key, value)
end
end
end
end
module Tags
class TagBuilder
include Singleton
PRE_CONTENT_STRINGS = {
textarea: "\n", "textarea" => "\n"
}.freeze
VOID_ELEMENTS = [
:area, :base, :br, :col, :embed, :hr, :img, :input,
:keygen, :link, :meta, :param, :source, :track, :wbr
].freeze
def self.call(name, content = nil, options = {})
instance.call(name, content, options)
end
def self.respond_to_missing?(*args)
true
end
def self.method_missing(called, *args)
call(called, *args)
end
def call(name, content, options)
if VOID_ELEMENTS.include?(name) && (content.nil? || content.is_a?(Hash))
"<#{Utils::Dasherize.(name)}#{Utils::AttributesBuilder.call(content.is_a?(Hash) ? content : options)}>"
else
content_tag_string(Utils::Dasherize.(name), content || '', options)
end
end
private
def content_tag_string(name, content, options)
tag_options = Utils::AttributesBuilder.call(options) if options
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS.fetch(name, '')}#{content}</#{name}>"
end
end
end
# == test ==
Tags::TagBuilder.tap do |tag|
puts tag.br
puts tag.br(nil, class: 'bar')
puts tag.br(class: 'foo')
puts tag.button('Baz', disabled: true)
puts tag.div(
tag.span(1, style: 'font-weight: bold;', data: {foo: true})
)
puts tag.div(tag.span(nil), style: 'font-weight: bold;', data: {foo: true})
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment