mirror of
https://github.com/we-promise/sure.git
synced 2026-04-11 16:24:51 +00:00
* feat(select): improve merchant dropdown behavior and placement controls - add configurable menu_placement strategy to DS::Select (auto/down/up) with safe normalization forward menu_placement through StyledFormBuilder#collection_select - force Merchant dropdown to open downward in transaction create and editor forms - fix select option/search text contrast by applying text-primary in DS select menu - prevent form jump on open by scrolling only inside dropdown content instead of using scrollIntoView - clamp internal dropdown scroll to valid bounds for stability - refactor select controller placement logic for readability (placementMode, clamp) without changing behavior * set menu_placement=auto for metchant selector
91 lines
2.6 KiB
Ruby
91 lines
2.6 KiB
Ruby
module DS
|
|
class Select < ViewComponent::Base
|
|
attr_reader :form, :method, :items, :selected_value, :placeholder, :variant, :searchable, :menu_placement, :options
|
|
|
|
VARIANTS = %i[simple logo badge].freeze
|
|
MENU_PLACEMENTS = %w[auto down up].freeze
|
|
HEX_COLOR_REGEX = /\A#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?\z/
|
|
RGB_COLOR_REGEX = /\Argb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)\z/
|
|
DEFAULT_COLOR = "#737373"
|
|
|
|
def initialize(form:, method:, items:, selected: nil, placeholder: I18n.t("helpers.select.default_label"), variant: :simple, include_blank: nil, searchable: false, menu_placement: :auto, **options)
|
|
@form = form
|
|
@method = method
|
|
@placeholder = placeholder
|
|
@variant = variant
|
|
@searchable = searchable
|
|
@menu_placement = normalize_menu_placement(menu_placement)
|
|
@options = options
|
|
|
|
normalized_items = normalize_items(items)
|
|
|
|
if include_blank
|
|
normalized_items.unshift({
|
|
value: nil,
|
|
label: include_blank,
|
|
object: nil
|
|
})
|
|
end
|
|
|
|
@items = normalized_items
|
|
@selected_value = selected
|
|
end
|
|
|
|
def selected_item
|
|
items.find { |item| item[:value] == selected_value }
|
|
end
|
|
|
|
# Returns the color for a given item (used in :badge variant)
|
|
def color_for(item)
|
|
obj = item[:object]
|
|
color = obj&.respond_to?(:color) ? obj.color : DEFAULT_COLOR
|
|
|
|
return DEFAULT_COLOR unless color.is_a?(String)
|
|
|
|
if color.match?(HEX_COLOR_REGEX) || color.match?(RGB_COLOR_REGEX)
|
|
color
|
|
else
|
|
DEFAULT_COLOR
|
|
end
|
|
end
|
|
|
|
# Returns the lucide_icon name for a given item (used in :badge variant)
|
|
def icon_for(item)
|
|
obj = item[:object]
|
|
obj&.respond_to?(:lucide_icon) ? obj.lucide_icon : nil
|
|
end
|
|
|
|
# Returns true if the item has a logo (used in :logo variant)
|
|
def logo_for(item)
|
|
obj = item[:object]
|
|
obj&.respond_to?(:logo_url) && obj.logo_url.present? ? Setting.transform_brand_fetch_url(obj.logo_url) : nil
|
|
end
|
|
|
|
private
|
|
|
|
def normalize_menu_placement(value)
|
|
normalized = value.to_s.downcase
|
|
MENU_PLACEMENTS.include?(normalized) ? normalized : "auto"
|
|
end
|
|
|
|
def normalize_items(collection)
|
|
collection.map do |item|
|
|
case item
|
|
when Hash
|
|
{
|
|
value: item[:value],
|
|
label: item[:label],
|
|
object: item[:object]
|
|
}
|
|
else
|
|
{
|
|
value: item.id,
|
|
label: item.name,
|
|
object: item
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|