avo 3.27.0 → 3.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +11 -11
  3. data/app/assets/stylesheets/avo.base.css +1 -0
  4. data/app/assets/stylesheets/css/fields/stars.css +13 -0
  5. data/app/components/avo/field_wrapper_component.rb +1 -5
  6. data/app/components/avo/fields/boolean_field/edit_component.html.erb +2 -1
  7. data/app/components/avo/fields/boolean_field/index_component.html.erb +1 -1
  8. data/app/components/avo/fields/boolean_field/show_component.html.erb +1 -1
  9. data/app/components/avo/fields/common/boolean_check_component.html.erb +2 -1
  10. data/app/components/avo/fields/common/boolean_check_component.rb +26 -6
  11. data/app/components/avo/fields/common/stars_component.html.erb +8 -0
  12. data/app/components/avo/fields/common/stars_component.rb +4 -0
  13. data/app/components/avo/fields/stars_field/edit_component.html.erb +23 -0
  14. data/app/components/avo/fields/stars_field/edit_component.rb +2 -0
  15. data/app/components/avo/fields/stars_field/index_component.html.erb +3 -0
  16. data/app/components/avo/fields/stars_field/index_component.rb +2 -0
  17. data/app/components/avo/fields/stars_field/show_component.html.erb +3 -0
  18. data/app/components/avo/fields/stars_field/show_component.rb +2 -0
  19. data/app/components/avo/index/field_wrapper_component.rb +1 -5
  20. data/app/components/avo/resource_component.rb +1 -17
  21. data/app/components/avo/views/resource_index_component.rb +1 -3
  22. data/app/javascript/js/controllers/fields/stars_field_controller.js +55 -0
  23. data/app/javascript/js/controllers.js +2 -0
  24. data/lib/avo/fields/boolean_field.rb +4 -0
  25. data/lib/avo/fields/stars_field.rb +13 -0
  26. data/lib/avo/media_library/configuration.rb +4 -5
  27. data/lib/avo/version.rb +1 -1
  28. data/lib/generators/avo/templates/cards/partial_card_partial.tt +3 -1
  29. data/public/avo-assets/avo.base.css +67 -38
  30. data/public/avo-assets/avo.base.js +158 -158
  31. data/public/avo-assets/avo.base.js.map +4 -4
  32. metadata +12 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 867fef6728ec2a3fddeea7b24f28821dfbd5f395b7df861e386201b748561d87
4
- data.tar.gz: 84b350b46c5cbef8823e6ae9a5e343938e3fae663bba8e407d836a3ff6721937
3
+ metadata.gz: 8265ebbf25207a0c543ecb8b87ead3ef6e116f561110c7f2e852cb473325296f
4
+ data.tar.gz: e82f9a4982b5ec344ad1b78db7b887c55de917ab9aa45895f8552ac5249e8537
5
5
  SHA512:
6
- metadata.gz: d71ba91f2bb012a65a44388c5ab1861df8a65339c0d9a125f1418a393e9d4815d1f7a00312a2c46812d2b46bd07ae34a64dbea0c37fbe7fb0ed3f1082d974085
7
- data.tar.gz: 56144157674c868c09d5c32fa1d04d754de3d4cbf7f8330e5c046d5d27ab5f3dbe3a93d02f38e80650c17e5755e9ad5cee0e798aaff823db6cc77c30ccc0699f
6
+ metadata.gz: 90d23c98f3fd3c3c97127dd693d596060cdda7b4fb08bb394303be685ea997f9234dbf96b2cc731901e697caa1f13ace446cb209e90f1c02861dbe74c0f045b3
7
+ data.tar.gz: 3df2ff9cb2cfdd4691caa1089ff7e5fad6f38d77dfecd648e456674e3b25a87bb06577fdd02a27f81135a9f8202db7a6473389c5257cce1fe8d6bfe6f52a7afa
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (3.27.0)
4
+ avo (3.28.0)
5
5
  actionview (>= 6.1)
6
6
  active_link_to
7
7
  activerecord (>= 6.1)
@@ -131,8 +131,8 @@ GEM
131
131
  money-rails (~> 1.12)
132
132
  avo-record_link_field (0.0.2)
133
133
  aws-eventstream (1.4.0)
