Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
<alchemy-element-editor
id="element_<%= element.id %>"
data-element-id="<%= element.id %>"
data-element-name="<%= element.name %>"
class="<%= element.css_classes.join(" ") %>"
<%= element.compact? ? "compact" : nil %>
<%= local_assigns[:created] ? "created" : nil %>
<%= element.fixed? ? "fixed" : nil %>
id="element_<%= id %>"
data-element-id="<%= id %>"
data-element-name="<%= name %>"
class="<%= css_classes.join(" ") %>"
<%= compact? ? "compact" : nil %>
<%= created ? "created" : nil %>
<%= fixed? ? "fixed" : nil %>
>
<% unless element.fixed? %>
<% unless fixed? %>
<%= render 'alchemy/admin/elements/header', element: element %>
<% end %>

<%= render 'alchemy/admin/elements/toolbar', element: element %>

<% element.definition.message.tap do |message| %>
<% definition.message.tap do |message| %>
<%= render_message(:info, sanitize(message)) if message %>
<% end %>

<% element.definition.warning.tap do |warning| %>
<% definition.warning.tap do |warning| %>
<%= render_message(:warning, sanitize(warning)) if warning %>
<% end %>

<% if element.editable? %>
<% if editable? %>
<%= form_for [alchemy, :admin, element], remote: true,
html: {id: "element_#{element.id}_form".html_safe, class: 'element-body'} do |f| %>
html: {id: "element_#{id}_form".html_safe, class: 'element-body'} do |f| %>

<div id="element_<%= element.id %>_errors" class="element_errors hidden">
<div id="element_<%= id %>_errors" class="element_errors hidden">
<alchemy-icon name="alert"></alchemy-icon>
<p><%= Alchemy.t(:ingredient_validations_headline) %></p>
</div>

<!-- Ingredients -->
<% if element.has_ingredients_defined? %>
<% if has_ingredients_defined? %>
<div class="element-ingredient-editors">
<%= render Alchemy::Admin::IngredientEditor.with_collection(
element.ungrouped_ingredients,
ungrouped_ingredients,
element_form: f
) %>

