require "htmlbeautifier"
module Lookbook
class AppController < ActionController::Base
EXCEPTIONS = [ViewComponent::PreviewTemplateError, ViewComponent::ComponentError, ViewComponent::TemplateError, ActionView::Template::Error]
protect_from_forgery with: :exception
prepend_view_path File.expand_path("../../views/lookbook", __dir__)
layout "layouts/app"
helper Lookbook::Engine.helpers
before_action :find_preview, only: [:preview, :show]
before_action :find_example, only: [:preview, :show]
before_action :assign_nav, only: [:index, :show]
before_action :initialize_inspector, only: [:show]
def preview
if @example
render html: rendered_example
else
render "app/not_found"
end
end
def show
if @example
begin
@rendered_example = rendered_example.gsub("\"", """)
(@example.type == :group ? @example.examples : [@example]).each do |example|
include_example_data(example)
end
assign_inspector
rescue *EXCEPTIONS
render "app/error"
end
else
render "app/not_found"
end
end
private
def initialize_inspector
@source = []
@output = []
@notes = []
end
def find_preview
candidates = []
params[:path].to_s.scan(%r{/|$}) { candidates << $` }
match = candidates.detect { |candidate| Lookbook::Preview.exists?(candidate) }
@preview = match ? Lookbook::Preview.find(match) : nil
end
def find_example
@example = if @preview
if params[:path] == @preview.lookbook_path
redirect_to show_path "#{params[:path]}/#{@preview.lookbook_examples.first.name}"
else
@example_name = File.basename(params[:path])
@preview.lookbook_example(@example_name)
end
end
end
def include_example_data(example)
content = HtmlBeautifier.beautify(preview_controller.render_example_to_string(@preview, example.name))
@output << {
label: "",
content: content,
lang: Lookbook::Lang.find(:html)
}
render_args = @preview.render_args(example.name, params: preview_controller.params.permit!)
has_template = render_args[:template] != "view_components/preview"
@source << {
label: has_template ? "" : "\# #{example.label}",
content: has_template ? example.template_source(render_args[:template]) : example.method_source,
lang: has_template ? example.template_lang(render_args[:template]) : example.source_lang
}
if example.notes.present?
@notes << {
label: example.label,
content: example.notes
}
end
end
def rendered_example
if @example.type == :group
examples = @example.examples.map do |example|
{
label: example.label,
html: preview_controller.render_example_to_string(@preview, example.name)
}
end
joined = render_to_string "./preview_group", locals: {examples: examples}, layout: nil
preview_controller.render_in_layout_to_string(joined, @preview.lookbook_layout)
else
preview_controller.request.params[:path] = "#{@preview.preview_name}/#{@example.name}".chomp("/")
preview_controller.process(:previews)
end
end
def assign_inspector
@inspector = {
panes: {
source: {
label: "Source",
template: "code",
hotkey: "s",
items: @source,
clipboard: @source.map { |s| @source.many? ? "#{s[:label]}\n#{s[:content]}" : s[:content] }.join("\n\n")
},
output: {
label: "Output",
template: "code",
hotkey: "o",
items: @output,
clipboard: @output.map { |o| @output.many? ? "#{o[:label]}\n#{o[:content]}" : o[:content] }.join("\n\n")
},
notes: {
label: "Notes",
template: "notes",
hotkey: "n",
items: @notes,
disabled: @notes.none?
}
}
}
end
def assign_nav
@nav = Collection.new
previews.reject { |p| p.hidden? }.each do |preview|
current = @nav
if preview.hierarchy_depth == 1
current.add(preview)
else
preview.lookbook_parent_collections.each.with_index(1) do |name, i|
target = current.get_or_create(name)
if preview.hierarchy_depth == i + 1
target.add(preview)
else
current = target
end
end
end
end
@nav
end
def previews
Lookbook::Preview.all
end
def preview_controller
return @preview_controller if @preview_controller.present?
controller_class = Lookbook.config.preview_controller.constantize
controller_class.class_eval { include Lookbook::PreviewController }
controller = controller_class.new
controller.request = request
controller.response = response
@preview_controller ||= controller
end
end
end