# frozen_string_literal: true

require_relative 'config_files_loader_merger/base'
require_relative 'config_files_loader_merger/json'
require_relative 'config_files_loader_merger/yml'

module OptParseValidator
  # Options Files container
  class ConfigFilesLoaderMerger < Array
    # @return [ Array<String> ] The downcased supported extensions list
    def self.supported_extensions
      extensions = ConfigFile.constants.select do |const|
        name = ConfigFile.const_get(const)
        name.is_a?(Class) && name != ConfigFile::Base
      end

      extensions.map { |sym| sym.to_s.downcase }
    end

    # @param [ String ] file_path
    #
    # @return [ Self ]
    def <<(file_path)
      return self unless File.exist?(file_path)

      ext = File.extname(file_path).delete('.')

      raise Error, "The option file's extension '#{ext}' is not supported" unless self.class.supported_extensions.include?(ext)

      super(ConfigFile.const_get(ext.upcase).new(file_path))
    end

    # @params [ Hash ] opts
    # @option opts [ Boolean ] :symbolize_keys Whether or not to symbolize keys in the returned hash
    # @option opts [ Array ] :yaml_arguments See https://ruby-doc.org/stdlib-2.3.1/libdoc/psych/rdoc/Psych.html#method-c-safe_load
    #
    # @return [ Hash ]
    def parse(opts = {})
      result = {}

      each { |option_file| result.deep_merge!(option_file.parse(opts)) }

      opts[:symbolize_keys] ? result.deep_symbolize_keys : result
    end
  end
end


# frozen_string_literal: true

module OptParseValidator
  # Implementation of the Integer Option
  class OptInteger < OptBase
    # @param [ String ] value
    #
    # @return [ Integer ]
    def validate(value)
      raise Error, "#{value} is not an integer" if value.to_i.to_s != value

      value.to_i
    end
  end
end


# -*- encoding: utf-8 -*-
require "zipping/version"
require 'zip'
require 'pathname'

