module Symphonia module ApplicationHelper include FormHelper def bootstrap_flash_map { "notice" => { class_name: "alert-success", icon: "check-circle" }, "error" => { class_name: "alert-danger", icon: "circle-exclamation" }, "warning" => { class_name: "alert-warning", icon: "circle-exclamation" }, "info" => { class_name: "alert-info", icon: "info-circle" }, } end # Renders flash messages def render_flash_messages(flash_messages = nil) s = "" Array(flash_messages || flash).each do |type, message| bootstrap_flash = bootstrap_flash_map[type] s << tag.div(class: "d-print-none alert #{bootstrap_flash[:class_name]} alert-dismissible fade show", role: "alert") do icon(bootstrap_flash[:icon], text: message) + tag.button("", class: "btn-close close", data: { "bs-dismiss" => "alert" }, "aria-label" => t(:button_close)) end end s end def render_menu(menu, options = {}) s = '' Symphonia::MenuManager.menu(menu).each do |name, item| s << render_menu_node(name, item).to_s end options[:container_class] ||= 'mr-auto' tag.ul(s.html_safe, itemscope: '', itemtype: 'http://schema.org/BreadcrumbList', class: "navbar-nav #{options[:container_class]}", id: menu.to_s) end def render_menu_node(menu, item, options = {}) condition = item[:if] ? item[:if].call : true return nil unless condition selected = @menu_item.to_sym == menu label = case item[:label].class.name when 'NilClass' ' '.html_safe when 'String' item[:label] when 'Symbol' t(item[:label]) when 'Proc' item[:label].call(controller) else raise "MenuManager error: Label is unknown type: #{item[:label].class}" end if item[:children].blank? tag.li(render_menu_link(item, label, options), class: "nav-item #{menu} #{'active' if selected} #{options[:class]}", id: item[:id]) else children = '' item[:children].each do |child, subitem| children << render_menu_node(menu, subitem, class: 'dropdown-item').to_s end if children.present? tag.li(class: "nav-item dropdown #{menu}") do concat render_menu_link(item.merge({ class: 'dropdown-toggle', data: { toggle: 'dropdown' } }), label, { is_submenu: true }) concat tag.ul(children.html_safe, class: 'dropdown-menu') end end end end def render_menu_link(item, label, _options = {}) url = case item[:url].class.name when 'Symbol' if item[:url].to_s.include?('.') endpoint, path = item[:url].to_s.split('.') send(endpoint).send(path) else main_app.send item[:url] end when 'Proc' item[:url].call(self) else item[:url] end link_to( (tag.i('', class: "#{item[:icon]}") + "\n" + tag.span(label, itemprop: 'title')).html_safe, url, class: "nav-link #{item[:class]}", data: item[:data], method: item[:method], itemprop: 'url' ) end def html_title(*args) if args.empty? title = @html_title || [] title << t(:meta_title) title.reject(&:blank?).join(' — ') else @html_title ||= [] @html_title += args end end def html_description(*args) if args.empty? desc = @html_description desc ||= t(:meta_description) desc.to_s else @html_description ||= [] @html_description += args end end def title(*args, &block) options = args.extract_options! header = args.shift small = args.shift || '' header_text = if header.is_a?(Symbol) t(header, default: header.to_s.humanize) else header.to_s.dup end if @symphonia_modal_dialog ActiveSupport::Deprecation.warn "@symphonia_modal_dialog is no used anymore !" @symphonia_modal_dialog.title ||= header_text '' else html_title(header_text.dup) header_text << ("\n" << tag.small(small, class: 'text-muted')) if small.present? s = '' if options[:back] && !request.xhr? back_url = options[:back] unless options[:back].is_a? TrueClass s << link_to_back(back_url) end s << capture(&block).to_s if block_given? header_class = (s.present? && "col-6") || nil header_tag = content_tag(((request.xhr? && :h5) || :h1), id: 'page_header', class: header_class) do header_text.html_safe end return header_tag if s.blank? tag.div(class: "row") do header_tag + tag.div(s.html_safe, class: "col-6 text-right") end end end alias_method :page_header, :title def render_no_data(message = nil) tag.div(icon("circle-info", text: message || t(:text_no_data)), class: 'alert alert-info text-center nodata') end def content_for(name, content = nil, &block) @has_content ||= {} @has_content[name] = true super(name, content, &block) end def has_content?(name) !!(@has_content && @has_content[name]) end def format_text(text, _options = {}) return '' if text.nil? markdown = RDiscount.new(text, :smart, :filter_html) markdown.to_html.html_safe end def format_html(text) tag.div((defined?(Ckeditor) ? text.html_safe : format_text(text)), class: 'formatted-text') end def format_price(value, options = {}) number_to_currency(value, { precision: 1, strip_insignificant_zeros: true }.merge(options)) end def multiselect_toggler(id = nil) link_to(icon('plus'), 'javascript:void(0);', onclick: "toggleMultiSelect(#{id || 'this'});return false", class: 'btn fa fa-border') end def link_to_back(url = nil) link_to(icon(:back, t(:button_back)), (params[:back_url] || url || :back), class: 'btn btn-link back') end def link_to_new_entity(options = {}) anchor = options.has_key?(:anchor) ? options.delete(:anchor) : 'page_header' label = options.delete(:label) || t("label_#{controller_name.singularize}_new") model = controller.try(:model) || controller_name.singularize url = options.delete(:url) || new_polymorphic_path(model, anchor: anchor) link_to(icon('square-plus', text: label), url, { class: 'btn btn-primary' }.merge(options)) end # change the default link renderer for will_paginate def will_paginate(collection_or_options = nil, options = {}) if collection_or_options.is_a? Hash options, collection_or_options = collection_or_options, nil end unless options[:renderer] options = options.merge renderer: Symphonia::BootstrapLinkRender end options[:query] ||= @query if @query super *[collection_or_options, options].compact end def ckeditor_for(field_id, options = {}) return '' unless !!defined?(Ckeditor) inline = options.delete(:inline) opts = options.inject({}) do |mem, var| key = var[0].to_s.camelcase(:lower) key[0].downcase! mem[key] = var[1] mem end opts['toolbar'] ||= 'Basic' # opts['customConfig'] ||= 'Basic' js = if inline "CKEDITOR.inline('#{field_id}', {toolbar: 'Basic'});" else " var ta_editor = CKEDITOR.instances['#{field_id}']; if (ta_editor) {CKEDITOR.remove(ta_editor);} CKEDITOR.replace('#{field_id}', #{opts.to_json.html_safe}); " end javascript_tag("$(document).ready(function() {#{js.html_safe}})".html_safe) end # prepend FontAwesome::Sass::Rails::ViewHelpers def icon(icon, text = nil, html_options = {}) if text.is_a?(Hash) html_options = text text = nil end text_content = if text tag.span(text, class: 'd-none d-sm-inline') elsif html_options[:text] html_options.delete(:text) end html_options[:title] ||= text html_options[:class] = "fa-solid fa-#{icon}" html_options['aria-hidden'] ||= true html = tag.i(nil, **html_options) html << ' ' << text_content.to_s if text_content.present? html end def fa_icon(fa, options = {}) ActiveSupport::Deprecation.warn "use `icon` instead" icon(fa, options.delete(:text), options) end # Render original template from engine # Useful for override part of engine view # # @example render_super "login/new" # @param [String] template_name # @param [Class] engine # @param [String] format def render_super(template_name, engine: Symphonia::Engine, format: :html) resolver = lookup_context.view_paths.paths.find do |resolver| resolver.path == engine.root.join("app", "views").to_s end template = resolver.find_all(template_name, engine.engine_name, false, { locale: ["."], formats: [format], variants: [], handlers: [:erb] }, nil, {}).first return "" unless template render template: template end def render_symphonia_dialog(*args, &block) ActiveSupport::Deprecation.warn "Use `render_modal` instead" options = args.extract_options! title = options[:title] || args.shift # first arg possible `title` body = args.shift options[:form_disabled] = true size = options.delete(:size) size ||= '90%' if options.delete(:large) if size.to_s.match(%r(^\d+$)) size = size.to_s + '%' end @symphonia_modal_dialog = SymphoniaModalDialog.new(self, options) @symphonia_modal_dialog.size = size if block_given? yield @symphonia_modal_dialog else raise ArgumentError if body.nil? end @symphonia_modal_dialog.title ||= title if @symphonia_modal_dialog.body.blank? if body.is_a?(Hash) body.merge!(formats: [:html]) else body = { partial: body, formats: [:html] } end @symphonia_modal_dialog.body = render(body) end html = @symphonia_modal_dialog.to_html if options[:render_only] html else js = %Q( $('##{@symphonia_modal_dialog.modal_id}').modal('hide').remove(); $('body').append('#{j html}'); showModal('##{@symphonia_modal_dialog.modal_id}'); $("##{@symphonia_modal_dialog.modal_id} .modal-dialog input:text").first().focus(); $("##{@symphonia_modal_dialog.modal_id} .modal-dialog .modal-body div.buttons").remove(); ) js.concat("$('##{@symphonia_modal_dialog.modal_id} .modal-dialog').css({width: '#{size}'});") if size.present? js.html_safe end end # def render_modal_dialog(show = true, options = {}, &block) # tags = SymphoniaModalDialog.new(self, options) # yield tags if block_given? # html = tags.to_html # if show # "$('##{tags.modal_id}').remove();$('body').append('#{j html}'); showModal('##{tags.modal_id}');".html_safe # else # html # end # end class SymphoniaModalDialog attr_writer :title, :body, :footer attr_reader :modal_id, :title attr_accessor :size def initialize(controller, options = {}) @c = controller @title = options.delete(:title) @modal_id = options.delete(:id) || 'modal-dialog' @form_options = options.delete(:form_options) || {} @options = options end alias_attribute :id, :modal_id def to_html html = "' html.html_safe end def header(&block) if block_given? @header = @c.capture(&block) else @header = (@header.is_a?(Proc) ? @header.call.to_s : @header.to_s) end end def body(&block) if block_given? @body = @c.capture(&block) else @body = (@body.is_a?(Proc) ? @body.call.to_s : @body.to_s) end end def footer(&block) if block_given? @footer = @c.capture(&block) else @footer = (@footer.is_a?(Proc) ? @footer.call.to_s : @footer.to_s) end end def submit(name = nil, options = {}) name ||= @c.t(:button_save) @footer = footer.to_s + @c.link_to(name, 'javascript:void(0)', { onclick: "$('##{@modal_id}').find('form').submit()", class: 'btn btn-primary' }.merge(options)).html_safe end end end end