module CarouselHelper def carousel_for(images, options = {}, &block) Carousel.new(self, images, options, block).html end class Carousel def initialize(view, images, options, block) @view = view @images = images @options = options @block = block end def html css = ["carousel", "slide"] << options[:class].to_s.split(" ") content = [].tap do |content| content << indicators if show_indicators? content << slides content << controls if show_controls? end content_tag(:div, safe_join(content), id: uid, class: css) end private attr_accessor :view, :images, :options, :block delegate :link_to, :content_tag, :image_tag, :safe_join, :concat, :capture, to: :view def show_controls? options.fetch(:controls, true) end def show_indicators? options.fetch(:indicators, true) end def uid @uid ||= "carousel_#{SecureRandom.hex(6)}" end def indicators items = images.count.times.map { |index| indicator_tag(index) } content_tag(:ol, safe_join(items), class: 'carousel-indicators') end def indicator_tag(index) options = { class: (index.zero? ? 'active' : ''), data: { target: "##{uid}", slide_to: index } } content_tag(:li, '', options) end def slides items = images.map.with_index { |image, index| slide_tag(image, index.zero?) } content_tag(:div, safe_join(items), class: 'carousel-inner') end def slide_tag(image, is_active) options = { class: (is_active ? 'carousel-item active' : 'carousel-item'), } slide_content = @block.present? ? capture(image, &@block) : image_tag(image) content_tag(:div, slide_content, options) end def controls safe_join([control_tag('prev'), control_tag('next')]) end def control_tag(direction) options = { class: "carousel-control-#{direction}", data: { slide: direction }, role: "button" } link_to "##{uid}", options do concat content_tag(:span, '', class: "carousel-control-#{direction}-icon", aria: { hidden: true }) concat content_tag(:span, direction.titleize, class: "sr-only") end end end end