From 54c772856b1a3abb4cf41254b51b06b270a852ae Mon Sep 17 00:00:00 2001 From: Adrian Marin Date: Sat, 2 Nov 2024 20:05:44 +0200 Subject: [PATCH 1/4] feature: combobox field --- Appraisals | 5 ++++ Gemfile | 4 ++- Gemfile.lock | 7 +++++ app/assets/stylesheets/avo.base.css | 1 + .../stylesheets/css/fields/combobox.css | 25 ++++++++++++++++++ .../combobox_field/edit_component.html.erb | 6 +++++ .../fields/combobox_field/edit_component.rb | 4 +++ .../combobox_field/index_component.html.erb | 3 +++ .../fields/combobox_field/index_component.rb | 4 +++ .../combobox_field/show_component.html.erb | 3 +++ .../fields/combobox_field/show_component.rb | 4 +++ .../avo/items/panel_component.html.erb | 2 +- .../avo/combobox_results_controller.rb | 26 +++++++++++++++++++ app/javascript/js/application.js | 4 ++- app/views/layouts/avo/application.html.erb | 1 + config/routes.rb | 2 ++ gemfiles/rails_6.1_ruby_3.1.4.gemfile | 2 +- gemfiles/rails_6.1_ruby_3.3.0.gemfile | 2 +- gemfiles/rails_7.1_ruby_3.1.4.gemfile | 3 ++- gemfiles/rails_7.1_ruby_3.1.4.gemfile.lock | 7 +++++ gemfiles/rails_7.1_ruby_3.3.0.gemfile | 3 ++- gemfiles/rails_7.1_ruby_3.3.0.gemfile.lock | 7 +++++ gemfiles/rails_7.2.0.beta2_ruby_3.1.4.gemfile | 3 ++- .../rails_7.2.0.beta2_ruby_3.1.4.gemfile.lock | 7 +++++ gemfiles/rails_7.2.0.beta2_ruby_3.3.0.gemfile | 3 ++- .../rails_7.2.0.beta2_ruby_3.3.0.gemfile.lock | 7 +++++ gemfiles/rails_8.0_ruby_3.1.4.gemfile | 3 ++- gemfiles/rails_8.0_ruby_3.1.4.gemfile.lock | 7 +++++ gemfiles/rails_8.0_ruby_3.3.0.gemfile | 3 ++- gemfiles/rails_8.0_ruby_3.3.0.gemfile.lock | 7 +++++ lib/avo/fields/base_field.rb | 9 ++++--- lib/avo/fields/combobox_field.rb | 18 +++++++++++++ package.json | 1 + spec/dummy/app/avo/resources/product.rb | 22 +++++++++++++++- yarn.lock | 5 ++++ 35 files changed, 205 insertions(+), 15 deletions(-) create mode 100644 app/assets/stylesheets/css/fields/combobox.css create mode 100644 app/components/avo/fields/combobox_field/edit_component.html.erb create mode 100644 app/components/avo/fields/combobox_field/edit_component.rb create mode 100644 app/components/avo/fields/combobox_field/index_component.html.erb create mode 100644 app/components/avo/fields/combobox_field/index_component.rb create mode 100644 app/components/avo/fields/combobox_field/show_component.html.erb create mode 100644 app/components/avo/fields/combobox_field/show_component.rb create mode 100644 app/controllers/avo/combobox_results_controller.rb create mode 100644 lib/avo/fields/combobox_field.rb diff --git a/Appraisals b/Appraisals index cae721aced..bd34f2d75e 100644 --- a/Appraisals +++ b/Appraisals @@ -1,6 +1,11 @@ ["3.1.4", "3.3.0"].each do |ruby_version| ["6.1", "7.1", "7.2.0.beta2"].each do |rails_version| appraise "rails-#{rails_version}-ruby-#{ruby_version}" do + if rails_version == "6.1" + # hotwire_combobox it's not compatible with Rails 6.1 + remove_gem "hotwire_combobox" + end + gem "psych", "< 4" gem "rails", "~> #{rails_version}" gem "activestorage", "~> #{rails_version}" diff --git a/Gemfile b/Gemfile index 8255fe4513..bc086435c7 100644 --- a/Gemfile +++ b/Gemfile @@ -72,7 +72,7 @@ group :development do # gem 'pry-rails' - gem "htmlbeautifier" + gem "htmlbeautifier", require: false gem "hotwire-livereload", "~> 1.3.0" @@ -193,3 +193,5 @@ gem "avo-record_link_field" gem "pagy", "> 8" gem "csv" + +gem "hotwire_combobox" diff --git a/Gemfile.lock b/Gemfile.lock index b337bf3f07..f061a123ed 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -348,6 +348,10 @@ GEM actioncable (>= 6.0.0) listen (>= 3.0.0) railties (>= 6.0.0) + hotwire_combobox (0.3.2) + rails (>= 7.0.7.2) + stimulus-rails (>= 1.2) + turbo-rails (>= 1.2) htmlbeautifier (1.4.3) i18n (1.14.6) concurrent-ruby (~> 1.0) @@ -621,6 +625,8 @@ GEM standard-performance (1.4.0) lint_roller (~> 1.1) rubocop-performance (~> 1.21.0) + stimulus-rails (1.3.4) + railties (>= 6.0.0) stringio (3.1.1) syntax_tree (6.2.0) prettier_print (>= 1.2.0) @@ -715,6 +721,7 @@ DEPENDENCIES hashid-rails (~> 1.4, >= 1.4.1) hightop hotwire-livereload (~> 1.3.0) + hotwire_combobox htmlbeautifier i18n-tasks (~> 1.0.12) image_processing (~> 1.12) diff --git a/app/assets/stylesheets/avo.base.css b/app/assets/stylesheets/avo.base.css index 67fa61f8d3..0ddab4d2c3 100644 --- a/app/assets/stylesheets/avo.base.css +++ b/app/assets/stylesheets/avo.base.css @@ -22,6 +22,7 @@ @import './css/fields/status.css'; @import './css/fields/code.css'; +@import './css/fields/combobox.css'; @import './css/fields/progress.css'; @import './css/fields/trix.css'; @import './css/fields/tags.css'; diff --git a/app/assets/stylesheets/css/fields/combobox.css b/app/assets/stylesheets/css/fields/combobox.css new file mode 100644 index 0000000000..bc4573ac2e --- /dev/null +++ b/app/assets/stylesheets/css/fields/combobox.css @@ -0,0 +1,25 @@ +root { + --hw-border-radius: 0.25rem; +} + +.hw-combobox .hw-combobox__main__wrapper, +.hw-combobox__input { + @apply bg-gray-25; +} +.hw-combobox .hw-combobox__main__wrapper { + @apply rounded; + + &:focus-within { + @apply border-gray-600 shadow-none; + } +} + + +[data-field-type="combobox"] { + .hw-combobox { + @apply w-full; + } + .hw-combobox .hw-combobox__main__wrapper { + @apply w-full; + } +} diff --git a/app/components/avo/fields/combobox_field/edit_component.html.erb b/app/components/avo/fields/combobox_field/edit_component.html.erb new file mode 100644 index 0000000000..3c66bceb04 --- /dev/null +++ b/app/components/avo/fields/combobox_field/edit_component.html.erb @@ -0,0 +1,6 @@ +<%= field_wrapper **field_wrapper_args do %> + <%= @form.combobox @field.for_attribute, + helpers.combobox_results_path(resource_class: @resource.class, field_id: @field.id), + name_when_new: "#{@resource.form_scope}[#{@field.for_attribute}]" + %> +<% end %> diff --git a/app/components/avo/fields/combobox_field/edit_component.rb b/app/components/avo/fields/combobox_field/edit_component.rb new file mode 100644 index 0000000000..707bb388bb --- /dev/null +++ b/app/components/avo/fields/combobox_field/edit_component.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class Avo::Fields::ComboboxField::EditComponent < Avo::Fields::EditComponent +end diff --git a/app/components/avo/fields/combobox_field/index_component.html.erb b/app/components/avo/fields/combobox_field/index_component.html.erb new file mode 100644 index 0000000000..cd04797d85 --- /dev/null +++ b/app/components/avo/fields/combobox_field/index_component.html.erb @@ -0,0 +1,3 @@ +<%= index_field_wrapper **field_wrapper_args do %> + <%= @field.value %> +<% end %> diff --git a/app/components/avo/fields/combobox_field/index_component.rb b/app/components/avo/fields/combobox_field/index_component.rb new file mode 100644 index 0000000000..2876ef7645 --- /dev/null +++ b/app/components/avo/fields/combobox_field/index_component.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class Avo::Fields::ComboboxField::IndexComponent < Avo::Fields::IndexComponent +end diff --git a/app/components/avo/fields/combobox_field/show_component.html.erb b/app/components/avo/fields/combobox_field/show_component.html.erb new file mode 100644 index 0000000000..d7740937a7 --- /dev/null +++ b/app/components/avo/fields/combobox_field/show_component.html.erb @@ -0,0 +1,3 @@ +<%= field_wrapper **field_wrapper_args do %> + <%= @field.value %> +<% end %> diff --git a/app/components/avo/fields/combobox_field/show_component.rb b/app/components/avo/fields/combobox_field/show_component.rb new file mode 100644 index 0000000000..bf31a7f275 --- /dev/null +++ b/app/components/avo/fields/combobox_field/show_component.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class Avo::Fields::ComboboxField::ShowComponent < Avo::Fields::ShowComponent +end diff --git a/app/components/avo/items/panel_component.html.erb b/app/components/avo/items/panel_component.html.erb index 290546c852..6824f430dd 100644 --- a/app/components/avo/items/panel_component.html.erb +++ b/app/components/avo/items/panel_component.html.erb @@ -7,7 +7,7 @@ <% end %> <% end %> <% c.with_body do %> - <% content_tag :div, class: "divide-y overflow-auto" do %> + <% content_tag :div, class: "divide-y" do %> <%= render Avo::Items::VisibleItemsComponent.new resource: @resource, item: @item, view: @view, form: @form %> <% end %> <% end %> diff --git a/app/controllers/avo/combobox_results_controller.rb b/app/controllers/avo/combobox_results_controller.rb new file mode 100644 index 0000000000..ea48577c70 --- /dev/null +++ b/app/controllers/avo/combobox_results_controller.rb @@ -0,0 +1,26 @@ +class Avo::ComboboxResultsController < Avo::ApplicationController + def show + # find resource + resource_class = Avo.resource_manager.get_resource(params[:resource_class]) + resource = resource_class.new(params: params, view: :edit, user: Avo::Current.user).tap do |r| + r.detect_fields + end + + # find field + field = resource.get_fields.find { |f| f.is_a?(Avo::Fields::ComboboxField) && f.id.to_sym == params[:field_id].to_sym } + + # get query + query = field.query + + # run query in the execution context + @results = Avo::ExecutionContext.new( + target: query, + resource: resource, + field: field, + params: params, + q: params[:q] + ).handle + + render turbo_stream: helpers.async_combobox_options(@results) + end +end diff --git a/app/javascript/js/application.js b/app/javascript/js/application.js index 72bd85a579..e296e43eb4 100644 --- a/app/javascript/js/application.js +++ b/app/javascript/js/application.js @@ -1,7 +1,8 @@ import { Alert, Popover } from 'tailwindcss-stimulus-components' import { Application } from '@hotwired/stimulus' -import TextareaAutogrow from 'stimulus-textarea-autogrow' +import HwComboboxController from '@josefarias/hotwire_combobox' import PasswordVisibility from '@stimulus-components/password-visibility' +import TextareaAutogrow from 'stimulus-textarea-autogrow' import TurboPower from 'turbo_power' TurboPower.initialize(window.Turbo.StreamActions) @@ -17,5 +18,6 @@ window.Stimulus = application // Register stimulus-components controller application.register('alert', Alert) application.register('popover', Popover) +application.register('hw-combobox', HwComboboxController) export { application } diff --git a/app/views/layouts/avo/application.html.erb b/app/views/layouts/avo/application.html.erb index eeac92cebc..02f9173050 100644 --- a/app/views/layouts/avo/application.html.erb +++ b/app/views/layouts/avo/application.html.erb @@ -22,6 +22,7 @@ <%= javascript_include_tag "hotwire-livereload", defer: true %> <% end %> <% end %> + <%= combobox_style_tag %> <%= render Avo::AssetManager::JavascriptComponent.new asset_manager: Avo.asset_manager %> <%= render partial: "avo/partials/head" %> <%= content_for :head %> diff --git a/config/routes.rb b/config/routes.rb index e5480b2117..a3684e3700 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -49,6 +49,8 @@ post "/debug/refresh_license", to: "debug#refresh_license" end + resource :combobox_results + if Rails.env.development? || Rails.env.staging? scope "/avo_private", as: "avo_private" do get "/design", to: "private#design" diff --git a/gemfiles/rails_6.1_ruby_3.1.4.gemfile b/gemfiles/rails_6.1_ruby_3.1.4.gemfile index 570b31baea..aa98ad8809 100644 --- a/gemfiles/rails_6.1_ruby_3.1.4.gemfile +++ b/gemfiles/rails_6.1_ruby_3.1.4.gemfile @@ -52,7 +52,7 @@ group :development do gem "bump", require: false gem "gem-release", require: false gem "annotate" - gem "htmlbeautifier" + gem "htmlbeautifier", require: false gem "hotwire-livereload", "~> 1.3.0" gem "rubocop", require: false gem "ripper-tags", require: false diff --git a/gemfiles/rails_6.1_ruby_3.3.0.gemfile b/gemfiles/rails_6.1_ruby_3.3.0.gemfile index 570b31baea..aa98ad8809 100644 --- a/gemfiles/rails_6.1_ruby_3.3.0.gemfile +++ b/gemfiles/rails_6.1_ruby_3.3.0.gemfile @@ -52,7 +52,7 @@ group :development do gem "bump", require: false gem "gem-release", require: false gem "annotate" - gem "htmlbeautifier" + gem "htmlbeautifier", require: false gem "hotwire-livereload", "~> 1.3.0" gem "rubocop", require: false gem "ripper-tags", require: false diff --git a/gemfiles/rails_7.1_ruby_3.1.4.gemfile b/gemfiles/rails_7.1_ruby_3.1.4.gemfile index 759f8a33ef..48128e6083 100644 --- a/gemfiles/rails_7.1_ruby_3.1.4.gemfile +++ b/gemfiles/rails_7.1_ruby_3.1.4.gemfile @@ -45,6 +45,7 @@ gem "avo-money_field" gem "avo-record_link_field" gem "pagy", "> 8" gem "csv" +gem "hotwire_combobox" gem "psych", "< 4" group :development do @@ -52,7 +53,7 @@ group :development do gem "bump", require: false gem "gem-release", require: false gem "annotate" - gem "htmlbeautifier" + gem "htmlbeautifier", require: false gem "hotwire-livereload", "~> 1.3.0" gem "rubocop", require: false gem "ripper-tags", require: false diff --git a/gemfiles/rails_7.1_ruby_3.1.4.gemfile.lock b/gemfiles/rails_7.1_ruby_3.1.4.gemfile.lock index 5e06f1a6a3..1fa947b27a 100644 --- a/gemfiles/rails_7.1_ruby_3.1.4.gemfile.lock +++ b/gemfiles/rails_7.1_ruby_3.1.4.gemfile.lock @@ -319,6 +319,10 @@ GEM actioncable (>= 6.0.0) listen (>= 3.0.0) railties (>= 6.0.0) + hotwire_combobox (0.3.2) + rails (>= 7.0.7.2) + stimulus-rails (>= 1.2) + turbo-rails (>= 1.2) htmlbeautifier (1.4.3) i18n (1.14.6) concurrent-ruby (~> 1.0) @@ -610,6 +614,8 @@ GEM standard-performance (1.4.0) lint_roller (~> 1.1) rubocop-performance (~> 1.21.0) + stimulus-rails (1.3.4) + railties (>= 6.0.0) syntax_tree (6.2.0) prettier_print (>= 1.2.0) terminal-table (3.0.2) @@ -702,6 +708,7 @@ DEPENDENCIES hashid-rails (~> 1.4, >= 1.4.1) hightop hotwire-livereload (~> 1.3.0) + hotwire_combobox htmlbeautifier i18n-tasks (~> 1.0.12) image_processing (~> 1.12) diff --git a/gemfiles/rails_7.1_ruby_3.3.0.gemfile b/gemfiles/rails_7.1_ruby_3.3.0.gemfile index 759f8a33ef..48128e6083 100644 --- a/gemfiles/rails_7.1_ruby_3.3.0.gemfile +++ b/gemfiles/rails_7.1_ruby_3.3.0.gemfile @@ -45,6 +45,7 @@ gem "avo-money_field" gem "avo-record_link_field" gem "pagy", "> 8" gem "csv" +gem "hotwire_combobox" gem "psych", "< 4" group :development do @@ -52,7 +53,7 @@ group :development do gem "bump", require: false gem "gem-release", require: false gem "annotate" - gem "htmlbeautifier" + gem "htmlbeautifier", require: false gem "hotwire-livereload", "~> 1.3.0" gem "rubocop", require: false gem "ripper-tags", require: false diff --git a/gemfiles/rails_7.1_ruby_3.3.0.gemfile.lock b/gemfiles/rails_7.1_ruby_3.3.0.gemfile.lock index ec9e0e12de..3d7ecbadb8 100644 --- a/gemfiles/rails_7.1_ruby_3.3.0.gemfile.lock +++ b/gemfiles/rails_7.1_ruby_3.3.0.gemfile.lock @@ -292,6 +292,10 @@ GEM actioncable (>= 6.0.0) listen (>= 3.0.0) railties (>= 6.0.0) + hotwire_combobox (0.3.2) + rails (>= 7.0.7.2) + stimulus-rails (>= 1.2) + turbo-rails (>= 1.2) htmlbeautifier (1.4.3) i18n (1.14.6) concurrent-ruby (~> 1.0) @@ -583,6 +587,8 @@ GEM standard-performance (1.3.1) lint_roller (~> 1.1) rubocop-performance (~> 1.20.2) + stimulus-rails (1.3.4) + railties (>= 6.0.0) syntax_tree (6.2.0) prettier_print (>= 1.2.0) terminal-table (3.0.2) @@ -675,6 +681,7 @@ DEPENDENCIES hashid-rails (~> 1.4, >= 1.4.1) hightop hotwire-livereload (~> 1.3.0) + hotwire_combobox htmlbeautifier i18n-tasks (~> 1.0.12) image_processing (~> 1.12) diff --git a/gemfiles/rails_7.2.0.beta2_ruby_3.1.4.gemfile b/gemfiles/rails_7.2.0.beta2_ruby_3.1.4.gemfile index 68b5b0698a..4e99213328 100644 --- a/gemfiles/rails_7.2.0.beta2_ruby_3.1.4.gemfile +++ b/gemfiles/rails_7.2.0.beta2_ruby_3.1.4.gemfile @@ -45,6 +45,7 @@ gem "avo-money_field" gem "avo-record_link_field" gem "pagy", "> 8" gem "csv" +gem "hotwire_combobox" gem "psych", "< 4" group :development do @@ -52,7 +53,7 @@ group :development do gem "bump", require: false gem "gem-release", require: false gem "annotate" - gem "htmlbeautifier" + gem "htmlbeautifier", require: false gem "hotwire-livereload", "~> 1.3.0" gem "rubocop", require: false gem "ripper-tags", require: false diff --git a/gemfiles/rails_7.2.0.beta2_ruby_3.1.4.gemfile.lock b/gemfiles/rails_7.2.0.beta2_ruby_3.1.4.gemfile.lock index d767cc3a40..000fe8bd0e 100644 --- a/gemfiles/rails_7.2.0.beta2_ruby_3.1.4.gemfile.lock +++ b/gemfiles/rails_7.2.0.beta2_ruby_3.1.4.gemfile.lock @@ -292,6 +292,10 @@ GEM actioncable (>= 6.0.0) listen (>= 3.0.0) railties (>= 6.0.0) + hotwire_combobox (0.3.2) + rails (>= 7.0.7.2) + stimulus-rails (>= 1.2) + turbo-rails (>= 1.2) htmlbeautifier (1.4.3) i18n (1.14.6) concurrent-ruby (~> 1.0) @@ -583,6 +587,8 @@ GEM standard-performance (1.3.1) lint_roller (~> 1.1) rubocop-performance (~> 1.20.2) + stimulus-rails (1.3.4) + railties (>= 6.0.0) syntax_tree (6.2.0) prettier_print (>= 1.2.0) terminal-table (3.0.2) @@ -675,6 +681,7 @@ DEPENDENCIES hashid-rails (~> 1.4, >= 1.4.1) hightop hotwire-livereload (~> 1.3.0) + hotwire_combobox htmlbeautifier i18n-tasks (~> 1.0.12) image_processing (~> 1.12) diff --git a/gemfiles/rails_7.2.0.beta2_ruby_3.3.0.gemfile b/gemfiles/rails_7.2.0.beta2_ruby_3.3.0.gemfile index 68b5b0698a..4e99213328 100644 --- a/gemfiles/rails_7.2.0.beta2_ruby_3.3.0.gemfile +++ b/gemfiles/rails_7.2.0.beta2_ruby_3.3.0.gemfile @@ -45,6 +45,7 @@ gem "avo-money_field" gem "avo-record_link_field" gem "pagy", "> 8" gem "csv" +gem "hotwire_combobox" gem "psych", "< 4" group :development do @@ -52,7 +53,7 @@ group :development do gem "bump", require: false gem "gem-release", require: false gem "annotate" - gem "htmlbeautifier" + gem "htmlbeautifier", require: false gem "hotwire-livereload", "~> 1.3.0" gem "rubocop", require: false gem "ripper-tags", require: false diff --git a/gemfiles/rails_7.2.0.beta2_ruby_3.3.0.gemfile.lock b/gemfiles/rails_7.2.0.beta2_ruby_3.3.0.gemfile.lock index d767cc3a40..000fe8bd0e 100644 --- a/gemfiles/rails_7.2.0.beta2_ruby_3.3.0.gemfile.lock +++ b/gemfiles/rails_7.2.0.beta2_ruby_3.3.0.gemfile.lock @@ -292,6 +292,10 @@ GEM actioncable (>= 6.0.0) listen (>= 3.0.0) railties (>= 6.0.0) + hotwire_combobox (0.3.2) + rails (>= 7.0.7.2) + stimulus-rails (>= 1.2) + turbo-rails (>= 1.2) htmlbeautifier (1.4.3) i18n (1.14.6) concurrent-ruby (~> 1.0) @@ -583,6 +587,8 @@ GEM standard-performance (1.3.1) lint_roller (~> 1.1) rubocop-performance (~> 1.20.2) + stimulus-rails (1.3.4) + railties (>= 6.0.0) syntax_tree (6.2.0) prettier_print (>= 1.2.0) terminal-table (3.0.2) @@ -675,6 +681,7 @@ DEPENDENCIES hashid-rails (~> 1.4, >= 1.4.1) hightop hotwire-livereload (~> 1.3.0) + hotwire_combobox htmlbeautifier i18n-tasks (~> 1.0.12) image_processing (~> 1.12) diff --git a/gemfiles/rails_8.0_ruby_3.1.4.gemfile b/gemfiles/rails_8.0_ruby_3.1.4.gemfile index 5366e6ffaf..e7933e50a0 100644 --- a/gemfiles/rails_8.0_ruby_3.1.4.gemfile +++ b/gemfiles/rails_8.0_ruby_3.1.4.gemfile @@ -45,6 +45,7 @@ gem "avo-money_field" gem "avo-record_link_field" gem "pagy", "> 8" gem "csv" +gem "hotwire_combobox" gem "psych", "< 4" group :development do @@ -52,7 +53,7 @@ group :development do gem "bump", require: false gem "gem-release", require: false gem "annotate" - gem "htmlbeautifier" + gem "htmlbeautifier", require: false gem "hotwire-livereload", "~> 1.3.0" gem "rubocop", require: false gem "ripper-tags", require: false diff --git a/gemfiles/rails_8.0_ruby_3.1.4.gemfile.lock b/gemfiles/rails_8.0_ruby_3.1.4.gemfile.lock index a1eecc99d1..8de46a9a0d 100644 --- a/gemfiles/rails_8.0_ruby_3.1.4.gemfile.lock +++ b/gemfiles/rails_8.0_ruby_3.1.4.gemfile.lock @@ -321,6 +321,10 @@ GEM actioncable (>= 6.0.0) listen (>= 3.0.0) railties (>= 6.0.0) + hotwire_combobox (0.3.2) + rails (>= 7.0.7.2) + stimulus-rails (>= 1.2) + turbo-rails (>= 1.2) htmlbeautifier (1.4.3) i18n (1.14.6) concurrent-ruby (~> 1.0) @@ -590,6 +594,8 @@ GEM standard-performance (1.3.1) lint_roller (~> 1.1) rubocop-performance (~> 1.20.2) + stimulus-rails (1.3.4) + railties (>= 6.0.0) syntax_tree (6.2.0) prettier_print (>= 1.2.0) terminal-table (3.0.2) @@ -683,6 +689,7 @@ DEPENDENCIES hashid-rails (~> 1.4, >= 1.4.1) hightop hotwire-livereload (~> 1.3.0) + hotwire_combobox htmlbeautifier i18n-tasks (~> 1.0.12) image_processing (~> 1.12) diff --git a/gemfiles/rails_8.0_ruby_3.3.0.gemfile b/gemfiles/rails_8.0_ruby_3.3.0.gemfile index 5366e6ffaf..e7933e50a0 100644 --- a/gemfiles/rails_8.0_ruby_3.3.0.gemfile +++ b/gemfiles/rails_8.0_ruby_3.3.0.gemfile @@ -45,6 +45,7 @@ gem "avo-money_field" gem "avo-record_link_field" gem "pagy", "> 8" gem "csv" +gem "hotwire_combobox" gem "psych", "< 4" group :development do @@ -52,7 +53,7 @@ group :development do gem "bump", require: false gem "gem-release", require: false gem "annotate" - gem "htmlbeautifier" + gem "htmlbeautifier", require: false gem "hotwire-livereload", "~> 1.3.0" gem "rubocop", require: false gem "ripper-tags", require: false diff --git a/gemfiles/rails_8.0_ruby_3.3.0.gemfile.lock b/gemfiles/rails_8.0_ruby_3.3.0.gemfile.lock index a1eecc99d1..8de46a9a0d 100644 --- a/gemfiles/rails_8.0_ruby_3.3.0.gemfile.lock +++ b/gemfiles/rails_8.0_ruby_3.3.0.gemfile.lock @@ -321,6 +321,10 @@ GEM actioncable (>= 6.0.0) listen (>= 3.0.0) railties (>= 6.0.0) + hotwire_combobox (0.3.2) + rails (>= 7.0.7.2) + stimulus-rails (>= 1.2) + turbo-rails (>= 1.2) htmlbeautifier (1.4.3) i18n (1.14.6) concurrent-ruby (~> 1.0) @@ -590,6 +594,8 @@ GEM standard-performance (1.3.1) lint_roller (~> 1.1) rubocop-performance (~> 1.20.2) + stimulus-rails (1.3.4) + railties (>= 6.0.0) syntax_tree (6.2.0) prettier_print (>= 1.2.0) terminal-table (3.0.2) @@ -683,6 +689,7 @@ DEPENDENCIES hashid-rails (~> 1.4, >= 1.4.1) hightop hotwire-livereload (~> 1.3.0) + hotwire_combobox htmlbeautifier i18n-tasks (~> 1.0.12) image_processing (~> 1.12) diff --git a/lib/avo/fields/base_field.rb b/lib/avo/fields/base_field.rb index 1e9ec2178c..e8b9ddd19e 100644 --- a/lib/avo/fields/base_field.rb +++ b/lib/avo/fields/base_field.rb @@ -224,9 +224,12 @@ def has_attribute?(record, attribute) # Try to see if the field has a different database ID than it's name def database_id - foreign_key - rescue - id + # belongs_to field has a foreign_key method + return foreign_key if respond_to?(:foreign_key) + + # we'll try the for_attribute if it's present + # otherwise we'll fallback to the id + for_attribute || id end def has_own_panel? diff --git a/lib/avo/fields/combobox_field.rb b/lib/avo/fields/combobox_field.rb new file mode 100644 index 0000000000..a302239c6a --- /dev/null +++ b/lib/avo/fields/combobox_field.rb @@ -0,0 +1,18 @@ +class Avo::Fields::ComboboxField < Avo::Fields::BaseField + attr_reader :query + attr_reader :compute_label + + def initialize(id, **args, &block) + super(id, **args, &block) + + # Store the original query + @query = args[:query] || lambda {} + add_string_prop args, :compute_label + end + + def label + return value if compute_label.blank? + + compute_label.call(value) + end +end diff --git a/package.json b/package.json index fc54947d7e..e5e000fa9d 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@babel/plugin-proposal-class-properties": "^7.12.1", "@hotwired/stimulus": "^3.2.2", "@hotwired/turbo-rails": "^8.0.10", + "@josefarias/hotwire_combobox": "^0.3.2", "@rails/activestorage": "^6.1.7", "@stimulus-components/password-visibility": "^3.0.0", "@tailwindcss/forms": "^0.5.9", diff --git a/spec/dummy/app/avo/resources/product.rb b/spec/dummy/app/avo/resources/product.rb index 53de4f952f..706d2d29cf 100644 --- a/spec/dummy/app/avo/resources/product.rb +++ b/spec/dummy/app/avo/resources/product.rb @@ -1,3 +1,6 @@ +require 'net/http' +require 'json' + class Avo::Resources::Product < Avo::BaseResource self.title = :title self.includes = [] @@ -28,7 +31,7 @@ class Avo::Resources::Product < Avo::BaseResource def fields field :id, as: :id - field :title, as: :text, html: { + field :title, as: :text, hide_on: :forms, html: { show: { label: { classes: "bg-gray-50 !text-pink-600" @@ -41,6 +44,23 @@ def fields } } } + field :combobox_title, + for_attribute: :title, + as: :combobox, + only_on: :forms, + help: "This is a combobox field and will suggest values based on your query.", + query: -> { + # Fetch products from an API + url = URI("https://dummyjson.com/products/search?q=#{params[:q]}") + + # Make the HTTP request + response = Net::HTTP.get(url) + + # Parse JSON response + data = JSON.parse(response) + + data["products"].map { |item| item["title"] } + } field :price, as: :money, currencies: %w[EUR USD RON PEN] field :description, as: :tiptap, placeholder: "Enter text", always_show: false field :image, as: :file, is_image: true diff --git a/yarn.lock b/yarn.lock index d5544a0dc8..06ae2163f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,6 +1825,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@josefarias/hotwire_combobox@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@josefarias/hotwire_combobox/-/hotwire_combobox-0.3.2.tgz#bf3d4b069bc3d41e72022bc0158666d35d010514" + integrity sha512-FFs6nqL1xmdofaBJDkAcLcFdiLgTOajSopPaBFc05EddgS2EjP9bkGvJDLaIEZn/CzWPSwwbMt3WvLEW968Geg== + "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.3" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" From c0c5695b6666adec3846e6a1257ff9e05f892f5a Mon Sep 17 00:00:00 2001 From: Adrian Marin Date: Sat, 2 Nov 2024 20:10:00 +0200 Subject: [PATCH 2/4] lint --- spec/dummy/app/avo/resources/product.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/dummy/app/avo/resources/product.rb b/spec/dummy/app/avo/resources/product.rb index 706d2d29cf..82c5061984 100644 --- a/spec/dummy/app/avo/resources/product.rb +++ b/spec/dummy/app/avo/resources/product.rb @@ -1,5 +1,5 @@ -require 'net/http' -require 'json' +require "net/http" +require "json" class Avo::Resources::Product < Avo::BaseResource self.title = :title From 0c3b6d46a13cd2be89dd261a5a75839ded1eff5e Mon Sep 17 00:00:00 2001 From: Adrian Marin Date: Tue, 5 Nov 2024 19:43:55 +0200 Subject: [PATCH 3/4] add accept_free_text --- .../avo/fields/combobox_field/edit_component.html.erb | 2 +- app/components/avo/fields/combobox_field/edit_component.rb | 5 +++++ lib/avo/fields/combobox_field.rb | 2 ++ spec/dummy/app/avo/resources/product.rb | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/components/avo/fields/combobox_field/edit_component.html.erb b/app/components/avo/fields/combobox_field/edit_component.html.erb index 3c66bceb04..6feca493ba 100644 --- a/app/components/avo/fields/combobox_field/edit_component.html.erb +++ b/app/components/avo/fields/combobox_field/edit_component.html.erb @@ -1,6 +1,6 @@ <%= field_wrapper **field_wrapper_args do %> <%= @form.combobox @field.for_attribute, helpers.combobox_results_path(resource_class: @resource.class, field_id: @field.id), - name_when_new: "#{@resource.form_scope}[#{@field.for_attribute}]" + name_when_new: %> <% end %> diff --git a/app/components/avo/fields/combobox_field/edit_component.rb b/app/components/avo/fields/combobox_field/edit_component.rb index 707bb388bb..c94a64d804 100644 --- a/app/components/avo/fields/combobox_field/edit_component.rb +++ b/app/components/avo/fields/combobox_field/edit_component.rb @@ -1,4 +1,9 @@ # frozen_string_literal: true class Avo::Fields::ComboboxField::EditComponent < Avo::Fields::EditComponent + def name_when_new + if @field.accept_free_text + "#{@resource.form_scope}[#{@field.for_attribute}]" + end + end end diff --git a/lib/avo/fields/combobox_field.rb b/lib/avo/fields/combobox_field.rb index a302239c6a..29ee8e6ac1 100644 --- a/lib/avo/fields/combobox_field.rb +++ b/lib/avo/fields/combobox_field.rb @@ -1,6 +1,7 @@ class Avo::Fields::ComboboxField < Avo::Fields::BaseField attr_reader :query attr_reader :compute_label + attr_reader :accept_free_text def initialize(id, **args, &block) super(id, **args, &block) @@ -8,6 +9,7 @@ def initialize(id, **args, &block) # Store the original query @query = args[:query] || lambda {} add_string_prop args, :compute_label + add_boolean_prop args, :accept_free_text, true end def label diff --git a/spec/dummy/app/avo/resources/product.rb b/spec/dummy/app/avo/resources/product.rb index 82c5061984..a04df0835c 100644 --- a/spec/dummy/app/avo/resources/product.rb +++ b/spec/dummy/app/avo/resources/product.rb @@ -47,6 +47,7 @@ def fields field :combobox_title, for_attribute: :title, as: :combobox, + # accept_free_text: false, only_on: :forms, help: "This is a combobox field and will suggest values based on your query.", query: -> { From 627f4af59d65d098adbd7f4395ce9482b8c75fb5 Mon Sep 17 00:00:00 2001 From: Adrian Marin Date: Tue, 5 Nov 2024 19:48:30 +0200 Subject: [PATCH 4/4] try to stub the combobox_style_tag method --- spec/rails_helper.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index c7985c6ed8..6e3a1570e6 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -210,6 +210,14 @@ def headless_download_setup(driver) require "support/shared_contexts" require "support/timezone" +Avo::ApplicationHelper.define_method(:combobox_style_tag) { + # The HW Combobox component is not available for Rails 6.x + # We need to stub it for that suite of tests + return super if respond_to?(:combobox_style_tag) + + "" +} + # https://github.com/titusfortner/webdrivers/issues/247 # Webdrivers::Chromedriver.required_version = "114.0.5735.90" # Webdrivers::Chromedriver.required_version = "116.0.5845.96"