From 64751729dbe545571a795492681105984cb6b417 Mon Sep 17 00:00:00 2001 From: PageHey Date: Fri, 20 Jun 2025 18:01:01 +0200 Subject: [PATCH] change coupdoeil_popover_tag signature --- app/helpers/coupdoeil/application_helper.rb | 11 ++----- app/models/coupdoeil/popover.rb | 29 ++++++++++++++++--- app/models/coupdoeil/popover/options_set.rb | 11 +++---- app/models/coupdoeil/popover/setup.rb | 16 +++------- app/models/coupdoeil/tag.rb | 29 ++++++++++++------- docs/api.md | 28 ++++++++++++++++-- .../dummy/app/views/pages/_side_menu.html.erb | 2 +- test/dummy/app/views/pages/home.html.erb | 9 +++--- 8 files changed, 87 insertions(+), 48 deletions(-) diff --git a/app/helpers/coupdoeil/application_helper.rb b/app/helpers/coupdoeil/application_helper.rb index e83a55c..239b301 100644 --- a/app/helpers/coupdoeil/application_helper.rb +++ b/app/helpers/coupdoeil/application_helper.rb @@ -2,15 +2,8 @@ module Coupdoeil module ApplicationHelper - def coupdoeil_popover_tag(popover, options = nil, **attributes_or_options, &) - if options.present? - attributes = attributes_or_options - else - options = attributes_or_options.extract!(*Popover::OptionsSet::OPTION_NAMES) - attributes = attributes_or_options - end - popover_options = options - render(Coupdoeil::Tag.new(popover:, popover_options:, attributes:), &) + def coupdoeil_popover_tag(popover, popover_options = nil, tag_attributes = nil, &) + render(Coupdoeil::Tag.new(popover:, popover_options:, attributes: tag_attributes), &) end end end diff --git a/app/models/coupdoeil/popover.rb b/app/models/coupdoeil/popover.rb index d1d445f..2ba4ab9 100644 --- a/app/models/coupdoeil/popover.rb +++ b/app/models/coupdoeil/popover.rb @@ -46,7 +46,7 @@ module Coupdoeil attr_reader :registry def popover_resource_name = @popover_resource_name ||= name.delete_suffix("Popover").underscore - def with(...) = Setup.new(self).with_params(...) + def with(...) = setup_class.new(self).with_params(...) def inherited(subclass) super @@ -69,14 +69,35 @@ module Coupdoeil def method_missing(method_name, *args, &) return super unless action_methods.include?(method_name.name) - raise ArgumentError, "expected no arguments" if args.any? - Setup.new(self).with_type(method_name) - end + action_methods.each do |action_name| + define_singleton_method(action_name) do |**k| + setup_class.new(self).with_type(action_name) + end + end + raise ArgumentError, "expected no arguments, given #{args.size}" if args.any? + + setup_class.new(self).with_type(method_name.name) + end def respond_to_missing?(method, include_all = false) action_methods.include?(method.name) || super end + + def setup_class + @setup_class ||= begin + popover_klass = self + Class.new(Setup) do + popover_klass.action_methods.each do |action_name| + define_method(action_name) do |*args| + raise ArgumentError, "expected no arguments, given #{args.size}" if args.any? + + with_type(action_name) + end + end + end + end + end end attr_reader :params diff --git a/app/models/coupdoeil/popover/options_set.rb b/app/models/coupdoeil/popover/options_set.rb index 5649e9f..68a706b 100644 --- a/app/models/coupdoeil/popover/options_set.rb +++ b/app/models/coupdoeil/popover/options_set.rb @@ -23,28 +23,29 @@ module Coupdoeil def to_h = options def initialize(options = {}) + options.assert_valid_keys(OPTION_NAMES) + @options = options end def merge(options_set) - OptionsSet.new(@options.merge(options_set.options)) + OptionsSet.new(options.merge(options_set.options)) end def validate! ORDERED_OPTIONS.map do |option| - next unless @options.key?(option.key) + next unless options.key?(option.key) - value = @options[option.key] + value = options[option.key] option.new(value).validate! end - @options.assert_valid_keys(ORDERED_OPTIONS.map(&:key)) end def to_base36 @to_base36 ||= begin shift = 0 ORDERED_OPTIONS.reverse.sum do |option| - bits = option.into_bits(@options[option.key]) + bits = option.into_bits(options[option.key]) result = bits << shift shift += option.bit_size result diff --git a/app/models/coupdoeil/popover/setup.rb b/app/models/coupdoeil/popover/setup.rb index a1d4d1d..0bb52dd 100644 --- a/app/models/coupdoeil/popover/setup.rb +++ b/app/models/coupdoeil/popover/setup.rb @@ -13,9 +13,9 @@ module Coupdoeil @params = EMPTY_PARAMS end - def default_options = klass.default_options_for(type) def identifier = "#{type}@#{klass.popover_resource_name}" def render_in(view_context) = klass.new(params, view_context).process(type) + def options = @options ||= klass.default_options_for(type) def with_type(type) @type = type @@ -27,17 +27,9 @@ module Coupdoeil self end - def method_missing(method_name, *args, &) - if klass.action_methods.include?(method_name.name) - raise ArgumentError, "expected no arguments, given #{args.size}" if args.any? - with_type(method_name) - else - super - end - end - - def respond_to_missing?(method, include_all = false) - klass.action_methods.include?(method.name) || super + def with_options(new_options) + @options = options.merge(Popover::OptionsSet.new(new_options)) + self end end end diff --git a/app/models/coupdoeil/tag.rb b/app/models/coupdoeil/tag.rb index 13cf379..3194a29 100644 --- a/app/models/coupdoeil/tag.rb +++ b/app/models/coupdoeil/tag.rb @@ -2,19 +2,20 @@ module Coupdoeil class Tag + delegate :options, to: :popover_setup, prefix: :popover + def initialize(popover:, popover_options:, attributes:) @popover_setup = popover + @popover_setup.with_options(popover_options) if popover_options @attributes = attributes - popover_options = Popover::OptionsSet.new(popover_options) - @popover_options_set = popover_setup.default_options.merge(popover_options) - @popover_options_set.validate! + @popover_setup.options.validate! end def render_in(view_context, &block) ActiveSupport::Notifications.instrument("render_tag.coupdoeil") do content = view_context.capture(&block) if block_given? - view_context.content_tag("coup-doeil", **@attributes.merge(popover_attributes)) do - if popover_options_set.preload? + view_context.content_tag("coup-doeil", **tag_attributes) do + if popover_options.preload? view_context.tag.template(view_context.render(popover_setup), class: "popover-content") + content else content @@ -25,21 +26,29 @@ module Coupdoeil private - attr_reader :popover_options_set, :popover_setup + attr_reader :popover_setup def popover_attributes - attributes = { "popover-options": popover_options_set.to_base36 } + attributes = { "popover-options": popover_options.to_base36 } - unless popover_options_set.preload? + unless popover_options.preload? params = Params.serialize(popover_setup.params).sole.presence&.to_json attributes.merge!("popover-type" => popover_setup.identifier, "popover-params" => params) end - + # if Rails.env.local? - attributes.merge!(popover_options_set.to_h.transform_keys { "popover-#{_1}" }) + attributes.merge!(popover_options.to_h.transform_keys { "popover-#{_1}" }) end attributes end + + def tag_attributes + if @attributes + @attributes.merge(popover_attributes) + else + popover_attributes + end + end end end diff --git a/docs/api.md b/docs/api.md index b2b87f8..e99c1ab 100644 --- a/docs/api.md +++ b/docs/api.md @@ -6,7 +6,7 @@ nav_order: 3 # {{ page.title }} -# Coupdoeil::Popover +## `Coupdoeil::Popover` ## Class methods @@ -29,7 +29,7 @@ ProjectPopover.default_options # > ``` -### `.default_options_for` -> [Coupdoeil::Popover::OptionsSet] +### `.default_options_for(*action_names, **option_values)` -> [Coupdoeil::Popover::OptionsSet] Sets or returns default options for one or many actions. Options inherit from default options. ```ruby @@ -51,7 +51,7 @@ ProjectPopover.default_options_for :tooltip ## Instance methods ### `#params` -> [HashWithIndifferentAccess] -Returns deserialized params passed to `.with` method. +Returns deserialized params initially passed to `.with` method. ```ruby ProjectPopover.with(project: @project).summary @@ -71,3 +71,25 @@ Returns applications helpers. It is accessible both within action method and tem ### `#controller` -> [ActionController::Base] Returns controller that renders the popover, either the current controller when preloading or a `Coupdoeil::PopoversController` instance. Can be used to retrieve data about current request for example. + +## `Coupdoeil::ApplicationHelper` + +## Instance methods + +### `#coupdoeil_popover_tag(popover, popover_options = nil, tag_attributes = nil, &block)` -> [ActiveSupport::SafeBuffer] +Returns a tag for the given popover and its options. +```ruby +coupdoeil_popover_tag ProjectPopover.with(project: @project).summary +``` +You can pass specific options that will override default options for the popover action. +```ruby +coupdoeil_popover_tag ProjectPopover.with(project: @project).summary, placement: "top", animation: false +``` +You can also pass attributes for tag. +```ruby +coupdoeil_popover_tag ProjectPopover.with(project: @project).summary, nil, class: "inline-block" +``` +Passing attributes to tag and options to popover +```ruby +coupdoeil_popover_tag ProjectPopover.with(project: @project).summary, { placement: "top", animation: false }, class: "inline-block" +``` diff --git a/test/dummy/app/views/pages/_side_menu.html.erb b/test/dummy/app/views/pages/_side_menu.html.erb index 726406f..c3003d8 100644 --- a/test/dummy/app/views/pages/_side_menu.html.erb +++ b/test/dummy/app/views/pages/_side_menu.html.erb @@ -8,7 +8,7 @@ data-controller="side-menu-item" data-action="click->side-menu-item#toggle" > - <%= coupdoeil_popover_tag SideMenuItemPopover.with(actions: sub_actions_count).sub_actions, data: { side_menu_item_target: "popover" } do %> + <%= coupdoeil_popover_tag SideMenuItemPopover.with(actions: sub_actions_count).sub_actions, nil, data: { side_menu_item_target: "popover" } do %>
Action group <%= main_action %> diff --git a/test/dummy/app/views/pages/home.html.erb b/test/dummy/app/views/pages/home.html.erb index f6af930..677068c 100644 --- a/test/dummy/app/views/pages/home.html.erb +++ b/test/dummy/app/views/pages/home.html.erb @@ -30,13 +30,14 @@ <%= coupdoeil_popover_tag ProjectPopover.with(project:).actions, - data: { actions_target: "actionsMenu" } do %> + nil, + data: { actions_target: "actionsMenu" } do %> <% end %> <%= coupdoeil_popover_tag ManagerPopover.new_form, - animation: "fade-in", - class: "block", - data: { actions_target: "newManager" } + { animation: "fade-in" }, + class: "block", + data: { actions_target: "newManager" } %> -- GitLab