styd/apexcharts.rb

View on GitHub
lib/apex_charts/options_builder.rb

Summary

Maintainability
C
1 day
Test Coverage
# frozen_string_literal: true

module ApexCharts
  class OptionsBuilder
    include ApexCharts::Utils::Hash
    include ApexCharts::Utils::DateTime

    attr_accessor :built

    def initialize(sample, options)
      @options = camelize_keys(options)
      if options[:plotOptions]&.[](:bar)&.[](:horizontal)
        @ytype = type(sample)
      else
        @xtype = type(sample)
      end
      @built = {}
    end

    def build_options
      build_chart
      build_div
      build_general_options
    end

    def build_general_options
      build_annotations
      build_colors
      build_data_labels
      build_defer
      build_fill
      build_grid
      build_labels
      build_legend
      build_markers
      build_module
      build_no_data
      build_plot_options
      build_responsive
      build_states
      build_stroke
      build_subtitle
      build_theme
      build_title
      build_tooltip
      build_xaxis
      build_yaxis
      built.compact
    end

    def build_div
      built[:div] = {
        id: @options.delete(:id),
        var: @options.delete(:var),
        class: @options.delete(:class),
        style: @options.delete(:style)
      }.compact
    end

    def build_annotations
      annotations = @options.delete :annotations
      built[:annotations] = (
        annotations.compact if annotations.is_a? Hash
      )
    end

    def build_chart
      built[:chart] =
        if target = @options.delete(:brushTarget)
          {
            brush: {enabled: true, target: target.to_s},
            selection: {enabled: true}
          }
        else
          {}
        end

      built[:chart].merge!({
        id: @options[:chartId] || @options[:id],
        group: @options.delete(:group),
        height: @options.delete(:height) { target ? 180 : 400 },
        width: @options.delete(:width),
        stacked: @options.delete(:stacked),
        animations: enabled(@options.delete(:animations)),
        sparkline: enabled(@options.delete(:sparkline)),
        background: @options.delete(:background),
        foreColor: @options.delete(:foreColor)
      }.compact)

      chart = @options.delete :chart

      return unless chart.is_a? Hash

      built[:chart].merge! chart.compact
    end

    def build_colors
      colors = @options.delete :colors
      colors &&= Array(colors)
      built[:colors] = colors
    end

    def build_data_labels
      data_labels = @options.delete :dataLabels
      return if data_labels.nil?

      built[:dataLabels] = if [true, false].include? data_labels
                             {enabled: data_labels}
                           elsif data_labels.is_a? Hash
                             data_labels.compact
                           end
    end

    def build_defer
      defer = @options.delete :defer
      built[:defer] = defer == true
    end

    def build_module
      omodule = @options.delete :module
      built[:module] = omodule == true
    end

    def build_fill
      fill = @options.delete :fill
      built[:fill] = case fill
                     when String
                       {type: fill}
                     when Hash
                       fill.compact
                     end
    end

    def build_grid
      grid = @options.delete :grid
      built[:grid] = if [true, false].include? grid
                       {show: grid}
                     elsif grid.is_a? Hash
                       grid.compact
                     end
    end

    def build_labels
      labels = @options.delete :labels
      labels &&= Array(labels)
      built[:labels] = labels
    end

    def build_legend
      legend = @options.delete :legend
      built[:legend] = if [true, false].include? legend
                         {show: legend}
                       elsif legend.is_a? String
                         {show: true, position: legend}
                       elsif legend.is_a? Hash
                         legend.compact
                       end
    end

    def build_markers
      markers = @options.delete :markers
      built[:markers] = case markers
                        when String
                          {shape: markers}
                        when Hash
                          markers.compact
                        end
    end

    def build_no_data
      no_data = @options.delete :noData
      built[:noData] = case no_data
                       when String
                         {text: no_data}
                       when Hash
                         no_data.compact
                       end
    end

    def build_plot_options
      plot_options = @options.delete :plotOptions
      return unless plot_options.is_a? Hash

      built[:plotOptions] =
        plot_options.compact
    end

    def build_responsive
      responsive = @options.delete :responsive
      responsive &&= responsive.is_a?(Hash) ? [responsive] : Array(responsive)
      built[:responsive] = responsive
    end

    def build_states
      built[:states] = {
        normal: filter_type_hash(@options.delete(:normal)),
        hover: filter_type_hash(@options.delete(:hover)),
        active: filter_type_hash(@options.delete(:active))
      }.compact

      states = @options.delete :states
      built[:states].merge! states.compact if states.is_a? Hash

      built[:states] = nil if built[:states].empty?
    end

    def build_stroke
      curve = @options.delete :curve
      built[:stroke] = {curve: curve}.compact

      stroke = @options.delete :stroke
      if [true, false].include? stroke
        built[:stroke].merge!(show: stroke)
      elsif stroke.is_a? Hash
        built[:stroke].merge! stroke.compact
      end

      built[:stroke] = nil if built[:stroke].empty?
    end

    def build_subtitle
      subtitle = @options.delete(:subtitle)
      built[:subtitle] = case subtitle
                         when String
                           {text: subtitle}
                         when Hash
                           subtitle.compact
                         end
    end

    def build_theme
      theme = @options.delete(:theme)
      built[:theme] = case theme
                      when String
                        case theme
                        when 'random'
                          resolve_theme(Theme.all_palettes.sample)
                        when 'monochrome'
                          {monochrome: {enabled: true}}
                        else
                          resolve_theme(theme)
                        end
                      when Hash
                        theme.compact
                      end
    end

    def build_title
      title = @options.delete(:title)
      built[:title] = case title
                      when String
                        {text: title}
                      when Hash
                        title.compact
                      end
    end

    def build_tooltip
      tooltip = @options.delete :tooltip
      built[:tooltip] = if [true, false].include? tooltip
                          {enabled: tooltip}
                        elsif tooltip.is_a? Hash
                          tooltip.compact
                        end
    end

    def build_xaxis
      xaxis = @options.delete :xaxis
      built[:xaxis] = {
        type: @options.delete(:xtype) { @xtype },
        title: {
          text: @options.delete(:xtitle)
        }.compact
      }.compact
      built[:xaxis].delete(:title) if built[:xaxis][:title].empty?

      case xaxis
      when String
        built[:xaxis][:title] = {text: xaxis}
      when Hash
        built[:xaxis].merge! xaxis
      end

      built[:xaxis] = nil if built[:xaxis].empty?
    end

    def build_yaxis
      yaxis = @options.delete :yaxis

      case yaxis
      when Array
        built[:yaxis] = yaxis
      when Hash
        built[:yaxis] = {
          type: @options.delete(:ytype) { @ytype },
          title: {
            text: @options.delete(:ytitle)
          }.compact
        }.compact
        built[:yaxis].delete(:title) if built[:yaxis][:title].empty?
        built[:yaxis].merge! yaxis
      when String
        built[:yaxis] = {
          type: @options.delete(:ytype) { @ytype },
          title: {
            text: yaxis
          }
        }.compact
      when NilClass
        built[:yaxis] = if ytitle = @options.delete(:ytitle)
                          {title: {text: ytitle}}
                        else
                          {}
                        end
      end

      built[:yaxis] = nil if built[:yaxis].all?(&:empty?)
    end

  private

    def enabled(options)
      boolean_to_hash(options) do |opts|
        {enabled: opts}
      end
    end

    def boolean_to_hash(options)
      return if options.nil?

      if [true, false].include? options
        yield(options)
      elsif options.is_a?(Hash)
        options.compact
      end
    end

    def filter_type_hash(state)
      case state
      when String
        {filter: {type: state}}
      when Hash
        state.compact
      end
    end

    def resolve_theme(theme)
      if Theme::PALETTES.include? theme
        {palette: theme}
      elsif Theme.palette_names.include? theme
        built[:colors] = Theme.get_colors(theme)
        nil
      end
    end

    def options_class(name)
      schema = ApexCharts.config.schema
      ApexCharts.const_get "Options::#{schema}::#{name}Options"
    end
  end
end