Skip to content

WIP: experimental, allow adding sub-blocks to blocks using the concept of content areas #786

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
52 changes: 52 additions & 0 deletions app/future_models/better_together/content/area.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module BetterTogether
module Content
class Area < ApplicationRecord
include BetterTogether::Creatable
include BetterTogether::Positioned
include BetterTogether::Privacy
include BetterTogether::Visible

belongs_to :parent, polymorphic: true, touch: true

belongs_to :block, polymorphic: true, autosave: true

accepts_nested_attributes_for :block, allow_destroy: true

validate :no_self_reference

# Ensure block_type is set to the specific STI subclass
# def block_attributes=(attributes)
# block_class = attributes[:type].constantize
# area_block = nil
# if attributes[:id].present?
# area_block = block_class.find(attributes[:id])
# else
# area_block = block_class.new(attributes.except(:type, :id))
# end
# area_block.save!

# self.block = area_block

# self.block_id = area_block.id # Explicitly set the block_type
# self.block_type = area_block.class.name # Explicitly set the block_type

# raise self
# end

# def build_block(attrs={})
# raise self
# block_class = attrs[:type].constantize
# self.block_type = block_class
# # block = super attrs
# self.block = super attrs
# raise attributes
# end

def no_self_reference
raise self
errors.add :block, 'no self-reference in content areas' if contentable_id == block_id
end

end
end
end
20 changes: 20 additions & 0 deletions app/future_models/better_together/content/background_image.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module BetterTogether
module Content
# Allows the user to ceate and display image content
class BackgroundImage < Medium
# include ::BetterTogether::Content::BlockAttributes
has_many :page_blocks, foreign_key: :block_id
has_many :pages, through: :page_blocks

def self.content_addable?
false
end

def self.extra_permitted_attributes
%i[ id type media creator_id _destroy ]
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

<%- scope = local_assigns[:scope] ? local_assigns[:scope] : BetterTogether::Content::Block.block_name %>

<div class="accordion mb-3" id="<%= dom_id(block, temp_id) %>-background-image">
<div class="accordion-item">
<h2 class="accordion-header" id="<%= dom_id(block, temp_id) %>-background-image-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#<%= dom_id(block, temp_id) %>-background-image-collapse" aria-expanded="false" aria-controls="<%= dom_id(block, temp_id) %>-background-image-collapse">
<%= block.class.model_name.human %>
</button>
</h2>
<div id="<%= dom_id(block, temp_id) %>-background-image-collapse" class="accordion-collapse collapse mb-3" aria-labelledby="<%= dom_id(block, temp_id) %>-background-image-header" data-bs-parent="#<%= dom_id(block, temp_id) %>-background-image">
<div class="accordion-body">

<div class="background-image-fields" data-controller="image-preview">
<%= hidden_field_tag "#{scope}[id]", block.id %>
<%= hidden_field_tag "#{scope}[type]", block.class.to_s %>
<%= hidden_field_tag "#{scope}[creator_id]", current_person&.id %>

<%= label_tag do %>
<%= block.class.human_attribute_name('media') %>
<% if block.media.attached? %>
: <%= block.media.filename %>
<% end %>
<% end %>
<%= file_field_tag "#{scope}[media]", required: false, accept: acceptable_image_file_types, data: { action: "image-preview#preview", image_preview_target: "input" }, class: "form-control" %>

<!-- Image preview container (empty initially) -->
<div class="my-3 text-center" data-image-preview-target="preview" data-url="<%= block.media.url if block.media.attached? %>">
<!-- The image preview will be dynamically inserted here -->
</div>
</div>
</div>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div class="content_areas" data-controller="content-areas" data-content-areas-target="contentAreas">

<!-- Render the associated block's specific fields -->
<%= render partial: "better_together/content/blocks/fields/background_image", locals: { block: block.background_image || block.build_background_image, scope: "#{scope}[background_image_attributes]", temp_id: SecureRandom.uuid } %>

</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<div class="content_areas" data-controller="content-areas" data-content-areas-target="contentAreas">
<% block.available_content_areas.each do |class_name, attributes| %>
<% content_area_block = class_name.constantize %>
<% quantity = attributes[:quantity] || { min: 1, max: 1 } %>

<!-- Display existing content areas -->
<div class="content-area-blocks" data-content-areas-target="areaBlocks">
<% block.content_areas.where(block_type: content_area_block.to_s).each_with_index do |content_area, index| %>
<div class="content-area-block">
<!-- Render Content Area Fields -->
<%= fields_for "#{scope}[content_areas_attributes][#{index}]", content_area do |area_form| %>

<!-- Content Area fields -->
<%= area_form.hidden_field :id %>
<%# area_form.hidden_field :block_type, value: content_area.block_type %>
<%# area_form.hidden_field :block_id, value: content_area.block_id %>