module Zipping

  #
  # Constants
  #

  # header signatures
  ZS_c_pk0102        = 0x02014b50
  ZS_c_pk0304        = 0x04034b50
  ZS_c_pk0506        = 0x06054b50
  ZS_c_pk0708        = 0x08074b50

  # values for zip version
  ZS_c_ver_nocomp    = 0x000a
  ZS_c_ver_deflate   = 0x0014
  ZS_c_ver_made      = 0x0315

  # values for option
  ZS_c_opt_none      = 0x0000
  ZS_c_opt_0708      = 0x0008

  # compression types
  ZS_c_comp_deflate  = 0x0008
  ZS_c_comp_none     = 0x0000

  # empty values
  ZS_c_int2_zero     = 0x0000
  ZS_c_int4_zero     = 0x00000000

  # OS-specific attributes (OS X)
  ZS_c_oattr_dir     = 0x41ed4000
  ZS_c_oattr_file    = 0x81a44000
  ZS_c_oattr_symlink = 0xa1ed4000

  #
  # API methods
  #

  def self.files_to_zip(output_stream, files, file_division_size = 1048576, encoding = :utf8)
    builder = ZipBuilder.new output_stream, file_division_size, encoding
    builder.pack files
    builder.close
    output_stream
  end

  # use if you need to make File directly.
  def self.files_to_zipfile(path, files, encoding = :utf8)
    File.open(path, 'wb'){|f| files_to_zip f, files, 1048576, encoding}
  end

  # use if you need to make binary String directly.
  def self.files_to_zipdata(files, encoding = :utf8)
    files_to_zip ''.force_encoding('ASCII-8bit'), files, 1048576, encoding
  end

  #
  # Internal Classes
  #

  # Errors
  class Error < StandardError
  end

  # Output stream wrapper that measures size of stream passing through.
  class StreamMeter
    def initialize(output_stream)
      @output_stream = output_stream
      @size = 0
      @crc = Zlib.crc32
    end
    def << (data)
      @size += data.bytesize
      @crc = Zlib.crc32 data, @crc
      @output_stream << data
    end
    attr_reader :size
    attr_reader :crc
  end

  class ZipBuilder

    # Initialize ZipBuilder.
    # 'files' must be a String(file or directory path), a Hash(entity), or an Array of Strings and/or Hashes.
    def initialize(output_stream, file_division_size = 1048576, encoding = :utf8)
      @w = Writer.new output_stream, file_division_size
      @e = encoding
      @l = []
    end

    ### Attr controls

    def reset_state
      @pending_dirs = []
      @current_dir = {name: '', time: Time.now}
    end

    def has_dir?
      ! @pending_dirs.empty?
    end

    def next_dir
      @pending_dirs.shift
    end

    def postpone_dir(dir)
      queue_entity dir, @pending_dirs
    end

    def postpone_symlink(link)
      queue_entity link, @l
    end

    def queue_entity(entity, queue)
      entity[:abs_path] = abs_path_for entity[:name] || File.basename(entity[:path])
      queue << entity
    end

    def cd(dir)
      @current_dir = dir
    end

    def root_dir?
      @current_dir.nil? || @current_dir[:abs_path].nil?
    end

    def current_dir
      root_dir? ? '' : @current_dir[:abs_path]
    end

    def current_dir_entity
      @current_dir
    end

    def abs_path_for(name)
      root_dir? ? name : (@current_dir[:abs_path] + '/' + name)
    end

    def abs_path_for_entity(entity)
      abs_path_for entity[:name] || File.basename(entity[:path])
    end

    ### Conversions

    # Get entities of files in dir
    def subdir_entities(dir = @current_dir)
      Dir.glob(dir[:path].gsub(/[*?\\\[\]{}]/, '\\\\\0') + '/*').map!{|path| {path: path, time: File.mtime(path), name: File.basename(path)}}
    end

    # Fix an entity: time -> DOSTime object, name -> abs path in zip & encoded
    def fix_entity(entity)
      {
        path: entity[:path],
        filetime: Zip::DOSTime.at(entity[:time] || File.mtime(entity[:path])),
        binary_name: string_to_bytes(abs_path_for_entity(entity)),
        zip_path: abs_path_for_entity(entity)
      }
    end

    def fix_current_dir_entity
      fix_entity(@current_dir).merge!(
        {
          binary_name: string_to_bytes(@current_dir[:abs_path] + '/'),
          zip_path: @current_dir[:abs_path]
        }
      )
    end

    # Create ASCII-8bits string. Also convert encoding if needed.
    def string_to_bytes(str)
      unless @e.nil? || @e == :utf8
        if @e == :shift_jis
          begin
            str = str.gsub /[\\:*?"<>|\uff5e]/, '？'
            str.encode! 'Shift_JIS', :invalid => :replace, :undef => :replace, :replace => '？'
          rescue => e
          end
        end
      end
      [str].pack('a*')
    end

    ### Compression operations

    # Pack file and directory entities and output to stream.
    def pack(files)
      entities = Entity.entities_from files
      return if entities.empty?

      reset_state
      pack_entities entities
      while has_dir?
        cd next_dir
        pack_current_dir
      end
    end

    # Pack a directory
    def pack_current_dir
      pack_current_directory_entity
      pack_entities subdir_entities
    end

    # Pack symlinks if its link path exists in zip
    def pack_symlinks
      reset_state
      @l.each do |link|
        if @w.path_exists? Entity.linked_path(link[:abs_path], File.readlink(link[:path]))
          link[:name] = link[:abs_path]
          pack_symbolic_link_entity link
        end
      end
    end

    # Create central directories
    def close
      pack_symlinks
      @w.close
    end

    # Pack file entities. Directory entities are queued, not packed in this method.
    def pack_entities(entities)
      entities.each do |entity|
        # ignore bad entities
        next unless entity.is_a?(Hash) && entity[:path]

        path = entity[:path]
        if File.symlink? path
          postpone_symlink entity
        elsif File.directory? path
          postpone_dir entity
        elsif File.file? path
          pack_file_entity entity
        end
      end
    end

    def pack_file_entity(entity)
      pack_entity entity do
        @w.write_file_entry
      end
    end

    def pack_symbolic_link_entity(entity)
      pack_entity entity do
        @w.write_symbolic_link_entry
      end
    end

    def pack_current_directory_entity
      @w.load_entity fix_current_dir_entity
      @w.write_directory_entry
    end

    def pack_entity(entity)
      @w.load_entity fix_entity entity
      yield
    end
  end

  class Writer

    def initialize(output_stream, file_division_size)
      @output_stream = output_stream
      raise Error, 'Specified output stream does not support `<<\' method.' unless @output_stream.respond_to? :<<
      @o = StreamMeter.new output_stream
      @s = file_division_size
      raise Error, 'Bad file_division_size' unless @s.is_a?(Integer) && @s > 0
      @dps = []
      @entries = []
    end

    ### Data interface

    def load_entity(entity)
      @fixed_entity = entity
      @entries << entity[:zip_path]
    end

    def path_exists?(abs_path)
      @entries.include? abs_path
    end

    ### Write operations

    def write_file_entry
      write_entry do |path, filetime, name|
        write PKHeader.pk0304(filetime, name.length, true).pack('VvvvvvVVVvv'), name
        ret = deflate_file path
        write PKHeader.pk0708(ret[:crc], ret[:complen], ret[:uncomplen]).pack('VVVV')
        ret
      end
    end

    def write_directory_entry
      write_entry_without_compress '', :dir
    end

    def write_symbolic_link_entry
      write_entry_without_compress File.readlink(@fixed_entity[:path]), :symlink
    end

    def close
      # start of central directories
      @header_offset = current_position
      write_central_dir_headers

      # total size of central directories
      @header_size = current_position - @header_offset
      write_end_central_dir_header
    end

    ### Internal methods
    private

    def write(*args)
      args.each do |content|
        @o << content
      end
    end

    def current_position
      @o.size
    end

    def pipe_little_by_little(o, i)
      o << i.read(@s) until i.eof?
    end

    def deflate_file(path)
      meter = StreamMeter.new @o
      deflater = Zip::Deflater.new meter
      File.open(path, 'rb'){|f| pipe_little_by_little deflater, f}
      deflater.finish
      {
        crc: deflater.crc,
        complen: meter.size,
        uncomplen: deflater.size,
        deflated: true
      }
    end

    def write_entry_without_compress(data, type)
      write_entry do |path, filetime, name|
        crc = Zlib.crc32 data
        write PKHeader.pk0304(filetime, name.length, false, crc, data.size, data.size).pack('VvvvvvVVVvv'), name, data
        {
          type: type,
          crc: crc,
          complen: data.size,
          uncomplen: data.size
        }
      end
    end

    def write_entry
      remember_entry_info current_position, yield(@fixed_entity[:path], @fixed_entity[:filetime], @fixed_entity[:binary_name])
    end

    def remember_entry_info(offset, write_result)
      @dps << @fixed_entity.merge!(write_result).merge!({offset: offset})
    end

    def write_central_dir_headers
      @dps.each do |dp|
        write PKHeader.pk0102(dp).pack('VvvvvvvVVVvvvvvVV'), dp[:binary_name]
      end
    end

    def write_end_central_dir_header
      write PKHeader.pk0506(@dps.length, @header_size, @header_offset).pack('VvvvvVVv')
    end
  end

  class Entity
    def self.entities_from(files)
      if files.is_a? Array
        entities_from_array files
      else
        [entity_from(files)]
      end
    end

    # Create an entity Hash with a path String
    def self.entity_from(ent)
      if ent.is_a?(Hash) && File.exists?(ent[:path])
        ent
      elsif ent.is_a?(String) && File.exists?(ent)
        entity_from_path ent
      end
    end

    def self.entities_from_array(arr)
      arr.map{|ent| entity_from ent}.delete_if(&:nil?)
    end

    def self.entity_from_path(path)
      {
        :path => path,
        :name => File.basename(path),
        :time => File.mtime(path)
      }
    end

    def self.linked_path(abs_path, link)
      (Pathname.new(abs_path).parent + link).expand_path('/').to_s[1..-1]
    end
  end

  module PKHeader
    # 0102: central directory
    def self.pk0102(dp)
      # dp: info of an entry
      [
        ZS_c_pk0102,
        ZS_c_ver_made,
        (dp[:deflated] ? ZS_c_ver_nocomp : ZS_c_ver_deflate),
        (dp[:deflated] ? ZS_c_opt_0708 : ZS_c_opt_none),
        (dp[:deflated] ? ZS_c_comp_deflate : ZS_c_comp_none),
        dp[:filetime].to_binary_dos_time,
        dp[:filetime].to_binary_dos_date,
        dp[:crc],
        dp[:complen],
        dp[:uncomplen],
        dp[:binary_name].length,
        ZS_c_int2_zero,
        ZS_c_int2_zero,
        ZS_c_int2_zero,
        ZS_c_int2_zero,
        (dp[:type] == :symlink ? ZS_c_oattr_symlink : dp[:type] == :dir ? ZS_c_oattr_dir : ZS_c_oattr_file),
        dp[:offset]
      ]
    end

    # 0304: header of entries
    def self.pk0304(filetime, namelen, deflated, crc = nil, compsize = nil, uncompsize = nil)
      [
        ZS_c_pk0304,
        (deflated ? ZS_c_ver_deflate : ZS_c_ver_nocomp),
        (deflated ? ZS_c_opt_0708 : ZS_c_opt_none),
        (deflated ? ZS_c_comp_deflate : ZS_c_comp_none),
        filetime.to_binary_dos_time,
        filetime.to_binary_dos_date,
        (crc || ZS_c_int4_zero),
        (compsize || ZS_c_int4_zero),
        (uncompsize || ZS_c_int4_zero),
        namelen,
        ZS_c_int2_zero
      ]
    end

    # 0506: end of central directory
    def self.pk0506(entry_count, dirsize, offset)
      # entry_count: count of entries in zip
      # dirsize: sum of central directory sizes
      # offset: byte offset of the first central directory
      [
        ZS_c_pk0506,
        ZS_c_int2_zero,
        ZS_c_int2_zero,
        entry_count,
        entry_count,
        dirsize,
        offset,
        ZS_c_int2_zero
      ]
    end

    # 0708: optional trailer of entries
    def self.pk0708(crc, compsize, uncompsize)
      # crc: CRC32 for uncompressed data
      # compsize: size of compressed data
      # uncompsize: size of uncompressed data
      [
        ZS_c_pk0708,
        crc,
        compsize,
        uncompsize
      ]
    end
  end
end


# frozen_string_literal: true

module Navigator
  # Conditionally activates a tag.
  class TagActivator
    attr_reader :search_key, :search_value, :target_key, :target_value

    # rubocop:disable Metrics/ParameterLists
    def initialize search_key: :href, search_value: nil, target_key: :class, target_value: "active"
      @search_key = search_key
      @search_value = search_value
      @target_key = target_key
      @target_value = target_value
    end
    # rubocop:enable Metrics/ParameterLists

    # :reek:TooManyStatements
    def activatable? attributes = {}
      return false unless search_value.present?

      attributes = attributes.with_indifferent_access
      current_search_value = attributes[search_key]

      if current_search_value.is_a?(Regexp) || search_value.is_a?(Regexp)
        return false if current_search_value.blank?

        current_search_value.match? search_value
      else
        current_search_value == search_value
      end
    end

    # rubocop:disable Metrics/LineLength
    def activate attributes = {}
      attributes = attributes.with_indifferent_access
      attributes[target_key] = [attributes[target_key], target_value].compact.join " " if activatable? attributes
      attributes
    end
    # rubocop:enable Metrics/LineLength
  end
end


# frozen_string_literal: true

module Navigator
  # Renders a HTML menu.
  class Menu
    # rubocop:disable Metrics/LineLength
    def self.allowed_methods
      /^(div|section|header|h[1-6]|nav|ul|li|a|img|b|em|s|small|span|strong|sub|sup|form|label|select|option|input|button)$/
    end
    # rubocop:enable Metrics/LineLength

    def initialize template,
                   tag: "ul",
                   attributes: {},
                   activator: Navigator::TagActivator.new,
                   &block

      @template = template
      @tag = Navigator::Tag.new tag, attributes: attributes, activator: activator
      @menu_activator = activator
      @items = []
      instance_eval(&block) if block_given?
    end

    def add name, content = nil, attributes: {}, activator: menu_activator, &block
      tag = Navigator::Tag.new name, content, attributes: attributes, activator: activator
      return items << tag.render unless block_given?

      items << tag.prefix
      items << tag.content
      instance_eval(&block)
      items << tag.suffix
    end

    def link content = nil, url, attributes: {}, activator: menu_activator, &block
      add "a", content, attributes: attributes.merge(href: url), activator: activator, &block
    end

    def image url, alt = nil, attributes: {}, activator: menu_activator
      modified_attributes = attributes.merge src: url, alt: alt
      modified_attributes = modified_attributes.delete_if { |_, value| !value.present? }

      add "img", attributes: modified_attributes, activator: activator
    end

    def item content = nil,
             url,
             item_attributes: {},
             link_attributes: {},
             activator: menu_activator,
             &block

      modified_item_attributes = item_attributes.clone
      activate_item_attributes! modified_item_attributes, url, activator

      add "li", attributes: modified_item_attributes, activator: activator do
        link content,
             url,
             attributes: link_attributes,
             activator: Navigator::TagActivator.new,
             &block
      end
    end

    def method_missing name, *args, &block
      if respond_to_missing? name
        add(name, *args, &block)
      else
        template.public_send(name, *args) || super
      end
    end

    def render
      [tag.prefix, tag.content, items.compact.join(""), tag.suffix].compact * ""
    end

    private

    attr_accessor :template, :tag, :menu_activator, :items

    def respond_to_missing? name, include_private = false
      self.class.allowed_methods.match?(name) || super
    end

    def activate_item_attributes! attributes, url, activator
      return unless url == activator.search_value

      key = [attributes[activator.target_key], activator.target_value].compact.join " "
      attributes[activator.target_key] = key
    end
  end
end


# frozen_string_literal: true

module Navigator
  # Navigation helper methods for the view layer.
  module NavigationHelper
    # rubocop:disable Metrics/ParameterLists
    # rubocop:disable Metrics/LineLength
    def navigation tag = "ul", attributes: {}, activator: navigation_activator, &block
      raw Navigator::Menu.new(self, tag: tag, attributes: attributes, activator: activator, &block).render
    end
    # rubocop:enable Metrics/ParameterLists
    # rubocop:enable Metrics/LineLength

    module_function

    def current_path
      request.env["PATH_INFO"]
    end

    def navigation_activator
      Navigator::TagActivator.new search_value: current_path
    end
  end
end


module UiBibz::Ui::Ux::Tables
  class Searchable < UiBibz::Ui::Base

    def initialize store, options, html_options = nil
      @store        = store
      @options      = options
      @html_options = html_options
    end

    # Render html tag
    def render
      content_tag :div, @html_options do
        concat content_tag(:div, table_name, class: 'title')
        concat TableSearchField.new({ store: @store, wrap_form: @options[:wrap_form] }).render if searchable?
        concat tag :br, class: 'ui-bibz-clear'
      end
    end

    def searchable?
      @options[:searchable].nil? ? true : @options[:searchable]
    end

  private

    def translate_searchable_attributes_by_active_record attr
      @store.model.human_attribute_name(attr)
    end

    def table_title
      if @options[:title] != false
        title = @options[:title] || "#{ @store.controller.humanize } list"
        UiBibz::Utils::Internationalization.new("ui_bibz.table.title.#{ model_name }", default: ["ui_bibz.table.title.defaults", title]).translate
      end
    end

    def model_name
      @store.model.to_s.underscore
    end

    def table_name
      "#{table_glyph}#{table_title}".html_safe
    end

    def table_glyph
      UiBibz::Ui::Core::Icons::Glyph.new(@options[:glyph]).render unless @options[:glyph].nil?
    end

  end
end


require 'ui_bibz/ui/core/navigations/components/nav_link_link'
require 'ui_bibz/ui/core/navigations/components/nav_link_list'
require 'ui_bibz/ui/core/navigations/components/nav_link_span'
module UiBibz::Ui::Core::Navigations

  # Create a NavLink
  #
  # This element is an extend of UiBibz::Ui::Core::Navigations::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +state+ - status of élement with symbol value:
  #   (+:active+)
  # * +url+ - String
  # * +badge+ - String
  # * +link_html_options+ - Hash
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Navigations::NavLink.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Navigations::NavLink.new(options = nil, html_options = nil) do
  #     content
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Navigations::NavLink.new(content, { badge: 15, url: '/', state: :active, link_html_options: { class: 'link1' }},{ class: 'test' }).render
  #
  #   UiBibz::Ui::Core::Navigations::NavLink.new({glyph: { name: 'eye', size: 3 }, { class: 'test' }) do
  #     'Home'
  #   end.render
  #
  class NavLink < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
      @old_options = options
    end

    # Render html tag
    def pre_render
      if options[:nav_type] == "nav-links"
        UiBibz::Ui::Core::Navigations::NavLinkLink.new(content, options, html_options).render
      else
        if options[:tag_type] == :span
          cont = UiBibz::Ui::Core::Navigations::NavLinkSpan.new(content, @old_options).render
        else
          cont = UiBibz::Ui::Core::Navigations::NavLinkLink.new(content, options).render
        end
        #html_options[:class] = remove_class(html_options[:class])
        remove_classes
        UiBibz::Ui::Core::Navigations::NavLinkList.new(cont, options, html_options).render
      end
    end

    private

    def remove_classes
      exclude_classes_in_html_options %w(active disabled)
      options.delete(:state)
    end

  end
end


require "ui_bibz/ui/core/lists/components/list/list_header"
require "ui_bibz/ui/core/lists/components/list/list_body"
module UiBibz::Ui::Core::Lists::Components

  # Create a list
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +state+ - status of élement with symbol value:
  #   (+:active+, +:disabled+)
  # * +url+
  # * +tap+ - Boolean to add header and body
  # * +status+ - status of élement with symbol value:
  #   (+:default+, +:primary+, +:info+, +:warning+, +:danger+)
  # * +glyph+ - Add glyph with name or hash options
  #   * +name+ - String
  #   * +size+ - Integer
  #   * +tag+ - Symbol (+:a+, +:button+, +:li+)
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Lists::Components::List.new(tap: true) do |l|
  #     l.header content =  nil, options = nil, html_options = nil, &block
  #     l.body content =  nil, options = nil, html_options = nil, &block
  #   end
  #
  #   UiBibz::Ui::Core::Lists::Components::List.new content, options = nil, html_options = nil
  #
  #   UiBibz::Ui::Core::Lists::Components::List.new options = nil, html_options = nil do
  #     content
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Lists::Components::List.new('Test', status: :success, url: '#test').render
  #
  #   UiBibz::Ui::Core::Lists::Components::List.new(status: :primary) do
  #       'Test 2'
  #   end.render
  #
  #   UiBibz::Ui::Core::List.new(tap: true, state: :active) do |l|
  #     l.header 'My title', nil, class: 'my-title'
  #     l.body do
  #       'My content'
  #     end
  #   end.render
  #
  class List < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
    end

    # Render html tag
    def pre_render
      content_tag tag_type, html_options do
        concat glyph_and_content_html if @content
        concat header_html if @body
        concat body_html   if @body
        concat badge_html  if @options[:badge]
      end
    end

    # Add header which is a component
    def header content = nil, options = nil, html_options = nil, &block
      @header = UiBibz::Ui::Core::Lists::Components::ListHeader.new content, options, html_options, &block
    end

    # Add body which is a component
    def body content = nil, options = nil, html_options = nil, &block
      @body = UiBibz::Ui::Core::Lists::Components::ListBody.new content, options, html_options, &block
    end

  private

    def component_html_classes
      super << ['list-group-item', tag_type_class, badge_classes, header_classes]
    end

    def header_html
      @header.render
    end

    def body_html
      @body.render
    end

    def badge_classes
      "d-flex justify-content-between align-items-center" unless options[:badge].nil?
    end

    def status
      "list-group-item-#{ @options[:status] }" unless @options[:status].nil?
    end

    def header_classes
      "flex-column align-items-start" unless options[:tap].nil?
    end

    def tag_type_class
      "list-group-item-action" if is_button_type? || is_link_type?
    end

    def is_link_type?
      @html_options[:href] = @options[:url] if @options[:url]
      @options[:tag_type] == :a || @html_options.delete(:tag_type) == :a
    end

    def is_button_type?
      @options[:tag_type] == :button || @html_options.delete(:tag_type) == :button
    end

    def tag_type
      if is_link_type?
        :a
      elsif is_button_type?
        :button
      else
        :li
      end
    end

    def badge_html
      if options[:badge].kind_of? Hash
        options[:badge][:status] = options[:status] || :secondary
        UiBibz::Ui::Core::Notifications::Badge.new(options[:badge].delete(:content), options[:badge]).render

      else
        UiBibz::Ui::Core::Notifications::Badge.new(options[:badge], type: :pill, status: (options[:status] || :secondary)).render
      end
    end

  end
end


require "ui_bibz/ui/core/lists/components/list"
module UiBibz::Ui::Core::Lists

  # Create a list group
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +flush+ - Boolean
  # * +tag_type+ - Type of list (default: :li)
  #   (+:a+, +:button+, +:li+)
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::ListGroup.new().tap |lg|
  #     ...
  #     lg.list content = nil, options = nil, html_options = nil, &block
  #     ...
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::ListGroup.new().tap do |d|
  #     d.list 'Test', status: :success
  #     d.list 'Test2', status: :primary
  #   end.render
  #
  #   UiBibz::Ui::Core::ListGroup.new(tag_type: :li).tap do |d|
  #     d.list 'Test', status: :success, url: '#test'
  #     d.list(status: :primary) do
  #       'Test 2'
  #     end
  #     d.list(tap: true, state: :active) do |l|
  #       l.header 'My title', nil, class: 'my-title'
  #       l.body do
  #         'My content'
  #       end
  #     end
  #   end.render
  #
  # ==== Helper
  #
  #   list_group( options = { tap: true }, html_options = {}) do |l|
  #     l.list(content, options = {}, html_options = {})
  #     l.list(options = {}, html_options = {}) do
  #       content
  #     end
  #     l.list(options = { tap: true }, html_options = {}) do |li|
  #       li.header(content, options = {}, html_options = {})
  #       # or
  #       li.header(options = {}, html_options = {}) do
  #         content
  #       end
  #
  #       li.body(content, options = {}, html_options = {})
  #       # or
  #       li.body(options = {}, html_options = {}) do
  #         content
  #       end
  #     end
  #   end
  #
  class ListGroup < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
      @lists = []
    end

    # Render html tag
    def pre_render
      content_tag tag_type, @lists.join().html_safe, html_options
    end

    # Add group list
    # See UiBibz::Ui::Core::List
    def list content = nil, options = {} , html_options = nil, &block
      options = options.merge({ tag_type: @options[:tag_type] }) unless @options[:tag_type].nil?

      if is_tap(content, options)
        @lists << UiBibz::Ui::Core::Lists::Components::List.new(content, options, html_options).tap(&block).render
      else
        @lists << UiBibz::Ui::Core::Lists::Components::List.new(content, options, html_options, &block).render
      end
    end

  private

    def component_html_classes
      super << ["list-group", flush]
    end

    def flush
      "list-group-flush" if options[:flush]
    end

    def tag_type
      [:a, :button].include?(options[:tag_type]) ? :div : :ul
    end

  end
end


module UiBibz::Ui::Core::Notifications::Components

  # Create a alert body
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Notifications::ToastHeader.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Notifications::ToastHeader.new(options = nil, html_options = nil) do
  #     content
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Notifications::ToastHeader.new.render
  #
  #   UiBibz::Ui::Core::Notifications::ToastHeader.new do
  #     'Exemple'
  #   end.render
  #
  class ToastHeader < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
    end

    def pre_render
      content_tag :div, html_options do
        concat UiBibz::Ui::Core::Icons::Glyph.new(options[:glyph], class: 'mr-2').render unless options[:glyph].nil?
        concat image_tag(options[:img], class: 'rounded mr-2') unless options[:img].nil?
        concat content_tag(:strong, content, class: 'mr-auto')
        concat content_tag(:small, options[:time], class: 'text-muted') unless options[:time].nil?
        concat close_button
      end
    end

  private

    def close_button
      content_tag :button, class: 'ml-2 mb-1 close', "data-dismiss": "toast", "aria-label": "Close" do
        content_tag(:span, "×", "aria-hidden": true).html_safe
      end
    end

    def component_html_classes
      "toast-header"
    end

  end
end


module UiBibz::Ui::Core::Forms::Files

  # Create a FileField
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +state+ - Symbol
  #   (+:active+, +:disabled+)
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Forms::Texts::FileField.new(content, options = {}, html_options = {}).render
  #
  #   UiBibz::Ui::Core::Forms::Texts::FileField.new(options = {}, html_options = {}) do
  #     content
  #   end.render
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Forms::Texts::FileField.new('upload', class: 'test')
  #
  #   UiBibz::Ui::Core::Forms::Texts::FileField.new(class: 'test') do
  #     #content
  #   end
  #
  # ==== Helper
  #
  #   ui_file_field(options = {}, html_options = {}) do
  #    # content
  #   end
  #
  class FileField < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
    end

    # Render html tag
    def pre_render
      content_tag :div, html_options do
        concat file_field_tag content, class: "custom-file-input" , multiple: options[:multiple], disabled: is_disabled?
        concat label_tag label_name, label_content, class: 'custom-file-label'
      end
    end

    private

    def label_name
      html_options[:id] || content
    end

    def label_content
      options[:value] || ''
    end

    def component_html_classes
      super << "custom-file"
    end

  end
end


require 'ui_bibz/ui/core/forms/choices/components/choice'
module UiBibz::Ui::Core::Forms::Choices

  # Create a choice group
  #
  # This element is an extend of UiBibz::Ui::Core::Forms::Choices::ButtonGroup
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +type+ - Symbol (+:checkbox+, +:radio+)
  # * +status+ - status of élement with symbol value:
  #   (+:primary+, +:secondary+, +:info+, +:warning+, +:danger+)
  # * +size+
  #   (+:xs+, +:sm+, +:lg+)
  # * +outline+ - Boolean
  # * +state+ - Symbol (+:active+, +:disabled)
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Forms::Choices::ChoiceGroup.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Forms::Choices::ChoiceGroup.new(options = nil, html_options = nil) do
  #     content
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Forms::Choices::ChoiceGroup.new({ status: primary }, { class: 'lable-class'}) do |cg|
  #     cg.choice 'Choice 1'
  #     cg.choice 'Choice 2'
  #   end.render
  #
  # ==== Helper
  #
  #   ui_choice_group(options = {}, html_options = {}) do |cg|
  #     cg.choice content, options, html_options
  #     cg.choice content, options, html_options
  #   end
  #
  class ChoiceGroup < UiBibz::Ui::Core::Forms::Buttons::ButtonGroup

    # See UiBibz::Ui::Core::Forms::Choices::Button.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
    end

    def choice content = nil, opts = nil, html_options = nil, &block
      if block.nil?
        opts    = @options.merge(opts || {})
      else
        content = @options.merge(content || {})
      end

      @items << Choice.new(content, opts, html_options, &block).render
    end

    private

    def component_html_classes
      super << ['button-choice', "btn-group-toggle", options.delete(:class)]
    end

    def component_html_options
      {}
    end

    def component_html_data
      super
      add_html_data "toggle", "buttons"
    end

  end
end


require "ui_bibz/ui/ux/tables/components/store"
require "ui_bibz/ui/ux/tables/components/columns"
require "ui_bibz/ui/ux/tables/components/column"
require "ui_bibz/ui/ux/tables/components/actions"
require "ui_bibz/ui/ux/tables/components/thead"
require "ui_bibz/ui/ux/tables/components/as"
require "ui_bibz/ui/ux/tables/extensions/paginable"
require "ui_bibz/ui/ux/tables/extensions/paginable"
require "ui_bibz/ui/ux/tables/extensions/searchable"
require "ui_bibz/ui/ux/tables/extensions/sortable"
require "ui_bibz/ui/ux/tables/extensions/actionable"
module UiBibz::Ui::Ux::Tables

  # Create a Table
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +store+ - Store generate by '+table_search_pagination+' method
  # * +url+ - String
  # * +tap+ - Boolean
  # * +actionable+ - Boolean
  # * +sortable+ - Boolean
  # * +searchable+ - Boolean
  # * +default_actions+ - Boolean
  # * +status+
  #   (+:inverse+, +:default+, +:success+, +:primary+, +:secondary+, +:info+,
  #   +:danger+, +:warning+)
  # * +thead+ - Hash
  #   (+status+)
  #     (+:inverse+, +:default+)
  # * +bordered+ - Boolean
  # * +hoverable+ - Boolean
  # * +size+
  #   (+:sm+)
  # * +responsive+ - Boolean
  # * +reflow+ - Boolean
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Ux::Tables::Table.new(store: @store)
  #
  #   UiBibz::Ui::Ux::Tables::Table.new(store: @store, tap: true) do |t|
  #     t.columns do |c|
  #       c.column :id, name: '#'
  #     end
  #     t.actions do |a|
  #       a.link '', url: url, glyph: ''
  #     end
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Ux::Tables::Table.new(store: @users).render
  #
  #   UiBibz::Ui::Ux::Tables::Table.new(store: @users).tap do |t|
  #     t.columns do |c|
  #       c.column :id, { name: '#' }
  #       c.column :name_fr', { link: edit_user_path(:id), order: 2 }
  #       c.column :name_en'
  #       c.column :state_id, { name: 'state', format: lambda{ |records, record| "Test #{ record.id}"} }
  #     end
  #     t.actions do |a|
  #       a.link 'state', url: users_path(:id), glyph: 'eye'
  #       a.divider
  #       a.link 'momo', url: users_path(:id), glyph: 'home'
  #     end
  #   end.render
  #
  # ==== Helper
  #
  #   table(options = {}, html_options = {})
  #
  #   table(options = { tap: true }, html_options = {}) do |t|
  #     t.columns do |cls|
  #       cls.column(name, options = {}, html_options = {})
  #       cls.column(options = {}, html_options = {}) do
  #         name
  #       end
  #     end
  #     t.actions do |acs|
  #       acs.link(content, options = {}, html_options = {})
  #       acs.link(options = {}, html_options = {}) do
  #         content
  #       end
  #     end
  #   end
  class Table < UiBibz::Ui::Core::Component

    attr_accessor :columns

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
      @columns = Columns.new
      @actions = Actions.new store
    end

    # Add table columns items
    def columns &block
      @columns.tap(&block)
    end

    # Add table actions items
    def actions &block
      @actions.tap(&block)
    end

    # for test
    def actions_list
      @actions
    end

    # Render html tag
    def pre_render
      table_html
    end

    # Store must be generated by *table_search_pagination* method
    def store
      @store ||= if @options[:store].nil?
        raise 'Store is nil!'
      elsif @options[:store].try(:records).nil?
        raise 'Store can be created only with "table_search_pagination" method!'
      else
        Store.new @options.delete :store
      end
    end

  protected

    def sort
      @sort ||= Sortable.new store, @options
    end

    def action
      @action ||= Actionable.new store, @options, @actions
    end

    def cols
      @columns.list.empty? ? store.columns.list : @columns.list
    end

    def type
      "table-#{ @options[:type] }" unless @options[:type].nil?
    end

    def table_html
      content_tag(:table, html_options) do

        ths = cols.collect do |col|
          content_tag(:th, sort.header(col), class: col.class) unless col.hidden?
        end

        ths = action.header ths
        concat Thead.new(content_tag(:tr, ths.join.html_safe), @options[:thead]).render

        trs = store.records.collect do |record|
          tds = cols.collect do |col|
            content_tag(:td, td_content(record, col), class: col.class) unless col.hidden?
          end

          tds = action.body record, tds
          content_tag(:tr, tds.join.html_safe)
        end

        concat content_tag :tbody, trs.join.html_safe
      end
    end

    # Maybe create a class for td_content
    def td_content record, col
      content = col.count ? record.send(col.data_index).count : record.send(col.data_index)
      unless content.nil?
        content = content.strftime(col.date_format)                    unless col.date_format.nil?
        content = link_to content, action.inject_url(col.link, record) unless col.link.nil?
        content = col.format.call(@store.records, record)              unless col.format.nil?
      end
      content = As.new(col, record, content, @options).render unless col.as.nil?
      content
    end

  private

    def component_html_classes
      ["table", striped, bordered, hoverable, size, responsive, reflow]
    end

    def status
      "table-#{ @options[:status] }" unless @options[:status].nil?
    end

    def striped
      "table-striped" unless @options[:striped].nil?
    end

    def bordered
      "table-bordered" unless @options[:bordered].nil?
    end

    def hoverable
      "table-hoverable" unless @options[:hoverable].nil?
    end

    def size
      "table-#{ @options[:size] }" unless @options[:size].nil?
    end

    def responsive
      "table-responsive" unless @options[:responsive].nil?
    end

    def reflow
      "table-reflow" unless @options[:reflow].nil?
    end

  end
end


require 'ui_bibz/ui/core/notifications/components/toast_header'
require 'ui_bibz/ui/core/notifications/components/toast_body'
module UiBibz::Ui::Core::Notifications

  # Create an alert
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +status+ - status of élement with symbol value:
  #   (+:default+, +:primary+, +:info+, +:warning+, +:danger+)
  # * +glyph+ - [String | Hash] Add glyph with name or hash options
  #   * +name+ - [String]
  #   *+timeout+ - [Integer]
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Toast.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Toast.new(options = nil, html_options = nil) do
  #     content
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Toast.new(class: 'my-toast').tap |t|
  #     t.header "My header toast", glyph: 'eye', time: 'now'
  #     t.body "My body toast"
  #   end
  #
  #   UiBibz::Ui::Core::Toast.new(class: 'my-toast').tap |t|
  #     t.header glyph: 'eye', time: 'now' do
  #       My header toast
  #     end
  #     t.body class: 'my-body-toast' do
  #       My body toast
  #     end
  #   end.render
  #
  # ==== Helper
  #
  #   ui_toast(options = { tap: true }, html_options = {}) do |t|
  #     t.header "My header toast", glyph: 'eye', time: 'now'
  #     t.body "My body toast"
  #   end
  #
  class Toast < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
    end

    # Render html tag
    def pre_render
      content_tag :div, html_options do
        concat @header
        concat @body
      end
    end

    # Add Header which is a component
    def header content = nil, options = nil, html_options = nil, &block
      @header = UiBibz::Ui::Core::Notifications::Components::ToastHeader.new(content, options, html_options, &block).render
    end

    # Add Body which is a component
    def body content = nil, options = nil, html_options = nil, &block
      @body = UiBibz::Ui::Core::Notifications::Components::ToastBody.new(content, options, html_options, &block).render
    end

  private

    def component_html_classes
      super << 'toast'
    end

    def component_html_options
      { role: 'alert', "aria-live": "assertive", "aria-atomic": true }
    end

    def component_html_data
      super
      add_html_data "autohide", options[:auto_hide] if options[:auto_hide]
    end

  end
end


require 'ui_bibz/ui/core/boxes/components/card_header'
require 'ui_bibz/ui/core/boxes/components/card_footer'
require 'ui_bibz/ui/core/boxes/components/card_body'
require 'ui_bibz/ui/core/boxes/components/card_image'
require 'ui_bibz/ui/core/boxes/components/card_list_group'
module UiBibz::Ui::Core::Boxes

  # Create a card
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  # You can use tap method to add header, body or footer element.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +status+ - status of élement with symbol value:
  #   (+:primary+, +:secondary+, +:info+, +:warning+, +:danger+)
  # * text - Hash (+:size+, +:position+)
  #   (+:left+, +:right+, +:center+)
  # * block - Boolean
  # * type - Symbol
  #   (+:inverse+)
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Boxes::Card.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Boxes::Card.new(options = nil, html_options = nil) do
  #     content
  #   end
  #
  #   UiBibz::Ui::Core::Boxes::Card.new(options = nil, html_options = nil).tap do |p|
  #     p.header content = nil, options = nil, html_options = nil, &block
  #     p.block content = nil, options = nil, html_options = nil, &block
  #     p.footer content = nil, options = nil, html_options = nil, &block
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Boxes::Card('test').render
  #
  #   UiBibz::Ui::Core::Boxes::Card(status: :primary) do |d|
  #     'test'
  #   end.render
  #
  #   UiBibz::Ui::Core::Boxes::Card.new().tap do |p|
  #     p.header 'header', glyph: 'eye', class: 'header-test'
  #     p.block do
  #       'body'
  #     end
  #     p.footer 'footer'
  #   end.render
  #
  # ==== Helper
  #
  #   card(content, options = {}, html_options = {})
  #
  #   card(options = {}, html_options = {}) do
  #     content
  #   end
  #
  #   card(options = { tap: true }, html_options = {}) do |p|
  #     p.header(content, options = {}, html_options = {})
  #     # or
  #     p.header(options = {}, html_options = {}) do
  #       content
  #     end
  #
  #     p.body(content, options = {}, html_options = {})
  #     # or
  #     p.body(options = {}, html_options = {}) do
  #       content
  #     end
  #
  #     p.footer(content, options = {}, html_options = {})
  #     # or
  #     p.footer(options = {}, html_options = {}) do
  #       content
  #     end
  #   end
  #
  class Card < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
      @items = @content.nil? ? [] : [UiBibz::Ui::Core::Boxes::Components::CardBody.new(@content).render]
    end

    # Add Header which is a component
    def header content = nil, options = nil, html_options = nil, &block
      options, content = inherit_options(content, options, block)
      if is_tap(content, options)
        @header = UiBibz::Ui::Core::Boxes::Components::CardHeader.new(content, options, html_options).tap(&block).render
      else
        @header = UiBibz::Ui::Core::Boxes::Components::CardHeader.new(content, options, html_options, &block).render
      end
    end

    # Add Body div which is a component
    def body content = nil, options = nil, html_options = nil, &block
      options, content = inherit_options(content, options, block)
      if is_tap(content, options)
        content = (content || {}).merge(collapse: options.try(:[], :collapse), parent_collapse: @options[:parent_collapse] )
        @items << UiBibz::Ui::Core::Boxes::Components::CardBody.new(content, options, html_options).tap(&block).render
      else
        options = (options || {}).merge(collapse: options.try(:[], :collapse), parent_collapse: @options[:parent_collapse] )
        @items << UiBibz::Ui::Core::Boxes::Components::CardBody.new(content, options, html_options, &block).render
      end
    end

    # Add Footer which is a component
    def footer content = nil, options = nil, html_options = nil, &block
      options, content = inherit_options(content, options, block)
      @footer = UiBibz::Ui::Core::Boxes::Components::CardFooter.new(content, options, html_options, &block).render
    end

    # Add List group which is a component
    def list_group content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Core::Boxes::Components::CardListGroup.new(content, options, html_options).tap(&block).render
    end

    # Add Image which is a component
    def image content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Core::Boxes::Components::CardImage.new(content, options, html_options, &block).render
    end

    # Render html tag
    def pre_render
      content_tag :div, html_structure, html_options
    end

  protected

    def html_structure
      [@header, @items.join, @footer].compact.join.html_safe
    end

    def inherit_options content, options, block
      if block.nil?
        options = (options || {}).merge({ outline: @options[:outline] , status: @options[:status] })
      else
        content = (content || {}).merge({ outline: @options[:outline], status: @options[:status] })
      end
      [options, content]
    end

    def component_html_classes
      ["card", text, type, tab_pane, outline]
    end

    def status
      unless @options[:status].nil?
        output = @options[:outline] ? ["border-#{ @options[:status] }"] : ["bg-#{ @options[:status] }"]
        output << "text-white" if @options[:status] != :light && @options[:status] != :warning
        output.join(' ')
      end
    end

    def text
      "text-#{ @options[:text][:size] || :md }-#{ @options[:text][:position] || :left }" unless @options[:text].nil?
    end

    def card_block
      "card-block" if !@options[:block].nil? || @options[:tap].nil?
    end

    def type
      "card-#{ @options[:type] }" unless @options[:type].nil?
    end

    def tab_pane
      "tab-pane" if @options[:tab]
    end

    def outline
      "bg-transparent" if @options[:outline]
    end

  end
end


require 'ui_bibz/ui/core/navigations/components/navbar_nav'
require 'ui_bibz/ui/core/navigations/components/navbar_text'
require 'ui_bibz/ui/core/navigations/components/navbar_form'
require 'ui_bibz/ui/core/navigations/components/navbar_brand'
module UiBibz::Ui::Core::Navigations

  # Create a Navbar
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +type+ - Symbol
  #   (+:light+, +:dark+)
  # * +glyph+
  # * +status+
  #   (+:secondary+, +:primary+, +:info+, +:warning+, +:danger+, +inverse+)
  # * +position+ - Symbol
  #   (+:top+, +:bottom+)
  # * +position_type+ - Symbol
  #   (+:fixed+, +:sticky+)
  # * +title+ - String
  #
  # ==== Signatures
  #
  #
  #   UiBibz::Ui::Core::Navbar.new(options = nil, html_options = nil).tap do |nb|
  #     ...
  #     nb.nav(options = nil, html_options = nil) do |n|
  #       n.link content options = nil, html_options = nil, &block
  #       n.link content options = nil, html_options = nil, &block
  #     end
  #     ...
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Navbar.new().tap do |nb|
  #     nb.nav(position: :right) do |n|
  #       n.link 'Link 1', "#"
  #       n.link 'Link 2', "#"
  #     end
  #     nb.form(url: 'search/', type: :form_tag) do
  #       text_field_tag 'search', nil, { class: 'form-control', placeholder: 'Search' }
  #       button 'Submit', type: :submit
  #     end
  #     nb.text 'My text'
  #   end.render
  #
  # ==== Helper
  #
  #   ui_navbar(options = { tap: true }, html_options = {}) do |nb|
  #     nb.nav(options = { tap: true }, html_options = {}) do |n|
  #       n.link(content, options = {}, html_options = {})
  #       n.link(options = {}, html_options = {}) do
  #         content
  #       end
  #     end
  #     nb.brand content
  #     nb.link 'toto', "#"
  #   end
  #
  class Navbar < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
      @items = []
    end

    # Render html tag
    def pre_render
      content_tag :nav, html_options do
        concat title if brand_position == :left
        concat navbar_toggle_button_html
        concat title if brand_position == :right
        concat body_html
      end
    end

    # Add navbar nav items
    # See UiBibz::Ui::Core::NavbarNav
    def nav content = nil, options = nil, html_options = nil, &block
      options = options || {}
      @items << UiBibz::Ui::Core::Navigations::NavbarNav.new(content, options, html_options).tap(&block)
    end

    # Add navbar form items
    # See UiBibz::Ui::Core::NavbarForm
    def form model_or_url, options = {}, &block
      @items << UiBibz::Ui::Core::Navigations::NavbarForm.new(model_or_url, options, &block)
    end

    # Not use !!!!!
    # Add navbar text items
    # See UiBibz::Ui::Core::NavbarText
    def text content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Core::Navigations::NavbarText.new(content, options, html_options, &block)
    end

    def brand content = nil, options = nil, html_options = nil, &block
      @brand = UiBibz::Ui::Core::Navigations::NavbarBrand.new(content, options, html_options, &block).render
    end

    def spacer num = "auto"
      kls = " mr-#{ num }"
      @items.last.html_options[:class].nil? ? @items.last.html_options[:class] = kls : @items.last.html_options[:class] << kls
    end

    def id
      @id ||= generate_id("navbar-id")
    end

  private

    def component_html_classes
      ['navbar', type, position, expand_size]
    end

    def title
      @title ||= if @brand.nil?
         brand(@options[:title]) unless @options[:title].nil?
      else
        @brand
      end
    end

    def body_html
      content_tag :div, class: "navbar-collapse", id: id do
        concat @items.map(&:render).join.html_safe
      end
    end

    def navbar_toggle_button_html
      content_tag :button, "☰", class: 'navbar-toggler hidden-sm-up', type: :button, data: { toggle: 'collapse', target:"##{ id }" }
    end

    def expand_size
      "navbar-expand-#{ @options[:expand_size] || :lg }" #unless @options[:expand_size].nil?
    end

    def position
      "#{ position_type }-#{ @options[:position] }" unless @options[:position].nil?
    end

    def brand_position
      @options[:brand_position] || :left
    end

    # fixed || sticky
    def position_type
      @options[:position_type] || "fixed"
    end

    def type
      "navbar-#{ @options[:type] || 'light' }"
    end

    def status
      "bg-#{ @options[:status] }" unless @options[:status].nil?
    end

  end
end


module UiBibz::Ui::Core::Navigations

  # Create a tab_group
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +position+ - Symbol
  #   (+:left+, +:right+, +:center+)
  # * +tag_type+ - Symbol
  #   (+:a+, +:span)
  # * +justify+ - Boolean
  # * +fill+ - Boolean
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Navigations::TabGroup.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Navigations::TabGroup.new(options = nil, html_options = nil).tap do |n|
  #     ...
  #     n.tab content = nil, options = nil, html_options = nil, block
  #     n.tab content = nil, options = nil, html_options = nil, block
  #     n.dropdown content = nil, options = nil, html_options = nil, block
  #     ...
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Navigations::TabGroup.new(tag_type: :span).tap do |n|
  #     n.tab 'Test', url: '#test'
  #     n.tab 'Test2', url: '#test2', state: :active
  #     n.dropdown('Action') do |d|
  #       d.list content = nil, options = nil, html_options = nil, &block
  #     end
  #   end.render
  #
  # ==== Helper
  #
  #   ui_tab_group(options = { tap: true }, html_options = {}) do |n|
  #     n.tab(content, options = {}, html_options = {})
  #     n.tab(options = {}, html_options = {}) do
  #       content
  #     end
  #     n.dropdown(name, options = {}, html_options = {}) do |d|
  #       d.list(content, options = {}, html_options = {})
  #       d.list(options = {}, html_options = {}) do
  #         content
  #       end
  #     end
  #   end
  #
  class TabGroup < UiBibz::Ui::Core::Navigations::Nav

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
    end

    # Add nav link items
    # See UiBibz::Ui::Core::Navigations::NavLink
    def tab content = nil, options = {}, html_options = nil, &block
      block_given? ? content.merge!({ nav_type: type, tag_type: @options[:tag_type] }) : options.merge!({ nav_type: type, tag_type: @options[:tag_type] })
      @items << NavLink.new(content, options, html_options, &block)
    end

  private

    def component_html_classes
      ["nav", "nav-tabs", position, justify, fill]
    end

    def type
      "nav-tabs"
    end

  end
end


require 'ui_bibz/ui/core/navigations/components/breadcrumb_link'
module UiBibz::Ui::Core::Navigations

  # Breadcrumb
  #
  # Indicate the current page's location within a navigational hierarchy.
  # Separators are automatically added in CSS through +:before+ and +content+.
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  # You can use tap method to add link items.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +link_label+ - [Symbol | String] Display label of link with store
  # * +link_url+   - [String] Display url of link with store
  #
  # ==== Components
  #
  # +link+ is UiBibz::Ui::Core::Navigations::BreadCrumb::Components::BreadcrumbLink component
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Navigations::Breadcrumb.new().tap do |b|
  #     b.link content = nil, options = nil, html_options = nil, &block
  #     b.link content = nil, options = nil, html_options = nil, &block
  #     b.link content = nil, options = nil, html_options = nil, &block
  #     ...
  #   end
  #
  #   UiBibz::Ui::Core::Navigations::Breadcrumb.new(@store)
  #
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Navigations::Breadcrumb.new().tap do |b|
  #     b.link 'Home', url: '#home'
  #     b.link url: '#level-1' do
  #       'Level 1'
  #     end
  #     b.link 'Level 2', state: :active
  #   end.render
  #
  #   # or
  #
  #   @users = User.all
  #   UiBibz::Ui::Core::Navigations::Breadcrumb.new(@users).render
  #
  # ==== Helper
  #
  #   ui_breadcrumb({tap: true}) do |b|
  #     b.link(content, options = {}, html_options = {})
  #     b.link(options = {}, html_options = {}) do
  #       content
  #     end
  #   end
  #
  #   # or
  #
  #   @users = User.all
  #   ui_breadcrumb(@users, { link_label: name, link_url: user_path(:id) })
  #
  class Breadcrumb < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
      @links = []
      generate_links unless @content.nil?
    end

    # Render html tag
    def pre_render
      content_tag :nav, html_options do
        content_tag :ol, @links.join.html_safe, class: 'breadcrumb'
      end
    end

    # Add breadcrumb link items
    # See UiBibz::Ui::Core::BreadcrumbLink
    def link content = nil, options = nil, html_options = nil, &block
      @links << UiBibz::Ui::Core::Navigations::Components::BreadcrumbLink.new(content, options, html_options, &block).render
    end

    private

    def component_html_options
      super.merge({ "arial-label": "breadcrumb" })
    end

    def model_name
      content.class.to_s
    end

    def link_label
      @options[:link_label] || :name
    end

    def link_url
      @options[:link_url] || '#'
    end

    def generate_links
      content.each do |item|
        @links << UiBibz::Ui::Core::Navigations::Components::BreadcrumbLink.new(item.send(link_label), url: inject_url(link_url, item), current: item == last_item).render
      end
    end

    def last_item
      content.last
    end

  end
end


module UiBibz::Ui::Core::Lists::Components

  # Create a listBody
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  class ListBody < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
    end

    # Render html tag
    def pre_render
      output = [content_tag(:p, content, html_options)]
      output << content_tag(:small, options[:extra])
      output.join.html_safe
    end

  private

    def component_html_classes
      'mb-1'
    end

  end
end


#require 'will_paginate'
require "will_paginate-bootstrap4"
require "ui_bibz/ui/ux/tables/components/store"
module UiBibz::Ui::Ux::Tables

  # Create a pagination for table
  #
  # ==== Attributes
  #
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +store+ - Store generate by '+table_search_pagination+' method
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Ux::Tables::TablePagination.new(store: @store)
  #
  # ==== Helper
  #
  #    table_pagination(options = {})
  #
  class TablePagination < UiBibz::Ui::Base
    include WillPaginate::ActionView
    include ActionView
    #include WillPaginate::ActionView::BootstrapLinkRenderer

    # Initialize pagination with component item
    # pagination require WillPaginate gem
    def initialize options = nil, html_options = nil
      @pagination = UiBibz::Ui::Core::Component.new nil, options, html_options
    end

    # Render html tag with boostrap pagination theme
    def render
      paginate_parameters = { controller: store.controller }
      paginate_parameters = paginate_parameters.merge({ store_id: store.id }) unless store.id.nil?
      paginate_parameters = paginate_parameters.merge(store.parameters)
      paginate_parameters = paginate_parameters.merge({ link_type: 'pagination' })
      will_paginate(store.records, params: paginate_parameters.with_indifferent_access.reject{|k,v| default_parameters?(k) || v.blank? }, renderer: WillPaginate::ActionView::BootstrapLinkRenderer)
    end

  private

    def default_parameters?(k)
      %w(per_page page link_type).include?(k)
    end

    # Store must be generated by *table_search_pagination* method
    def store
      @store ||= if @pagination.options[:store].nil?
        raise 'Store is nil!'
      elsif @pagination.options[:store].try(:records).nil?
        raise 'Store can be created only with "table_search_pagination" method!'
      else
        Store.new @pagination.options.delete :store
      end
    end

  end
end


module UiBibz::Ui::Ux::Tables
  class Paginable < UiBibz::Ui::Base
    include WillPaginate::ActionView

    def initialize store, options, html_options = nil
      @store        = store
      @options      = options
      @html_options = html_options
    end

    # Render html tag
    def render
      content_tag :div, @html_options do
        concat UiBibz::Ui::Ux::Tables::TablePagination.new(store: @store, wrap_form: @options[:wrap_form]).render
        concat UiBibz::Ui::Ux::Tables::TablePaginationPerPage.new(store: @store, wrap_form: @options[:wrap_form]).render
        concat tag(:br, class: 'ui-bibz-clear')
      end
    end

    def paginable?
      @options[:paginable].nil? ? true : @options[:paginable]
    end

  private

    def store
      @store ||= if @search_field.options[:store].nil?
        raise 'Store is nil!'
      elsif @search_field.options[:store].try(:records).nil?
        raise 'Store can be created only with "table_search_pagination" method!'
      else
        Store.new @search_field.options.delete :store
      end
    end

  end
end


require 'ui_bibz/ui/core/forms/dropdowns/components/dropdown_header'
require 'ui_bibz/ui/core/forms/dropdowns/components/dropdown_divider'
require 'ui_bibz/ui/core/forms/dropdowns/components/dropdown_link'
module UiBibz::Ui::Core::Forms::Dropdowns

  # Create a dropdown
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  # You can use tap method to add list items.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +status+ - status of élement with symbol value:
  #   (+:primary+, +:secondary+, +:info+, +:warning+, +:danger+)
  # * +size+
  #   (+:xs+, +:sm+, +:lg+)
  # * +glyph+ - Add glyph with name or hash options
  #   * +name+ - String
  #   * +size+ - Integer
  #   * +type+ - Symbol
  # * +html_button+ - Hash
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Forms::Dropdowns::Dropdown.new(options = nil, html_options = nil).tap do |d|
  #     ...
  #     d.header content = nil, options = nil, html_options = nil, &block
  #     d.divider
  #     d.link content = nil, options = nil, html_options = nil, &block
  #     ...
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Forms::Dropdowns::Dropdown.new(name, status: :success).tap do |d|
  #     d.link 'test', { url: '#' }
  #     d.divider
  #     d.header 'Header 1'
  #     d.link 'test2', { url: '#' }
  #   end.render
  #
  # ==== Helper
  #
  #   dropdown(name, options = { tap: true }, html_options = {}) do |d|
  #     d.link(content, options = {}, html_options = {})
  #     d.link(options = {}, html_options = {}) do
  #       content
  #     end
  #     d.divider
  #     d.header(content, options = {}, html_options = {})
  #     d.header(options = {}, html_options = {}) do
  #       content
  #     end
  #   end
  #
  class Dropdown < UiBibz::Ui::Core::Component

    def initialize content, options = nil, html_options  = nil, &block
      super
      @items = []
      @status = @options.delete(:status)
    end

    # Render html tag
    def pre_render
      content_tag :div, html_options do
        concat button_html
        concat ul_html
      end
    end

    # Add dropdown header
    # See UiBibz::Ui::Core::Forms::Dropdowns::Components::DropdownHeader
    def header content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Core::Forms::Dropdowns::Components::DropdownHeader.new(content, options, html_options, &block).render
    end

    # Add dropdown Separator
    # See UiBibz::Ui::Core::Forms::Dropdowns::Components::DropdownDivider
    def divider
      @items << UiBibz::Ui::Core::Forms::Dropdowns::Components::DropdownDivider.new.render
    end

    # Add dropdown link in list
    # See UiBibz::Ui::Core::Forms::Dropdowns::Components::DropdownLink
    def link content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Core::Forms::Dropdowns::Components::DropdownLink.new(content, options, html_options, &block).render
    end

    # Add html component
    def html content = nil, &block
      if !block.nil?
        context  = eval("self", block.binding)
        @items << context.capture(&block)
      else
        @items << content
      end
    end

    def id
      @id ||= html_options[:id] || generate_id("dropdown")
    end

  protected

    def component_html_classes
      [type, open, inline, without_caret, keep_open]
    end

    def button_content
      glyph_and_content_html(options[:text].nil? ? @content : '').html_safe
    end

    def button_html
      html_button = options[:html_button] || {}
      if options[:tag] == :a
        content_tag dropdown_tag, button_content, { class: join_classes("btn", button_status, state, size, "dropdown-toggle"), href: '#', role: 'button', "data-toggle" => 'dropdown', "aria-haspopup" => true, "aria-expanded" => false, "id" => id }.merge(html_button)
      else
        content_tag dropdown_tag, button_content, { class: join_classes("btn", button_status, state, size, "dropdown-toggle"), type: 'button', "data-toggle" => 'dropdown', "aria-haspopup" => true, "aria-expanded" => false, "id" => id }.merge(html_button)
      end
    end

    def ul_html
      content_tag :div, @items.join.html_safe, class: join_classes("dropdown-menu", position, open, ), "arial-labelledby" => id
    end

    def dropdown_tag
      options[:tag] || :button
    end

    def position
      "dropdown-menu-#{ @options[:position] }" unless @options[:position].nil?
    end

    def type
      @options[:type] || 'dropdown'
    end

    def open
      "show" if @options[:open]
    end

    def keep_open
      "keep-open" if @options[:keep_open]
    end

    def inline
      "btn-group" if @options[:inline]
    end

    def without_caret
      "without-caret" if @options[:caret] == false
    end

    def outline
      "outline" if @options[:outline]
    end

    def button_status
      ["btn", outline, @status || :secondary].compact.join('-')
    end

    # :lg, :sm or :xs
    def size
      "btn-#{ @options[:size] }" if @options[:size]
    end

  end
end


module UiBibz::Ui::Core::Boxes

  # Create a jumbotron
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +full_with+ - Boolean to render jumbotron in fullscreen
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Boxes::Jumbotron.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Boxes::Jumbotron.new(options = nil, html_options = nil) do
  #     content
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Boxes::Jumbotron.new(content, full_width: true).render
  #
  #   UiBibz::Ui::Core::Boxes::Jumbotron.new() do
  #     #content
  #   end.render
  #
  # ==== Helper
  #
  #   jumbotron(content, options = {} , html_options = {})
  #
  #   jumbotron(options = {}, html_options = {}) do
  #     content
  #   end
  #
  class Jumbotron < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Boxes::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
    end

    # Render html tag
    def pre_render
      content_tag :div, html_options do
        if fluid
          UiBibz::Ui::Core::Layouts::Container.new(content).render
        else
          content
        end
      end
    end

  private

    def component_html_classes
      ['jumbotron', fluid]
    end

    def fluid
      "jumbotron-fluid" unless options[:fluid].nil?
    end

  end
end


require 'ui_bibz/ui/extensions/core/forms/surround_extension'
module UiBibz::Ui::Core::Forms::Selects

  # Create a SelectField
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  # source : http://silviomoreto.github.io/bootstrap-select/examples/
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +status+ - status of élement with symbol value:
  #   (+:primary+, +:secondary+, +:info+, +:warning+, +:danger+, +:link+)
  # * +option_tags+ - Array, Object [required]
  # * +searchable+ - Boolean
  # * +max_options+ - Integer
  # * +selected_text_format+ - String
  # * +menu_size+ - Integer
  # * +header+ - String
  # * +actions_box+ - Boolean
  # * +show_tick+ - Boolean
  # * +show_menu_arrow+ - Boolean
  # * +dropup+ - Boolean
  # * +append+ - String, Html
  # * +prepend+ - String, Html
  # * +connect+ - Hash
  #   * +event+ - String
  #   * +mode+ - String
  #   * +target+ - Hash
  #     * +selector+ - String
  #     * +data+ - Array
  #     * +url+ - String
  # * +refresh+ - Hash
  #   * +event+ - String
  #   * +mode+ - String
  #   * +target+ - Hash
  #     * +selector+ - String
  #     * +data+ - Array
  #     * +url+ - String
  #
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Forms::Selects::SelectField.new(content, options = {}, html_options = {}).render
  #
  #   UiBibz::Ui::Core::Forms::Selects::SelectField.new(options = {}, html_options = {}) do
  #     content
  #   end.render
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Forms::Selects::SelectField.new('fruits', { option_tags: list_of_fruits, searchable: true }, { class: 'test' })
  #
  #   UiBibz::Ui::Core::Forms::Selects::SelectField.new({ option_tags: list_of_fruits, actions_box: true }, { class: 'test' }) do
  #     'fruits'
  #   end
  #
  # ==== Helper
  #
  #   select_field(content, options = {}, html_options = {})
  #
  class AbstractSelect < UiBibz::Ui::Core::Component
    include SurroundExtension

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
    end

    # Render html tag
    def pre_render
      surround_field select_field_html_tag
    end

    private

    def select_field_html_tag
      select_tag content, options[:option_tags], html_options
    end

    # Try to put it on a line
    def component_html_options
      super.merge({
        multiple:      options[:multiple],
        disabled:      options[:state] == :disabled,
        include_blank: options[:include_blank],
        prompt:        options[:prompt]
      })
    end

  end
end


require 'ui_bibz/ui/core/navigations/components/nav_link'
require 'ui_bibz/ui/core/navigations/components/nav_text'
require 'ui_bibz/ui/core/navigations/components/nav_dropdown'
module UiBibz::Ui::Core::Navigations

  # Create a nav
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +type+ - Symbol
  #   (+:pills+, +:tab+)
  # * +position+ - Symbol
  #   (+:left+, +:right+, +:center+)
  # * +stacked+ - Boolean
  # * +tag+ - Symbol
  #   (+:a+, +:li)
  # * +justify+ - Boolean
  # * +fill+ - Boolean
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Navigations::Nav.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Navigations::Nav.new(options = nil, html_options = nil).tap do |n|
  #     ...
  #     n.link content = nil, options = nil, html_options = nil, block
  #     n.link content = nil, options = nil, html_options = nil, block
  #     n.dropdown content = nil, options = nil, html_options = nil, block
  #     ...
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Navigations::Nav.new(type: :pills).tap do |n|
  #     n.link 'Test', url: '#test'
  #     n.link 'Test2', url: '#test2', state: :active
  #     n.dropdown('Action') do |d|
  #       d.list content = nil, options = nil, html_options = nil, &block
  #     end
  #   end.render
  #
  # ==== Helper
  #
  #   nav(options = { tap: true }, html_options = {}) do |n|
  #     n.link(content, options = {}, html_options = {})
  #     n.link(options = {}, html_options = {}) do
  #       content
  #     end
  #     n.dropdown(name, options = {}, html_options = {}) do |d|
  #       d.list(content, options = {}, html_options = {})
  #       d.list(options = {}, html_options = {}) do
  #         content
  #       end
  #     end
  #   end
  #
  class Nav < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
      @items = []
    end

    # Render html tag
    def pre_render
      content_tag tag, @items.map(&:render).join.html_safe, html_options
    end

    # Add nav link items
    # See UiBibz::Ui::Core::Navigations::NavLink
    def link content = nil, options = {}, html_options = nil, &block
      block_given? ? content.merge!({ nav_type: type }) : options.merge!({ nav_type: type })
      @items << NavLink.new(content, options, html_options, &block)
    end

    def text content = nil, options = {}, html_options = nil, &block
      block_given? ? content.merge!({ nav_type: type }) : options.merge!({ nav_type: type })
      @items << NavText.new(content, options, html_options, &block)
    end

    # Add nav in nav
    def nav content = nil, options = {}, html_options = nil, &block
      @items << UiBibz::Ui::Core::Component.new(Nav.new(content, options).tap(&block).render, {}, html_options)
    end

    # Add nav dropdown items
    # See UiBibz::Ui::Core::Navigations::NavDropdown
    def dropdown content = nil, options = {}, html_options = nil, &block
      @items << NavDropdown.new(content, options, html_options).tap(&block)
    end

    def spacer num = "auto"
      kls = " mr-#{ num }"
      @items.last.html_options[:class].nil? ? @items.last.html_options[:class] = kls : @items.last.html_options[:class] << kls
    end

  protected

    def component_html_classes
      ["nav", type, position, stacked, justify, fill]
    end

    # tabs or pills
    def type
      "nav-#{ @options[:type] }" unless @options[:type].nil?
    end

    def position
      case @options[:position]
      when :center
        "justify-content-center"
      when :right
        "justify-content-end"
      else
        nil
      end
    end

    def justify
      if @options[:justify]
        if type == "nav-links"
          "nav-justified"
        else
          "nav-fill"
        end
      end
    end

    def fill
      "nav-fill" if @options[:fill]
    end

    def stacked
      "flex-column" if @options[:stacked]
    end

    def tag
      type == "nav-links" ? :nav : :ul
    end

  end
end


require 'ui_bibz/ui/core/navigations/components/pagination_link'
module UiBibz::Ui::Core::Navigations

  # Create a Pagination
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +position+ - Symbol
  #   (+:left+, +:right+, +:center+)
  # * +size+
  #   (+:sm+, +:lg+)
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Navigations::Pagination.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Navigations::Pagination.new(options = nil, html_options = nil).tap do |n|
  #     ...
  #     n.link content = nil, options = nil, html_options = nil, block
  #     n.link content = nil, options = nil, html_options = nil, block
  #     n.dropdown content = nil, options = nil, html_options = nil, block
  #     ...
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Navigations::Pagination.new(type: :pills).tap do |n|
  #     n.link 'Test', url: '#test'
  #     n.link 'Test2', url: '#test2', state: :active
  #     n.dropdown('Action') do |d|
  #       d.list content = nil, options = nil, html_options = nil, &block
  #     end
  #   end.render
  #
  # ==== Helper
  #
  #   ui_pagination(options = { tap: true }, html_options = {}) do |n|
  #     n.link(content, options = {}, html_options = {})
  #     n.link(options = {}, html_options = {}) do
  #       content
  #     end
  #   end
  #
  class Pagination < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
      @items = []
    end

    # Render html tag
    def pre_render
      content_tag :nav do
        content_tag :ul, @items.join.html_safe, html_options
      end
    end

    # Add nav link items
    # See UiBibz::Ui::Core::Navigations::NavLink
    def link content = nil, options = {}, html_options = nil, &block
      @items << PaginationLink.new(content, options, html_options, &block).render
    end

  private

    def component_html_classes
      ["pagination", position, size]
    end

    def position
      case @options[:position]
      when :center
        "justify-content-center"
      when :right
        "justify-content-end"
      else
        nil
      end
    end

    def size
      "pagination-#{ @options[:size] }" if @options[:size]
    end

  end
end


require 'ui_bibz/ui/core/notifications/components/alert_header'
require 'ui_bibz/ui/core/notifications/components/alert_body'
module UiBibz::Ui::Core::Notifications

  # Create an alert
  #
  # This element is an extend of UiBibz::Ui::Core::Component.
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +status+ - status of élement with symbol value:
  #   (+:default+, +:primary+, +:info+, +:warning+, +:danger+)
  # * +glyph+ - [String | Hash] Add glyph with name or hash options
  #   * +name+ - [String]
  #   *+timeout+ - [Integer]
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Alert.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Alert.new(options = nil, html_options = nil) do
  #     content
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Alert.new(content, { status: :success, glyph: 'eye' },{ class: 'test' }).render
  #
  #   UiBibz::Ui::Core::Alert.new({glyph: { name: 'eye', size: 3}, { class: 'test' }) do
  #     content
  #   end.render
  #
  # ==== Helper
  #
  #   notify(content, options = {}, html_options = {})
  #
  #   notify(options = {}, html_options = {}) do
  #     content
  #   end
  #
  class Alert < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
      header(@content) if @options[:tap].nil?
    end

    # Render html tag
    def pre_render
      content_tag :div, html_options do
        concat @header
        concat @body unless @body.nil?
      end
    end

    # Add Header which is a component
    def header content = nil, options = nil, html_options = nil, &block
      if block.nil?
        options = @options.merge(options || {})
      else
        content = @options.merge(content || {})
      end

      @header = UiBibz::Ui::Core::Notifications::Components::AlertHeader.new(content, options, html_options, &block).render
    end

    # Add Body which is a component
    def body content = nil, options = nil, html_options = nil, &block
      @body = UiBibz::Ui::Core::Notifications::Components::AlertBody.new(content, options, html_options, &block).render
    end

  private

    def component_html_data
      super
      add_html_data "timeout", options[:timeout] if options[:timeout]
    end

    def component_html_classes
      ['alert', 'alert-dismissible']
    end

    def component_html_options
      { role: 'alert'}
    end

    def status
      "alert-#{ options[:status] || :info  }"
    end

  end
end


require 'ui_bibz/ui/core/boxes/components/body/card_body_text'
require 'ui_bibz/ui/core/boxes/components/body/card_body_title'
require 'ui_bibz/ui/core/boxes/components/body/card_body_link'
module UiBibz::Ui::Core::Boxes::Components

  # Create a card body
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Boxes::CardBody.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Boxes::CardBody.new(options = nil, html_options = nil) do
  #     content
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Boxes::CardBody.new.render
  #
  #   UiBibz::Ui::Core::Boxes::CardBody.new do
  #     'Exemple'
  #   end.render
  #
  class CardBody < UiBibz::Ui::Core::Component

    # See UiBibz::Ui::Core::Component.initialize
    def initialize content = nil, options = nil, html_options = nil, &block
      super
      @items = [@content]
    end

    # Render html tag
    def pre_render
      if options[:collapse]
        content_tag :div, class: join_classes("collapse", show), id: options[:collapse], "data-parent": "##{ options[:parent_collapse] }" do
          content_tag :div, @items.join.html_safe, html_options
        end
      else
        content_tag :div, @items.join.html_safe, html_options
      end
    end

    def title content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Core::Boxes::Components::Body::CardBodyTitle.new(content, options, html_options, &block).render
    end

    def link content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Core::Boxes::Components::Body::CardBodyLink.new(content, options, html_options, &block).render
    end

    def text content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Core::Boxes::Components::Body::CardBodyText.new(content, options, html_options, &block).render
    end

  private

    def component_html_classes
      ["card-body", outline]
    end

    def outline
      "text-#{ @options[:status] }" if @options[:outline]
    end

    def show
      "show" if @options[:show]
    end

  end
end


require 'haml'
require 'ui_bibz/ui/extensions/core/component/klass_extension'
require 'ui_bibz/ui/extensions/core/component/glyph_extension'
require 'ui_bibz/ui/extensions/core/component/popover_extension'
module UiBibz::Ui::Core

  # Creates a component of the given +name+ using options created by the set of +options+.
  #
  # ==== Attributes
  #
  # * +content+ -   Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +status+ - status of élement with symbol value:
  #   (+:default+, +:primary+, +:info+, +:warning+, +:danger+)
  # * +glyph+ - Add glyph with name or hash options
  #   * +name+ - String
  #   * +size+ - Integer
  #   * +type+ - Symbol
  #
  # ==== Signatures
  #
  #   UiBibz::Ui::Core::Component.new(content, options = nil, html_options = nil)
  #
  #   UiBibz::Ui::Core::Component.new(options = nil, html_options = nil) do
  #     content
  #   end
  #
  # ==== Examples
  #
  #   UiBibz::Ui::Core::Component.new(content, { type: :success, glyph: 'eye' }, { class: 'test' })
  #   # or
  #   UiBibz::Ui::Core::Component.new({glyph: { name: 'eye', size: 3}, { class: 'test' }) do
  #     content
  #   end
  #
  class Component < UiBibz::Ui::Base
    include KlassExtension
    include GlyphExtension
    include PopoverExtension

    # Constants
    STATUSES = %i(primary secondary success danger warning info light dark)
    SIZES    = %i(lg md sm)

    attr_accessor :content, :html_options, :options

    # Use link_to system in rails
    # * Content can be send by content variable or by block
    #   if a block is sent, variable 'content' does not exit.
    # * Options of component is defined in hash options
    # * Html options is defined in hash html_options
    def initialize content = nil, options = nil, html_options = nil, &block
      if !block.nil?
        @html_options, @options = options, content
        read_cache = Rails.cache.read(@options.try(:[], :cache))
        if read_cache.nil?
          context  = eval("self", block.binding)
          @content = context.capture(&block)
        else
          @content = read_cache
        end
      else
        if content.kind_of?(Hash)
          @html_options, @options = options, content
        else
          @html_options, @options, @content = html_options, options, content
        end
      end
      @html_options = @html_options || {}
      @options      = @options || {}
      init_options
      init_component_html_options
    end

    # Render html tag with or without cache
    def render
      render_with_or_without_cache
    end

    # Render without cache
    def pre_render
      glyph_and_content_html
    end

    # Know if component is tapped or not
    def is_tap content, options
      (content[:tap] if content.kind_of?(Hash)) || (options[:tap] unless options.nil?)
    end

  protected

    # Override this method to add html classes
    # Accept Array or String
    def component_html_classes
      []
    end

    # Override this method to add html data
    def component_html_data
      # To stimulusjs
      data_target = html_options.try(:[], :data).try(:[], :target) || options.try(:delete, :target)
      add_html_data(:target, data_target) unless data_target.nil?

      data_controller = html_options.try(:[], :data).try(:[], :controller) || options.try(:delete, :controller)
      add_html_data(:controller, data_controller) unless data_controller.nil?

      data_action = html_options.try(:[], :data).try(:[], :action) || options.try(:delete, :action)
      add_html_data(:action, data_action) unless data_action.nil?

      # To turbolinks
      data_turbolinks = html_options.try(:[], :data).try(:[], :turbolinks) || options.try(:delete, :turbolinks)
      add_html_data(:turbolinks, data_turbolinks) unless data_turbolinks.nil?
    end

    # Override this method to add html Options
    # Accept Hash
    def component_html_options
      {}
    end

    # Override this method to add Options to the component
    # Accept Hash
    def component_options
      {}
    end

    # Override this method to add html classes to wrapper
    def component_wrapper_html_classes
      []
    end

    # Add html data arguments
    def add_html_data name, value = true
      html_options[:data] = {} if html_options[:data].nil?
      value = value.kind_of?(String) ? value.strip : value
      html_options[:data].update(Hash[name, value])
    end

    def is_disabled?
      options[:state] == :disabled || html_options[:disabled]
    end

  private

    def render_with_or_without_cache
      if options[:cache]
        cache      = Rails.cache
        read_cache = cache.read(options[:cache])
        if read_cache
          read_cache
        else
          cache.write(options[:cache], pre_render)
          pre_render
        end
      else
        pre_render
      end
    end

    def init_options
      @options = component_options.merge(@options).with_indifferent_access
    end

    def initialize_component_html_data
      component_html_data
      popover_data_html
      tooltip_data_html
    end

    def initialize_component_html_options
      html_options.merge!(component_html_options)
    end

    def init_component_html_options
      initialize_component_html_data
      initialize_component_html_classes
      initialize_component_html_options
    end

  end
end


require 'ui_bibz/ui/ux/containers/components/panel_header'
require 'ui_bibz/ui/ux/containers/components/panel_body'
require 'ui_bibz/ui/ux/containers/components/panel_footer'
require 'ui_bibz/ui/ux/containers/components/panel_toolbar'
require 'ui_bibz/ui/ux/containers/components/panel_deck'
require 'ui_bibz/ui/ux/containers/components/panel_group'
require 'ui_bibz/ui/ux/containers/components/panel_column'
module UiBibz::Ui::Ux::Containers

  # Create a Panel
  #
  # This element is an extend of UiBibz::Ui::Core::Boxes::Card
  #
  # ==== Attributes
  #
  # * +content+ - Content of element
  # * +options+ - Options of element
  # * +html_options+ - Html Options of element
  #
  # ==== Options
  #
  # You can add HTML attributes using the +html_options+.
  # You can pass arguments in options attribute:
  # * +store+ - Store generate by '+table_search_pagination+' method
  #
  # ==== Signatures
  #
  #
  #   UiBibz::Ui::Ux::Containers::Panel.new do |p|
  #     p.header 'header'
  #     p.toolbar do |t|
  #       t.btn_group do |bg|
  #         bg.button 'button'
  #       end
  #     end
  #     p.body 'body'
  #     p.footer 'footer'
  #   end
  #
  class Panel < UiBibz::Ui::Core::Component

    def initialize content = nil, options = nil, html_options = nil, &block
      super
      @items = []
    end

    def pre_render
      content_tag :div, html_structure, html_options
    end

    def toolbar content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Ux::Containers::Components::PanelToolbar.new(content, options, html_options).tap(&block).render
    end

    def deck content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Ux::Containers::Components::PanelDeck.new(content, options, html_options).tap(&block).render
    end

    def column content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Ux::Containers::Components::PanelColumn.new(content, options, html_options).tap(&block).render
    end

    def group content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Ux::Containers::Components::PanelGroup.new(content, options, html_options).tap(&block).render
    end

    def panel content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Ux::Containers::Panel.new(content, options, html_options).tap(&block).render
    end

    def body content = nil, options = nil, html_options = nil, &block
      @items << UiBibz::Ui::Ux::Containers::Components::PanelBody.new(content, options, html_options, &block).render
    end

    # Add Header which is a component
    def header content = nil, options = nil, html_options = nil, &block
      if is_tap(content, options)
        @header = UiBibz::Ui::Ux::Containers::Components::PanelHeader.new(content, options, html_options).tap(&block).render
      else
        @header = UiBibz::Ui::Ux::Containers::Components::PanelHeader.new(content, options, html_options, &block).render
      end
    end

    # Add Header which is a component
    def footer content = nil, options = nil, html_options = nil, &block
      if is_tap(content, options)
        @footer = UiBibz::Ui::Ux::Containers::Components::PanelFooter.new(content, options, html_options).tap(&block).render
      else
        @footer = UiBibz::Ui::Ux::Containers::Components::PanelFooter.new(content, options, html_options, &block).render
      end
    end

    private

    def html_structure
      [@header, @items.join, @footer].compact.join.html_safe
    end

    def component_html_classes
      super << 'panel'
    end

  end
end


module UiBibz::Ui::Ux::Tables
  class Sortable < UiBibz::Ui::Base

    def initialize store, options
      @store   = store
      @options = options
    end

    # header use i18n
    def header column, name = nil
      @column  = column
      defaults = [translate_headers_by_defaults, translate_headers_by_defaults_active_record, translate_headers_by_active_record, header_name(name)]
      @name    = UiBibz::Utils::Internationalization.new(translate_headers_by_model, default: defaults).translate
      sortable? ? sortable_link : title
    end

  private

    def sortable_link
      link_to title.html_safe, url_for(url_options_o), { class: cls }
    end

    def header_name name
      name = name || @column.name
      name || @column.data_index.to_s.try('titleize')
    end

    def translate_headers_by_model
      "ui_bibz.grid.headers.#{ @store.model.to_s.underscore }.#{ @column.data_index }"
    end

    def translate_headers_by_defaults
      "ui_bibz.grid.headers.defaults.#{ @column.data_index }"
    end

    def translate_headers_by_defaults_active_record
      "activerecord.attributes.defaults.#{ @column.data_index }"
    end

    def translate_headers_by_active_record
      "activerecord.attributes.#{ @store.model.to_s.underscore }.#{ @column.data_index }"
    end

    def url_options_o
      args =  {
        controller:  @store.controller,
        action:      @store.action,
        search:      @store.search,
        sort:        sort_name,
        column_id:   @column.id,
        direction:   direction,
        only_path:   true
      }
      args = args.merge({ id: @store.param_id }) if @store.param_id
      args = args.merge({ custom_sort: true, column_name: @column.data_index }) if @column.custom_sort
      args = args.merge({ parent: true }) if @column.parent
      args = args.merge({ store_id: @store.id }) unless @store.id.nil?
      args = args.merge({ link_type: 'column' })
      args = args.merge(@store.parameters.reject{ |k,v| default_parameters.include?(k.to_s) })
      args.with_indifferent_access
    end

    def default_parameters
      %w(column_id direction search store_id controller sort page per_page)
    end

    def sortable?
      @options[:sortable].nil? ? true : @options[:sortable]
    end

    def title
      sortable? ? name_with_caret : @name
    end

    def name_with_caret
      sort_name.to_s == sort_column.to_s && @column.id.to_s == @store.column_id.to_s ? @name + caret : @name
    end

    def caret
      UiBibz::Ui::Core::Icons::Glyph.new("caret-#{ direction == 'desc' ? 'up' : 'down' }").render
    end

    def cls
      sort_direction == 'asc' ? 'dropup' : nil
    end

    def direction
      sort_name == sort_column && sort_direction == "asc" ? "desc" : "asc"
    end

    def column_name
      @column.data_index || @column
    end

    def sort_column_name
      @column.sort.nil? ? "#{ @store.model.to_s.underscore.pluralize }.#{ @column.data_index}" : @column.sort
    end

    def sort_name
      @sort_name || sort_column_name
    end

    def sort_column
      @store.sort
    end

    def sort_direction
      %w[asc desc].include?(@store.direction) ? @store.direction : "asc"
    end

  end
end