134
- aws-partitions (1.1188.0)
135
- aws-sdk-core (3.239.2)
134
+ aws-partitions (1.1196.0)
135
+ aws-sdk-core (3.240.0)
136
136
  aws-eventstream (~> 1, >= 1.3.0)
137
137
  aws-partitions (~> 1, >= 1.992.0)
138
138
  aws-sigv4 (~> 1.9)
@@ -143,7 +143,7 @@ GEM
143
143
  aws-sdk-kms (1.118.0)
144
144
  aws-sdk-core (~> 3, >= 3.239.1)
145
145
  aws-sigv4 (~> 1.5)
146
- aws-sdk-s3 (1.205.0)
146
+ aws-sdk-s3 (1.208.0)
147
147
  aws-sdk-core (~> 3, >= 3.234.0)
148
148
  aws-sdk-kms (~> 1)
149
149
  aws-sigv4 (~> 1.5)
@@ -231,9 +231,9 @@ GEM
231
231
  warden (~> 1.2.3)
232
232
  diff-lcs (1.6.2)
233
233
  docile (1.4.1)
234
- dotenv (3.1.8)
235
- dotenv-rails (3.1.8)
236
- dotenv (= 3.1.8)
234
+ dotenv (3.2.0)
235
+ dotenv-rails (3.2.0)
236
+ dotenv (= 3.2.0)
237
237
  railties (>= 6.1)
238
238
  drb (2.2.3)
239
239
  dry-configurable (1.3.0)
@@ -282,7 +282,7 @@ GEM
282
282
  factory_bot_rails (6.5.1)
283
283
  factory_bot (~> 6.5)
284
284
  railties (>= 6.1.0)
285
- faker (3.5.2)
285
+ faker (3.5.3)
286
286
  i18n (>= 1.8.11, < 2)
287
287
  faraday (2.14.0)
288
288
  faraday-net_http (>= 2.0, < 3.5)
@@ -369,7 +369,7 @@ GEM
369
369
  jmespath (1.6.2)
370
370
  jsbundling-rails (1.3.1)
371
371
  railties (>= 6.0.0)
372
- json (2.16.0)
372
+ json (2.17.1)
373
373
  kramdown (2.5.1)
374
374
  rexml (>= 3.3.9)
375
375
  kramdown-parser-gfm (1.1.0)
@@ -519,7 +519,7 @@ GEM
519
519
  ffi (~> 1.0)
520
520
  rbs (3.9.5)
521
521
  logger
522
- rdoc (6.16.0)
522
+ rdoc (6.16.1)
523
523
  erb
524
524
  psych (>= 4.0.0)
525
525
  tsort
@@ -666,7 +666,7 @@ GEM
666
666
  standard-performance (1.8.0)
667
667
  lint_roller (~> 1.1)
668
668
  rubocop-performance (~> 1.25.0)
669
- stringio (3.1.8)
669
+ stringio (3.1.9)
670
670
  syntax_tree (6.3.0)
671
671
  prettier_print (>= 1.2.0)
672
672
  terminal-table (4.0.0)
@@ -26,6 +26,7 @@
26
26
  @import './css/fields/trix.css';
27
27
  @import './css/fields/tags.css';
28
28
  @import './css/fields/tiptap.css';
29
+ @import './css/fields/stars.css';
29
30
 
30
31
  @import 'tailwindcss/components';
31
32
 
@@ -0,0 +1,13 @@
1
+ [data-component="avo/fields/common/stars_component"] {
2
+ .star {
3
+ @apply h-5 w-5 text-gray-300 transition-colors;
4
+ }
5
+
6
+ .star.filled {
7
+ @apply text-yellow-400;
8
+ }
9
+
10
+ .rating-group {
11
+ @apply flex items-center gap-1;
12
+ }
13
+ }
@@ -114,10 +114,6 @@ class Avo::FieldWrapperComponent < Avo::BaseComponent
114
114
  end
115
115
 
116
116
  def render_dash?
117
- if @field.type == "boolean"
118
- @field.value.nil?
119
- else
120
- @field.value.blank? && @dash_if_blank
121
- end
117
+ @field.value.blank? && @dash_if_blank
122
118
  end
123
119
  end
@@ -1,6 +1,7 @@
1
1
  <%= field_wrapper(**field_wrapper_args) do %>
2
2
  <div class="h-8 flex items-center">