<!-- Render Contentable Polymorphic Fields -->
<%= fields_for "#{area_form.object_name}[contentable_attributes]", block do |contentable_form| %>
<%= contentable_form.hidden_field :contentable_type, value: block.class.name %>
<%= contentable_form.hidden_field :contentable_id, value: block.id %>

<!-- You can render specific contentable fields based on the block type -->
<% if block.is_a?(SomeSpecificBlockType) %>
<%= contentable_form.text_field :specific_field_for_this_block %>
<% else %>
<!-- Generic contentable fields if needed -->
<% end %>
<% end %>

<!-- Render the associated block's specific fields -->
<%= render partial: "better_together/content/blocks/fields/#{content_area.block.block_name}", locals: { block: content_area.block, scope: "#{area_form.object_name}[block_attributes]", temp_id: SecureRandom.uuid } %>
<% end %>
</div>
<% end %>
</div>

<!-- Automatically render content areas if min > existing count -->
<% current_count = block.content_areas.where(block_type: content_area_block.to_s).count %>
<% (current_count...quantity[:min]).each do |index| %>
<div class="content-area-block new">
<!-- Render a new content area block -->
<%= fields_for "#{scope}[content_areas_attributes][]", BetterTogether::Content::Area.new(block: content_area_block.new) do |area_form| %>
<!-- Content Area fields -->
<%= area_form.hidden_field :block_type, value: content_area_block.name %>
<%= area_form.hidden_field :contentable_type, value: block.class.name %>
<%= area_form.hidden_field :contentable_id, value: block.id %> <!-- New contentable, so no ID yet -->
<%= area_form.hidden_field :creator_id, value: current_person&.id %> <!-- New contentable, so no ID yet -->
<%= area_form.hidden_field :visible, value: attributes[:visible] %> <!-- New contentable, so no ID yet -->

<!-- Render the new block fields -->
<%= render partial: "better_together/content/blocks/fields/#{content_area_block.block_name}", locals: { block: content_area_block.new, scope: "#{area_form.object_name}[block_attributes]", temp_id: SecureRandom.uuid } %>
<% end %>
</div>
<% end %>

<!-- Add button to allow more content areas if max > min -->
<% if quantity[:max] > quantity[:min] %>
<button type="button" class="btn btn-secondary add-content-area"
data-action="content-areas#addContentArea"
data-content-areas-type="<%= class_name %>"
data-max="<%= quantity[:max] %>">
Add More <%= content_area_block.name.demodulize %>
</button>
<% end %>
<% end %>
</div>
43 changes: 43 additions & 0 deletions app/models/concerns/better_together/content/block_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ module BlockAttributes
include BetterTogether::Translatable
include BetterTogether::Visible

# has_many :content_areas, -> { positioned }, class_name: 'BetterTogether::Content::Area', dependent: :destroy
# has_many :blocks, through: :content_areas, as: :block
# has_many :containing_blocks, through: :content_areas, as: :parent

# accepts_nested_attributes_for :content_areas

validate :validate_css_units

validates :container_class, inclusion: { in: VALID_CONTAINER_CLASSES, message: 'must be a valid Bootstrap container class (container, container-fluid) or none.' }, allow_blank: true
Expand Down Expand Up @@ -80,6 +86,43 @@ module BlockAttributes
end
end

class_methods do
def available_content_areas
{
'BetterTogether::Content::BackgroundImage' => {
quantity: {
min: 1,
max: 1
},
visible: false
}
}
end

def extra_permitted_attributes
content_areas_attributes = []

available_content_areas.map do |key, value|
content_areas_attributes << {
block_attributes: key.constantize.extra_permitted_attributes
}
end

content_areas_attributes += %i[
id type position visible _destroy parent_id block_id _destroy
]

super + [{
content_areas_attributes: content_areas_attributes.flatten.uniq,
background_image_attributes: BetterTogether::Content::BackgroundImage.extra_permitted_attributes
}]
end
end

def available_content_areas
self.class.available_content_areas
end

def block_styles
{
background_color: background_color,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class CreateBetterTogetherContentAreas < ActiveRecord::Migration[7.1]
def change
create_bt_table :areas, prefix: :better_together_content do |t|
t.bt_creator
t.bt_references :parent, null: false, target_table: :better_together_content_blocks
t.bt_references :block, null: false, target_table: :better_together_content_blocks
t.bt_position
t.bt_privacy
t.bt_visible

t.string :type, null: false, default: 'BetterTogether::Content::Area'
end
end
end
5 changes: 5 additions & 0 deletions spec/factories/better_together/content/areas.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FactoryBot.define do
factory :content_area, class: 'Content::Area' do

end
end
7 changes: 7 additions & 0 deletions spec/models/better_together/content/area_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'rails_helper'

module BetterTogether
RSpec.describe Content::Area, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
end