<!-- Each ingredient group -->
<% element.grouped_ingredients.each do |group, ingredients| %>
<%= content_tag :details, class: "ingredient-group", id: "element_#{element.id}_ingredient_group_#{group.parameterize.underscore}", is: "alchemy-ingredient-group" do %>
<% grouped_ingredients.each do |group, ingredients| %>
<%= content_tag :details, class: "ingredient-group", id: "element_#{id}_ingredient_group_#{group.parameterize.underscore}", is: "alchemy-ingredient-group" do %>
<summary>
<%= element.translated_group group %>
<%= translated_group group %>
<%= render_icon "arrow-left-s" %>
</summary>
<%= render Alchemy::Admin::IngredientEditor.with_collection(
Expand All @@ -54,7 +54,7 @@
</div>
<% end %>

<% if element.taggable? %>
<% if taggable? %>
<%= render Alchemy::Admin::TagsAutocomplete.new do %>
<%= f.label :tag_list %>
<%= f.text_field :tag_list, value: f.object.tag_list.join(",") %>
Expand All @@ -68,17 +68,15 @@
<%# We need to render nested elements even if the element is folded,
because we need the element present in the DOM for the feature
"click element in the preview => load and expand element editor". %>
<% if element.nestable_elements.any? %>
<% if nestable_elements.any? %>
<div class="nestable-elements">
<%= content_tag :div,
id: "element_#{element.id}_nested_elements",
id: "element_#{id}_nested_elements",
class: "nested-elements", data: {
'droppable-elements' => element.nestable_elements.join(' '),
'element-name' => element.name
'droppable-elements' => nestable_elements.join(' '),
'element-name' => name
} do %>
<%= render element.all_nested_elements.map { |element|
Alchemy::ElementEditor.new(element)
} %>
<%= render Alchemy::Admin::ElementEditor.with_collection(all_nested_elements) %>
<% end %>

<%= render "alchemy/admin/elements/add_nested_element_form", element: element %>
Expand Down
104 changes: 104 additions & 0 deletions app/components/alchemy/admin/element_editor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# frozen_string_literal: true

module Alchemy
module Admin
class ElementEditor < ViewComponent::Base
with_collection_parameter :element

attr_reader :element, :created

delegate :compact?, :definition, :fixed?, :folded?, :id, :ingredient_definitions,
:name, :nestable_elements, :all_nested_elements, :taggable?, :public?, :deprecated?,
to: :element

delegate :alchemy, :render_icon, :render_message, to: :helpers

def initialize(element:, created: false)
@element = element
@created = created
end

# CSS classes for the element editor.
def css_classes
[
"element-editor",
ingredient_definitions.present? ? "with-ingredients" : "without-ingredients",
nestable_elements.any? ? "nestable" : "not-nestable",
taggable? ? "taggable" : "not-taggable",
folded? ? "folded" : "expanded",
compact? ? "compact" : nil,
deprecated? ? "deprecated" : nil,
fixed? ? "is-fixed" : "not-fixed",
public? ? nil : "element-hidden"
]
end

# Tells us, if we should show the element footer and form inputs.
def editable?
ingredient_definitions.any? || taggable?
end

# Are any ingredients defined?
# @return [Boolean]
def has_ingredients_defined?
ingredient_definitions.any?
end

# Returns ingredient instances for defined ingredients
#
# Creates ingredient on demand if the ingredient is not yet present on the element
#
# @return Array<Alchemy::Ingredient>
def ingredients
ingredient_definitions.map do |ingredient|
find_or_create_ingredient(ingredient)
end
end

# Returns ingredients that are not part of any group
def ungrouped_ingredients
ingredients.reject { _1.definition.group }
end

# Returns ingredients grouped by their group name
#
# @return [Hash<String, Array<Alchemy::Ingredient>>]
def grouped_ingredients
ingredients.select { _1.definition.group }.group_by { _1.definition.group }
end

# Returns the translated ingredient group for displaying in admin editor group headings
#
# Translate it in your locale yml file:
#
# alchemy:
# element_groups:
# foo: Bar
#
# Optionally you can scope your ingredient role to an element:
#
# alchemy:
# element_groups:
# article:
# foo: Baz
#
def translated_group(group)
Alchemy.t(
group,
scope: "element_groups.#{element.name}",
default: Alchemy.t("element_groups.#{group}", default: group.humanize)
)
end

private

def find_or_create_ingredient(definition)
element.ingredients.detect { _1.role == definition.role } ||
element.ingredients.create!(
role: definition.role,
type: Alchemy::Ingredient.normalize_type(definition.type)
)
end
end
end
end
102 changes: 0 additions & 102 deletions app/decorators/alchemy/element_editor.rb

This file was deleted.

16 changes: 6 additions & 10 deletions app/views/alchemy/admin/elements/create.turbo_stream.erb
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
<% opts = {
partial: "alchemy/admin/elements/element",
locals: {
element: Alchemy::ElementEditor.new(@element),
created: true
}
} %>

<% if @element.fixed? %>
<% target = "fixed_element_#{@element.id}" %>
<% elsif @element.parent_element %>
Expand All @@ -19,9 +11,13 @@
<% end %>

<% if @insert_at_top %>
<%= turbo_stream.prepend target, **opts %>
<%= turbo_stream.prepend target do %>
<%= render Alchemy::Admin::ElementEditor.new(element: @element, created: true) %>
<% end %>
<% else %>
<%= turbo_stream.append target, **opts %>
<%= turbo_stream.append target do %>
<%= render Alchemy::Admin::ElementEditor.new(element: @element, created: true) %>
<% end %>
<% end %>

<%= turbo_stream.replace "clipboard_button",
Expand Down
6 changes: 3 additions & 3 deletions app/views/alchemy/admin/elements/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
id="main-content-elements"
style="--padding: 0"
>
<%= render @elements.map { |element| Alchemy::ElementEditor.new(element) } %>
<%= render Alchemy::Admin::ElementEditor.with_collection(@elements) %>
</sl-tab-panel>
<% @fixed_elements.each do |element| %>
<sl-tab-panel id="fixed_element_<%= element.id %>" name="fixed-element-<%= element.id %>" style="--padding: 0" class="scrollable-elements">
<%= render Alchemy::ElementEditor.new(element) %>
<%= render Alchemy::Admin::ElementEditor.new(element: element) %>
</sl-tab-panel>
<% end %>
</sl-tab-group>
Expand All @@ -53,7 +53,7 @@
data-droppable-elements="<%= @page.element_definition_names.join(' ') %>"
data-element-name="main-content-elements"
>
<%= render @elements.map { |element| Alchemy::ElementEditor.new(element) } %>
<%= render Alchemy::Admin::ElementEditor.with_collection(@elements) %>
</div>
<% end %>
</alchemy-elements-window>
Expand Down
Loading
Loading