app/helpers/alchemy/elements_block_helper.rb
# frozen_string_literal: true
module Alchemy
# Provides a collection of block-level helpers, allowing for a much more
# concise way of writing element view/editor partials.
#
module ElementsBlockHelper
# Base class for our block-level helpers.
#
class BlockHelper
attr_reader :helpers
attr_reader :opts
def initialize(helpers, opts = {})
@helpers = helpers
@opts = opts
end
def element
opts[:element]
end
end
# Block-level helper class for element views.
#
class ElementViewHelper < BlockHelper
# Renders one of the element's ingredients.
#
# If the element uses +ingredients+ it renders the ingredient record.
#
def render(name, options = {}, html_options = {})
renderable = element.ingredient_by_role(name)
return if renderable.nil?
helpers.render(
renderable.as_view_component(
options: options,
html_options: html_options
)
)
end
# Returns the value of one of the element's ingredients.
#
def value(name)
element.value_for(name)
end
# Returns true if the given ingredient has a value.
#
def has?(name)
element.has_value_for?(name)
end
# Return's the ingredient record by given role.
#
def ingredient_by_role(role)
element.ingredient_by_role(role)
end
end
# Block-level helper for element views. Constructs a DOM element wrapping
# your content element and provides a block helper object you can use for
# concise access to Alchemy's various helpers.
#
# === Example:
#
# <%= element_view_for(element) do |el| %>
# <%= el.render :title %>
# <%= el.render :body %>
# <%= link_to "Go!", el.ingredient(:target_url) %>
# <% end %>
#
# You can override the tag, ID and class used for the generated DOM
# element:
#
# <%= element_view_for(element, tag: 'span', id: 'my_id', class: 'thing') do |el| %>
# <%- ... %>
# <% end %>
#
# If you don't want your view to be wrapped into an extra element, simply set
# `tag` to `false`:
#
# <%= element_view_for(element, tag: false) do |el| %>
# <%- ... %>
# <% end %>
#
# @param [Alchemy::Element] element
# The element to display.
# @param [Hash] options
# Additional options.
#
# @option options :tag (:div)
# The HTML tag to be used for the wrapping element.
# @option options :id (the element's dom_id)
# The wrapper tag's DOM ID.
# @option options :class (the element's name)
# The wrapper tag's DOM class.
# @option options :tags_formatter
# A lambda used for formatting the element's tags (see Alchemy::ElementsHelper::element_tags_attributes). Set to +false+ to not include tags in the wrapper element.
#
def element_view_for(element, options = {})
options = {
tag: :div,
id: element.dom_id,
class: element.name,
tags_formatter: ->(tags) { tags.join(" ") }
}.merge(options)
# capture inner template block
output = capture do
yield ElementViewHelper.new(self, element: element) if block_given?
end
# wrap output in a useful DOM element
if (tag = options.delete(:tag))
# add preview attributes
options.merge!(element_preview_code_attributes(element))
# add tags
if (tags_formatter = options.delete(:tags_formatter))
options.merge!(element_tags_attributes(element, formatter: tags_formatter))
end
output = content_tag(tag, output, options)
end
# that's it!
output
end
end
end