#-- # Copyright (c) 2010-2011 Peter Horn, Provideal GmbH # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ # Tabulatr is a class to allow easy creation of data tables as you # frequently find them on 'index' pages in rails. The 'table convention' # here is that we consider every table to consist of three parts: # * a header containing the names of the attribute of the column # * a filter which is an input element to allow for searching the # particular attribute # * the rows with the actual data. # # Additionally, we expect that people want to 'select' rows and perform # batch actions on these rows. # # Author:: Peter Horn, (mailto:peter.horn@provideal.net) # Copyright:: Copyright (c) 2010-2011 by Provideal GmbH (http://www.provideal.net) # License:: MIT Licence class Tabulatr include ActionView::Helpers::TagHelper include ActionView::Helpers::FormTagHelper include ActionView::Helpers::FormOptionsHelper include ActionView::Helpers::TranslationHelper include ActionView::Helpers::RecordTagHelper # include ActionView::Helpers::AssetTagHelper # include Rails::Application::Configurable # Constructor of Tabulatr # # Parameters: # records:: the 'row' data of the table # view:: the current instance of ActionView # opts:: a hash of options specific for this table def initialize(records, view=nil, toptions={}) @records = records @view = view @table_options = TABLE_OPTIONS.merge(toptions) @table_form_options = TABLE_FORM_OPTIONS @val = [] @record = nil @row_mode = false if @records.respond_to? :__classinfo @klaz, @classname, @id, @id_type = @records.__classinfo @pagination = @records.__pagination @filters = @records.__filters @sorting = @records.__sorting @checked = @records.__checked @store_data = @records.__store_data @stateful = @records.__stateful @should_translate = @table_options[:translate] else @classname, @id, @id_type = nil @klaz = @records.first.class @pagination = { :page => 1, :pagesize => records.count, :count => records.count, :pages => 1, :pagesizes => records.count, :total => records.count } @table_options.merge!( :paginate => false, # true to show paginator :sortable => false, # true to allow sorting (can be specified for every sortable column) :selectable => false, # true to render "select all", "select none" and the like :info_text => nil, :filter => false ) @store_data = [] @should_translate = @table_options[:translate] end end # the actual table definition method. It takes an Array of records, a hash of # options and a block with the actual column calls. # # The following options are evaluated here: # :table_html:: a hash with html-attributes added to the created # :header_html:: a hash with html-attributes added to the created # for the header row # :filter_html:: a hash with html-attributes added to the created # for the filter row # :row_html:: a hash with html-attributes added to the s created # for the data rows # :filter:: if set to false, no filter row is output def build_table(&block) @val = [] make_tag(@table_options[:make_form] ? :form : nil, :method => :get, :class => @table_options[:form_class], 'data-remote' => (@table_options[:remote] ? "true" : nil)) do # TODO: make_tag(:input, :type => 'submit', :style => 'display:inline; width:1px; height:1px', :value => '__submit') make_tag(:div, :class => @table_options[:control_div_class_before]) do @table_options[:before_table_controls].each do |element| render_element(element) end end if @table_options[:before_table_controls].present? # @store_data.each do |k,v| make_tag(:input, :type => :hidden, :name => k, :value => h(v)) end render_element(:table, &block) make_tag(:div, :class => @table_options[:control_div_class_after]) do @table_options[:after_table_controls].each do |element| render_element(element) end end if @table_options[:after_table_controls].present? # end # @val.join("").html_safe end def render_element(element, &block) case element when :paginator then render_paginator if @table_options[:paginate] when :hidden_submit then "IMPLEMENT ME!" when :submit then make_tag(:input, :type => 'submit', :class => @table_options[:submit_class], :value => t(@table_options[:submit_label])) if @records.respond_to?(:__classinfo) when :reset then make_tag(:input, :type => 'submit', :class => @table_options[:reset_class], :name => "#{@classname}#{TABLE_FORM_OPTIONS[:reset_state_postfix]}", :value => t(@table_options[:reset_label])) if @stateful when :batch_actions then render_batch_actions if @table_options[:batch_actions] when :select_controls then render_select_controls if @table_options[:selectable] when :info_text make_tag(:div, :class => @table_options[:info_text_class]) do concat(format(t(@table_options[:info_text]), @records.count, @pagination[:total], @checked[:selected].length, @pagination[:count])) end if @table_options[:info_text] when :table then render_table &block else if element.is_a?(String) concat(element) else raise "unknown element '#{element}'" end end end def render_table(&block) to = @table_options[:table_html] to = (to || {}).merge(:class => @table_options[:table_class]) if @table_options[:table_class] make_tag(:table, to) do make_tag(:thead) do render_table_header(&block) render_table_filters(&block) if @table_options[:filter] end # make_tag(:tbody) do render_table_rows(&block) end # content_for(@table_options[:footer_content]) if @table_options[:footer_content] end #
end private # either append to the internal string buffer or use # ActionView#concat to output if an instance is available. def concat(s, html_escape=false) #@view.concat(s) if (Rails.version.to_f < 3.0 && @view) #puts "\##{Rails.version.to_f} '#{s}'" if s.present? then @val << (html_escape ? h(s) : s) end end def h(s) ERB::Util.h(s) end def t(s) return '' unless s.present? begin if s.respond_to?(:should_localize?) and s.should_localize? translate(s) else case @should_translate when :translate then translate(s) when true then translate(s) when :localize then localize(s) else if !@should_translate s elsif @should_translate.respond_to?(:call) @should_translate.call(s) else raise "Wrong value '#{@should_translate}' for table option ':translate', should be false, true, :translate, :localize or a proc." end end end rescue raise "Translating '#{s}' failed!" end end # render the header row def render_table_header(&block) make_tag(:tr, @table_options[:header_html]) do yield(header_row_builder) end # " end # render the filter row def render_table_filters(&block) make_tag(:tr, @table_options[:filter_html]) do yield(filter_row_builder) end # end # render the table rows def render_table_rows(&block) row_classes = @table_options[:row_classes] || [] row_html = @table_options[:row_html] || {} row_class = row_html[:class] || "" @records.each_with_index do |record, i| #concat("") if row_classes.present? rc = row_class.present? ? row_class + " " : '' rc += row_classes[i % row_classes.length] else rc = nil end make_tag(:tr, row_html.merge(:class => rc, :id => dom_id(record))) do yield(data_row_builder(record)) end # end end # stringly produce a tag w/ some options def make_tag(name, hash={}, &block) attrs = hash ? tag_options(hash) : '' if block_given? if name concat("<#{name}#{attrs}>") yield concat("") else yield end else concat("<#{name}#{attrs} />") end nil end def make_image_button(iname, options) inactive = options.delete(:inactive) psrc = @view.image_path File.join(@table_options[:image_path_prefix], iname) if !inactive make_tag(:input, options.merge( :type => 'image', :src => psrc ) ) else make_tag(:img, :src => psrc) end end end Dir[File.join(File.dirname(__FILE__), "tabulatr", "*.rb")].each do |file| require file end