module Ecoportal module API class V2 class Page class Component class SelectionField < Page::Component passboolean :multiple, :flat, :other passthrough :other_desc passthrough :data_type embeds_many :options, klass: "Ecoportal::API::V2::Page::Component::SelectionOption", order_key: :weight def numeric? self.data_type == "num" end def text? self.data_type == "txt" end def numeric!(&block) ordered_options.each {|opt| opt.numeric!(&block)} self.data_type = "num" end def text!(&block) ordered_options.each {|opt| opt.text!(&block)} self.data_type = "str" end def switch_type!(&block) numeric? ? text!(&block) : numeric!(&block) end def select(value_name, by_name: false) opt = find_option(value_name, by_name: by_name) sel = selected return true if !multiple && opt == sel sel.selected = false if !multiple && sel opt.selected = true unless !opt end def deselect(value_name, by_name: false) if opt = find_option(value_name, by_name: by_name) opt.selected = false end end def selected(by_name: false, by_value: false, value: false, name: false) case when by_value elems = [selected].flatten.compact options_hash(elems) do |option| name ? option.name : option end when by_name elems = [selected].flatten.compact options_hash(elems, by_name: true) do |option| value ? option.value : option end else if multiple ordered_options.select {|opt| opt.selected} else options.find {|opt| opt.selected} end end end def value if multiple selected.map {|opt| opt.value} else selected&.value end end def values [value].flatten.compact end def name if multiple selected.map {|opt| opt.name} else selected&.name end end def names [name].flatten.compact end def add_option(value:, name: nil, pos: NOT_USED, before: NOT_USED, after: NOT_USED) opt_doc = options.items_class.new_doc options.upsert!(opt_doc, pos: pos, before: before, after: after) do |option| option.name = name option.value = value if prev = previous_option(option) option.weight = prev.weight end yield(option) if block_given? fix_option_weights! end end def ordered_options options.sort_by.with_index do |option, index| [option.weight, index] end end # @param name [Boolean] whether or not the values of the `Hash` should be the `names` # @return [Hash] of **key** _value_ and **value**: # 1. _option_ if `name` is `false` # 2. _name_ if `name` is `true` def options_by_value(name: false) options_hash do |option| name ? option.name : option end end # @param value [Boolean] whether or not the values of the `Hash` should be the `values` # @return [Hash] of **key** _name_ and **value**: # 1. _option_ if `value` is `false` # 2. _value_ if `value` is `true` def options_by_name(value: false) options_hash(by_name: true) do |option| value ? option.value : option end end def to_s(name: true, delimiter: "\n") [selected].flatten.compact.map do |opt| name ? opt.name : opt.value end.join(delimiter) end # Quick config helper # @param conf [Symbol, Array] # - `:flat` to display in flat mode # - `:multiple` to allow multiple selection # - `:single` to set to singular selection # - `:other` to enable `other` button # - `:options` to add options (`Hash`) or update option names # - `:type` to define the type # - `:num` # - `:str` def configure(*conf) conf.each_with_object([]) do |cnf, unused| case cnf when :flat self.flat = true when :multiple self.multiple = true when :single self.multiple = false when :other self.other = true when Hash supported = [:flat, :options, :type] unless (rest = hash_except(cnf.dup, *supported)).empty? unused.push(rest) end if cnf.key?(:flat) then self.flat = cnf[:flat] end if cnf.key?(:options) if opts = cnf[:options] configure_options opts end end if cnf.key?(:type) if cnf[:type] == :str self.text! elsif cnf[:type] == :num self.numeric! else # Unknown type end end else unused.push(cnf) end end.yield_self do |unused| super(*unused) end end private def configure_options(opts) hopts = self.options_by_value opts.each do |val, nm| if option = hopts[val] option.name = nm else add_option(value: val, name: nm) end end end def fix_option_weights! ordered_options.each_with_index do |option, index| option.weight = index end end def previous_option(value) opts = ordered_options pos = opts.index(value) - 1 return if pos < 0 opts[pos] end def find_option(value_name, by_name: false) if by_name opt = options.find {|opt| same_string?(opt.name, value_name)} else opt = options.find {|opt| opt.value == value_name} end end def options_hash(elems = ordered_options, by_name: false) elems.each_with_object({}) do |option, hash| key = by_name ? option.name : option.value hash[key] = block_given?? yield(option) : option end end end end end end end end require 'ecoportal/api/v2/page/component/selection_option'