3
- <%= @form.label @field.id,
3
+ <%= content_tag :label,
4
+ for: "#{@form.object_name}_#{@field.id}",
4
5
  class: class_names(
5
6
  "relative flex items-center",
6
7
  "opacity-50": disabled?,
@@ -1,3 +1,3 @@
1
- <%= index_field_wrapper(**field_wrapper_args, flush: true) do %>
1
+ <%= index_field_wrapper(**field_wrapper_args, flush: true, dash_if_blank: @field.dash_if_blank?) do %>
2
2
  <%= render Avo::Fields::Common::BooleanCheckComponent.new checked: @field.value %>
3
3
  <% end %>
@@ -1,3 +1,3 @@
1
- <%= field_wrapper(**field_wrapper_args) do %>
1
+ <%= field_wrapper(**field_wrapper_args, dash_if_blank: @field.dash_if_blank?) do %>
2
2
  <%= render Avo::Fields::Common::BooleanCheckComponent.new checked: @field.value %>
3
3
  <% end %>
@@ -1 +1,2 @@
1
- <%= helpers.svg "#{@icon}.svg", class: classes %>
1
+ <%= content_tag :span, t(@checked), class: 'sr-only' %>
2
+ <%= helpers.svg "#{@icon}.svg", class: classes, data: {checked_state: state} %>
@@ -1,18 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Avo::Fields::Common::BooleanCheckComponent < Avo::BaseComponent
4
- prop :checked, default: false
4
+ STATE_CONFIG = {
5
+ true => {
6
+ name: "checked",
7
+ icon: "heroicons/outline/check-circle",
8
+ color: "text-green-600"
9
+ },
10
+ false => {
11
+ name: "unchecked",
12
+ icon: "heroicons/outline/x-circle",
13
+ color: "text-red-600"
14
+ },
15
+ nil => {
16
+ name: "indeterminate",
17
+ icon: "heroicons/outline/minus-circle",
18
+ color: "text-gray-400"
19
+ }
20
+ }.freeze
21
+
22
+ prop :checked, default: nil
5
23
  prop :size, default: :md
6
- prop :icon do |value|
7
- @checked ? "heroicons/outline/check-circle" : "heroicons/outline/x-circle"
24
+ prop :icon do
25
+ STATE_CONFIG[@checked][:icon]
26
+ end
27
+
28
+ def state
29
+ STATE_CONFIG[@checked][:name]
8
30
  end
9
31
 
10
32
  def classes
11
- helpers.class_names({
33
+ helpers.class_names(STATE_CONFIG[@checked][:color], {
12
34
  "h-5": @size == :sm,
13
35
  "h-6": @size == :md,
14
- "text-green-600": @checked,
15
- "text-red-600": !@checked,
16
36
  })
17
37
  end
18
38
  end
@@ -0,0 +1,8 @@
1
+ <div class="rating-group" data-component="<%= component_name %>">
2
+ <% (1..@max).each do |rating_value| %>
3
+ <%= helpers.svg(
4
+ 'heroicons/solid/star',
5
+ class: class_names("star inline-block mr-0.5", filled: rating_value <= (@value || 0))
6
+ ) %>
7
+ <% end %>
8
+ </div>
@@ -0,0 +1,4 @@
1
+ class Avo::Fields::Common::StarsComponent < Avo::BaseComponent
2
+ prop :value
3
+ prop :max, default: 5
4
+ end
@@ -0,0 +1,23 @@
1
+ <%= field_wrapper(**field_wrapper_args) do %>
2
+ <div data-controller="stars-field">
3
+ <%= @form.hidden_field @field.id,
4
+ value: @field.value,
5
+ data: { stars_field_target: "valueInput" } %>
6
+
7
+ <div class="rating-group flex items-center gap-1 mb-3"
8
+ data-component="avo/fields/common/stars_component"
9
+ data-stars-field-target="starsContainer"
10
+ data-max="<%= @field.max %>"
11
+ data-current-value="<%= @field.value || 0 %>">
12
+ <% (1..@field.max).each do |rating_value| %>
13
+ <%= helpers.svg('heroicons/solid/star',
14
+ class: class_names("star inline-block mr-0.5", filled: rating_value <= (@value || 0)),
15
+ data: {
16
+ star_value: rating_value,
17
+ stars_field_target: "star",
18
+ action: "click->stars-field#setStar mouseenter->stars-field#hoverStar mouseleave->stars-field#unhoverStar"
19
+ }) %>
20
+ <% end %>
21
+ </div>
22
+ </div>
23
+ <% end %>
@@ -0,0 +1,2 @@
1
+ class Avo::Fields::StarsField::EditComponent < Avo::Fields::EditComponent
2
+ end
@@ -0,0 +1,3 @@
1
+ <%= index_field_wrapper(**field_wrapper_args, flush: true) do %>
2
+ <%= render Avo::Fields::Common::StarsComponent.new(value: @field.value, max: @field.max) %>
3
+ <% end %>
@@ -0,0 +1,2 @@
1
+ class Avo::Fields::StarsField::IndexComponent < Avo::Fields::IndexComponent
2
+ end
@@ -0,0 +1,3 @@
1
+ <%= field_wrapper(**field_wrapper_args) do %>
2
+ <%= render Avo::Fields::Common::StarsComponent.new value: @field.value, max: @field.max %>
3
+ <% end %>
@@ -0,0 +1,2 @@
1
+ class Avo::Fields::StarsField::ShowComponent < Avo::Fields::ShowComponent
2
+ end
@@ -45,10 +45,6 @@ class Avo::Index::FieldWrapperComponent < Avo::BaseComponent
45
45
  end
46
46
 
47
47
  def render_dash?
48
- if @field.type == "boolean"
49
- @field.value.nil?
50
- else
51
- @field.value.blank? && @dash_if_blank
52
- end
48
+ @field.value.blank? && @dash_if_blank
53
49
  end
54
50
  end
@@ -59,7 +59,7 @@ class Avo::ResourceComponent < Avo::BaseComponent
59
59
  def can_see_the_actions_button?
60
60
  return authorize_association_for(:act_on) if @reflection.present?
61
61
 
62
- @resource.authorization.authorize_action(:act_on, raise_exception: false) && !has_reflection_and_is_read_only
62
+ @resource.authorization.authorize_action(:act_on, raise_exception: false)
63
63
  end
64
64
 
65
65
  def destroy_path
@@ -93,22 +93,6 @@ class Avo::ResourceComponent < Avo::BaseComponent
93
93
  end
94
94
  end
95
95
 
96
- def has_reflection_and_is_read_only
97
- if @reflection.present? && @reflection.active_record.name && @reflection.name
98
- resource = Avo.resource_manager.get_resource_by_model_class(@reflection.active_record.name).new(params: helpers.params, view: view, user: helpers._current_user)
99
- fields = resource.get_field_definitions
100
- filtered_fields = fields.filter { |f| f.id == @reflection.name }
101
- else
102
- return false
103
- end
104
-
105
- if filtered_fields.present?
106
- filtered_fields.find { |f| f.id == @reflection.name }.is_disabled?
107
- else
108
- false
109
- end
110
- end
111
-
112
96
  def render_control(control)
113
97
  send :"render_#{control.type}", control
114
98
  end
@@ -37,12 +37,10 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
37
37
 
38
38
  return authorize_association_for(:create) if @reflection.present?
39
39
 
40
- @resource.authorization.authorize_action(:new, raise_exception: false) && !has_reflection_and_is_read_only
40
+ @resource.authorization.authorize_action(:new, raise_exception: false)
41
41
  end
42
42
 
43
43
  def can_attach?
44
- return false if has_reflection_and_is_read_only
45
-
46
44
  reflection_class = if @reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection)
47
45
  @reflection.through_reflection.class
48
46
  else
@@ -0,0 +1,55 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ // Connects to data-controller="stars-field"
4
+ export default class extends Controller {
5
+ static targets = ["valueInput", "starsContainer", "star"]
6
+
7
+ connect() {
8
+ this.maxValue = parseInt(this.starsContainerTarget.dataset.max)
9
+ this.currentValue = parseInt(this.starsContainerTarget.dataset.currentValue)
10
+ this.updateStarsDisplay(this.currentValue)
11
+ }
12
+
13
+ setStar(event) {
14
+ const starValue = parseInt(event.currentTarget.dataset.starValue)
15
+
16
+ // If clicking the same star that's already selected, set to 0 (no rating)
17
+ if (starValue === this.currentValue) {
18
+ this.currentValue = 0
19
+ } else {
20
+ this.currentValue = starValue
21
+ }
22
+
23
+ // Update the hidden input value
24
+ this.valueInputTarget.value = this.currentValue
25
+
26
+ // Update the visual display
27
+ this.updateStarsDisplay(this.currentValue)
28
+
29
+ // Trigger change event for form validation
30
+ this.valueInputTarget.dispatchEvent(new Event('change'))
31
+ }
32
+
33
+ hoverStar(event) {
34
+ const hoverValue = parseInt(event.currentTarget.dataset.starValue)
35
+ this.updateStarsDisplay(hoverValue, true)
36
+ }
37
+
38
+ unhoverStar(event) {
39
+ // Return to the actual selected value when not hovering
40
+ this.updateStarsDisplay(this.currentValue)
41
+ }
42
+
43
+ updateStarsDisplay(value, isHover = false) {
44
+ this.starTargets.forEach((star, index) => {
45
+ const starValue = index + 1
46
+
47
+ // Add appropriate class based on value
48
+ if (starValue <= value) {
49
+ star.classList.add('filled')
50
+ } else {
51
+ star.classList.remove('filled')
52
+ }
53
+ })
54
+ }
55
+ }
@@ -52,6 +52,7 @@ import TiptapFieldController from './controllers/fields/tiptap_field_controller'
52
52
  import ToggleController from './controllers/toggle_controller'
53
53
  import TrixBodyController from './controllers/trix_body_controller'
54
54
  import TrixFieldController from './controllers/fields/trix_field_controller'
55
+ import StarsFieldController from './controllers/fields/stars_field_controller'
55
56
 
56
57
  application.register('action', ActionController)
57
58
  application.register('actions-overflow', ActionsOverflowController)
@@ -107,5 +108,6 @@ application.register('reload-belongs-to-field', ReloadBelongsToFieldController)
107
108
  application.register('tags-field', TagsFieldController)
108
109
  application.register('tiptap-field', TiptapFieldController)
109
110
  application.register('trix-field', TrixFieldController)
111
+ application.register('stars-field', StarsFieldController)
110
112
 
111
113
  // Custom controllers
@@ -28,6 +28,10 @@ module Avo
28
28
  end
29
29
 
30
30
  def as_toggle? = !!@args[:as_toggle]
31
+
32
+ def nil_as_indeterminate? = !!@args[:nil_as_indeterminate]
33
+
34
+ def dash_if_blank? = value.nil? && !nil_as_indeterminate?
31
35
  end
32
36
  end
33
37
  end
@@ -0,0 +1,13 @@
1
+ module Avo
2
+ module Fields
3
+ class StarsField < BaseField
4
+ attr_reader :max
5
+
6
+ def initialize(name, **args, &block)
7
+ super(name, **args, &block)
8
+
9
+ @max = args[:max] || 5
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,19 +1,18 @@
1
1
  module Avo
2
2
  module MediaLibrary
3
3
  class Configuration
4
- include ActiveSupport::Configurable
5
4
 
6
- config_accessor(:visible) { true }
7
- config_accessor(:enabled) { false }
5
+ class_attribute :visible, default: true
6
+ class_attribute :enabled, default: false
8
7
 
9
8
  def visible?
10
9
  return false if disabled?
11
10
 
12
- Avo::ExecutionContext.new(target: config[:visible]).handle
11
+ Avo::ExecutionContext.new(target: visible).handle
13
12
  end
14
13
 
15
14
  def enabled?
16
- Avo::ExecutionContext.new(target: config[:enabled]).handle
15
+ Avo::ExecutionContext.new(target: enabled).handle
17
16
  end
18
17
 
19
18
  def disabled? = !enabled?
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "3.27.0" unless const_defined?(:VERSION)
2
+ VERSION = "3.28.0" unless const_defined?(:VERSION)
3
3
  end
@@ -1,6 +1,8 @@
1
1
  <div class="flex-1 flex p-4">
2
2
  <div class="place-self-end">
3
- Dashboard ID: <%%= @dashboard.id %>
3
+ @parent: <%%= @parent %><br />
4
+ @resource: <%%= @resource %><br />
5
+ @dashboard: <%%= @dashboard %>
4
6
  <br />
5
7
  <br />
6
8
  Customize this partial under <code class='p-1 rounded bg-gray-500 text-white text-sm'>app/views/avo/cards/_<%= name.underscore %>.html.erb</code>