Spina::Streamfield

A StreamField component for Spina

Installation

# Gemfile
gem 'Spina' # prerequisite
gem 'spina-streamfield'

What is this thing?

It's basically a Ruby version of the StreamField component in the Python Wagtail CMS, but for Spina CMS! It provides an open-ended page component where the admin content creator can mixmatch any other Spina component in a sort of list, even custom components that you build in your app.

streamfield_demo.gif

It was created because I disliked the way Trix stored media like images and YouTube links in the RichText field. Links were hard coded, making them difficult to mass edit or customize how they are displayed.

Here's how StreamField works in Wagtail. This one is very similar in concept:

https://youtu.be/9YLBJC1rPnk?si=EliQ_gNI89cXhinQ

Usage

Base components that are registered are MultiLine, RichText, and Image. To make other components available in the Streamfield:

# config/initializers/spina.rb

# Custom YouTube component in the hosting program  
Spina::Part.register(Spina::Parts::YoutubeVideo)

# Register custom StreamField block types.
# Runs after class reload (which resets @component_types to defaults), so additions are re-applied on each reload.
Spina::Parts::StreamField.register_component("YouTube Video", Spina::Parts::YoutubeVideo)
# Spina::Parts::StreamField.unregister_component("Image")  # example: remove a default


# config/initializers/themes/default.rb
theme.parts = [
  {name: "streamfield", title: "Article Body", part_type: "Spina::Parts::StreamField"},
]

theme.view_templates = [
  {name: "page_parts_demo", title: "Page Parts Demo", parts: %w[streamfield]}
]

In your display template:

<%= # app/views/defaults/pages/page_parts_demo.html.erb %>

<% content(:streamfield)&.each do |item| %>
  <% if item.part_type == "Spina::Parts::MultiLine" %>
    <%= markdown(item.content_part.content) %>
  <% elsif item.part_type == "Spina::Parts::YoutubeVideo" %>
    <figure class="py-2">
      <%= render(partial: 'default/pages/youtube_video', locals: { youtube_part: item.content_part }) %>
    </figure>
  <% elsif item.part_type == "Spina::Parts::Image" %>
    <figure class="py-2">
      <%= content.image_tag(item.content_part, {}, { style: "max-width: 600px;" }) %>
      <% if item.content_part.alt.present? %>
        <figcaption><%= item.content_part.alt %></figcaption>
      <% end %>
    </figure>
  <% end %>
<% end %>

Notice that in the example above I have a Markdown helper that allows me to use Markdown in the MultiLine component:

module ApplicationHelper
  def markdown(text_page_part)
    Kramdown::Document.new(text_page_part.to_s).to_html.html_safe
  end
end

Contributing

Open a PR or Issue if you want, or not

License

The gem is available as open source under the terms of the MIT License.