avo 3.0.1.beta24 → 3.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of avo might be problematic. Click here for more details.

Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -5
  3. data/Gemfile.lock +164 -128
  4. data/README.md +19 -27
  5. data/app/components/avo/actions_component.rb +2 -2
  6. data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +122 -104
  7. data/app/components/avo/fields/belongs_to_field/edit_component.rb +11 -0
  8. data/app/components/avo/fields/location_field/edit_component.html.erb +12 -10
  9. data/app/components/avo/index/grid_item_component.html.erb +1 -1
  10. data/app/components/avo/index/resource_table_component.html.erb +2 -2
  11. data/app/components/avo/index/resource_table_component.rb +16 -0
  12. data/app/components/avo/index/table_row_component.html.erb +1 -1
  13. data/app/components/avo/items/panel_component.html.erb +25 -0
  14. data/app/components/avo/items/panel_component.rb +43 -0
  15. data/app/components/avo/items/switcher_component.html.erb +17 -0
  16. data/app/components/avo/items/switcher_component.rb +79 -0
  17. data/app/components/avo/items/visible_items_component.html.erb +10 -0
  18. data/app/components/avo/items/visible_items_component.rb +11 -0
  19. data/app/components/avo/modal_component.html.erb +11 -7
  20. data/app/components/avo/modal_component.rb +21 -0
  21. data/app/components/avo/paginator_component.html.erb +29 -40
  22. data/app/components/avo/paginator_component.rb +18 -0
  23. data/app/components/avo/panel_component.html.erb +2 -2
  24. data/app/components/avo/referrer_params_component.html.erb +1 -0
  25. data/app/components/avo/resource_component.rb +8 -15
  26. data/app/components/avo/resource_sidebar_component.html.erb +11 -18
  27. data/app/components/avo/resource_sidebar_component.rb +4 -2
  28. data/app/components/avo/sidebar_profile_component.html.erb +1 -1
  29. data/app/components/avo/tab_group_component.html.erb +1 -1
  30. data/app/components/avo/views/resource_edit_component.html.erb +15 -32
  31. data/app/components/avo/views/resource_edit_component.rb +18 -6
  32. data/app/components/avo/views/resource_index_component.rb +1 -0
  33. data/app/components/avo/views/resource_show_component.html.erb +14 -33
  34. data/app/components/avo/views/resource_show_component.rb +11 -0
  35. data/app/controllers/avo/actions_controller.rb +6 -4
  36. data/app/controllers/avo/application_controller.rb +2 -33
  37. data/app/controllers/avo/base_controller.rb +25 -16
  38. data/app/helpers/avo/application_helper.rb +4 -0
  39. data/app/javascript/js/controllers/fields/reload_belongs_to_field_controller.js +51 -0
  40. data/app/javascript/js/controllers.js +2 -0
  41. data/app/views/avo/actions/show.html.erb +4 -3
  42. data/app/views/avo/base/_new_via_belongs_to.html.erb +12 -0
  43. data/app/views/avo/base/close_modal_and_reload_field.turbo_stream.erb +8 -0
  44. data/app/views/avo/base/create_fail_action.turbo_stream.erb +13 -0
  45. data/app/views/avo/partials/_flash_alerts.turbo_stream.erb +3 -0
  46. data/config/i18n-tasks.yml +1 -1
  47. data/config/initializers/pagy.rb +2 -0
  48. data/config/master.key +1 -0
  49. data/lib/avo/base_action.rb +44 -38
  50. data/lib/avo/base_resource.rb +7 -8
  51. data/lib/avo/concerns/borrow_items_holder.rb +35 -0
  52. data/lib/avo/concerns/has_items.rb +65 -47
  53. data/lib/avo/concerns/hydration.rb +17 -0
  54. data/lib/avo/concerns/is_resource_item.rb +2 -10
  55. data/lib/avo/concerns/pagination.rb +53 -0
  56. data/lib/avo/concerns/visible_items.rb +7 -30
  57. data/lib/avo/dsl/field_parser.rb +1 -1
  58. data/lib/avo/fields/concerns/is_searchable.rb +3 -1
  59. data/lib/avo/fields/location_field.rb +2 -2
  60. data/lib/avo/filters/base_filter.rb +1 -0
  61. data/lib/avo/html/builder.rb +1 -0
  62. data/lib/avo/licensing/h_q.rb +4 -6
  63. data/lib/avo/licensing/license.rb +4 -0
  64. data/lib/avo/plugin.rb +5 -1
  65. data/lib/avo/resources/items/holder.rb +29 -11
  66. data/lib/avo/resources/items/item_group.rb +4 -9
  67. data/lib/avo/resources/items/main_panel.rb +10 -0
  68. data/lib/avo/resources/items/row.rb +4 -16
  69. data/lib/avo/resources/items/sidebar.rb +9 -10
  70. data/lib/avo/resources/items/tab.rb +3 -9
  71. data/lib/avo/resources/items/tab_group.rb +6 -12
  72. data/lib/avo/version.rb +1 -1
  73. data/lib/generators/avo/templates/locales/avo.ar.yml +7 -6
  74. data/lib/generators/avo/templates/locales/avo.en.yml +2 -0
  75. data/lib/generators/avo/templates/locales/avo.es.yml +127 -0
  76. data/lib/generators/avo/templates/locales/avo.fr.yml +2 -0
  77. data/lib/generators/avo/templates/locales/avo.nb.yml +2 -0
  78. data/lib/generators/avo/templates/locales/avo.nn.yml +2 -0
  79. data/lib/generators/avo/templates/locales/avo.pt-BR.yml +2 -0
  80. data/lib/generators/avo/templates/locales/avo.pt.yml +2 -0
  81. data/lib/generators/avo/templates/locales/avo.ro.yml +2 -0
  82. data/lib/generators/avo/templates/locales/avo.tr.yml +2 -0
  83. data/public/avo-assets/avo.base.css +121 -4
  84. data/public/avo-assets/avo.base.js +86 -86
  85. data/public/avo-assets/avo.base.js.map +3 -3
  86. data/public/avo-assets/avo.css +9744 -0
  87. data/public/avo-assets/avo.js +513 -0
  88. data/public/avo-assets/avo.js.map +7 -0
  89. metadata +23 -8
  90. data/app/components/avo/item_switcher_component.html.erb +0 -27
  91. data/app/components/avo/item_switcher_component.rb +0 -48
  92. data/app/views/avo/actions/keep_modal_open.turbo_stream.erb +0 -5
  93. data/lib/avo/action_model.rb +0 -20
@@ -1,115 +1,133 @@
1
- <% if is_polymorphic? %>
2
- <%
3
- # Set the model keys so we can pass them over
4
- model_keys = @field.types.map do |type|
5
- resource = Avo.resource_manager.get_resource_by_model_class(type.to_s)
6
- [type.to_s, resource.model_key]
7
- end.to_h
8
- %>
9
- <div class="divide-y"
10
- data-controller="belongs-to-field"
11
- data-searchable="<%= @field.is_searchable? %>"
12
- data-association="<%= @field.id %>"
13
- data-association-class="<%= @field&.target_resource&.model_class || nil %>"
14
- >
15
- <%= field_wrapper **field_wrapper_args, help: @field.polymorphic_help || '' do %>
16
- <%= @form.select @field.type_input_foreign_key, @field.types.map { |type| [Avo.resource_manager.get_resource_by_model_class(type.to_s).name, type.to_s] },
17
- {
18
- value: @field.value,
19
- include_blank: @field.placeholder,
20
- },
21
- {
22
- class: classes("w-full"),
23
- data: {
24
- **@field.get_html(:data, view: view, element: :input),
25
- action: "change->belongs-to-field#changeType #{field_html_action}",
26
- 'belongs-to-field-target': "select",
27
- },
28
- disabled: disabled
29
- }
1
+ <div data-controller="reload-belongs-to-field"
2
+ data-action="turbo:before-stream-render@document->reload-belongs-to-field#beforeStreamRender"
3
+ data-reload-belongs-to-field-polymorphic-value="<%= is_polymorphic? %>"
4
+ data-reload-belongs-to-field-searchable-value="<%= @field.is_searchable? %>"
5
+ data-reload-belongs-to-field-relation-name-value="<%= @field.id %>"
6
+ data-reload-belongs-to-field-target-name-value="<%= form.object_name %>[<%= @field.id_input_foreign_key %>]"
7
+ >
8
+ <% if is_polymorphic? %>
9
+ <%
10
+ # Set the model keys so we can pass them over
11
+ model_keys = @field.types.map do |type|
12
+ resource = Avo.resource_manager.get_resource_by_model_class(type.to_s)
13
+ [type.to_s, resource.model_key]
14
+ end.to_h
30
15
  %>
31
- <%
32
- # If the select field is disabled, no value will be sent. It's how HTML works.
33
- # Thus the extra hidden field to actually send the related id to the server.
34
- if disabled %>
35
- <%= @form.hidden_field @field.type_input_foreign_key %>
36
- <% end %>
37
- <% end %>
38
- <% @field.types.each do |type| %>
39
- <div class="hidden"
40
- data-belongs-to-field-target="type"
41
- data-type="<%= type %>"
42
- >
43
- <%= field_wrapper **field_wrapper_args, label: Avo.resource_manager.get_resource_by_model_class(type.to_s).name do %>
44
- <% if @field.is_searchable? %>
45
- <%= render Avo::Pro::SearchableAssociations::AutocompleteComponent.new form: @form,
46
- disabled: disabled,
47
- field: @field,
48
- foreign_key: @field.id_input_foreign_key,
49
- model_key: model_keys[type.to_s],
50
- polymorphic_record: polymorphic_record,
51
- resource: @resource,
52
- style: @field.get_html(:style, view: view, element: :input),
53
- type: type,
54
- classes: classes("w-full"),
55
- view: view
56
- %>
57
- <% else %>
58
- <%= @form.select @field.id_input_foreign_key,
59
- options_for_select(@field.values_for_type(type), @resource.present? && @resource.record.present? ? @resource.record[@field.id_input_foreign_key] : nil),
60
- {
61
- value: @resource.record[@field.id_input_foreign_key].to_s,
62
- include_blank: @field.placeholder,
63
- },
64
- {
65
- class: classes("w-full"),
66
- data: @field.get_html(:data, view: view, element: :input),
67
- disabled: disabled
68
- }
69
- %>
70
- <%
71
- # If the select field is disabled, no value will be sent. It's how HTML works.
72
- # Thus the extra hidden field to actually send the related id to the server.
73
- if disabled %>
74
- <%= @form.hidden_field @field.id_input_foreign_key %>
75
- <% end %>
76
- <% end %>
77
- <% end %>
78
- </div>
79
- <% end %>
80
- </div>
81
- <% else %>
82
- <%= field_wrapper **field_wrapper_args do %>
83
- <% if @field.is_searchable? %>
84
- <%= render Avo::Pro::SearchableAssociations::AutocompleteComponent.new form: @form,
85
- field: @field,
86
- model_key: @field.target_resource&.model_key,
87
- foreign_key: @field.id_input_foreign_key,
88
- resource: @resource,
89
- disabled: disabled,
90
- classes: classes("w-full"),
91
- view: view,
92
- style: @field.get_html(:style, view: view, element: :input)
93
- %>
94
- <% else %>
95
- <%= @form.select @field.id_input_foreign_key, @field.options,
16
+ <div class="divide-y"
17
+ data-controller="belongs-to-field"
18
+ data-searchable="<%= @field.is_searchable? %>"
19
+ data-association="<%= @field.id %>"
20
+ data-association-class="<%= @field&.target_resource&.model_class || nil %>"
21
+ >
22
+ <%= field_wrapper **field_wrapper_args, help: @field.polymorphic_help || '' do %>
23
+ <%= @form.select @field.type_input_foreign_key, @field.types.map { |type| [Avo.resource_manager.get_resource_by_model_class(type.to_s).name, type.to_s] },
96
24
  {
25
+ value: @field.value,
97
26
  include_blank: @field.placeholder,
98
- value: @field.value
99
27
  },
100
28
  {
101
29
  class: classes("w-full"),
102
- data: @field.get_html(:data, view: view, element: :input),
103
- disabled: disabled,
104
- style: @field.get_html(:style, view: view, element: :input)
30
+ data: {
31
+ **@field.get_html(:data, view: view, element: :input),
32
+ action: "change->belongs-to-field#changeType #{field_html_action}",
33
+ 'belongs-to-field-target': "select",
34
+ },
35
+ disabled: disabled
105
36
  }
106
37
  %>
107
- <%
108
- # If the select field is disabled, no value will be sent. It's how HTML works.
109
- # Thus the extra hidden field to actually send the related id to the server.
110
- if disabled %>
111
- <%= @form.hidden_field @field.id_input_foreign_key %>
38
+ <%
39
+ # If the select field is disabled, no value will be sent. It's how HTML works.
40
+ # Thus the extra hidden field to actually send the related id to the server.
41
+ if disabled %>
42
+ <%= @form.hidden_field @field.type_input_foreign_key %>
43
+ <% end %>
44
+ <% end %>
45
+ <% @field.types.each do |type| %>
46
+ <div class="hidden"
47
+ data-belongs-to-field-target="type"
48
+ data-type="<%= type %>"
49
+ >
50
+ <%= field_wrapper **field_wrapper_args, label: Avo.resource_manager.get_resource_by_model_class(type.to_s).name do %>
51
+ <% if @field.is_searchable? %>
52
+ <%= render Avo::Pro::SearchableAssociations::AutocompleteComponent.new form: @form,
53
+ disabled: disabled,
54
+ field: @field,
55
+ foreign_key: @field.id_input_foreign_key,
56
+ model_key: model_keys[type.to_s],
57
+ polymorphic_record: polymorphic_record,
58
+ resource: @resource,
59
+ style: @field.get_html(:style, view: view, element: :input),
60
+ type: type,
61
+ classes: classes("w-full"),
62
+ view: view
63
+ %>
64
+ <% else %>
65
+ <%= @form.select @field.id_input_foreign_key,
66
+ options_for_select(@field.values_for_type(type), @resource.present? && @resource.record.present? ? @resource.record[@field.id_input_foreign_key] : nil),
67
+ {
68
+ value: @resource.record[@field.id_input_foreign_key].to_s,
69
+ include_blank: @field.placeholder,
70
+ },
71
+ {
72
+ class: classes("w-full"),
73
+ data: @field.get_html(:data, view: view, element: :input),
74
+ disabled: disabled
75
+ }
76
+ %>
77
+ <%
78
+ # If the select field is disabled, no value will be sent. It's how HTML works.
79
+ # Thus the extra hidden field to actually send the related id to the server.
80
+ if disabled %>
81
+ <%= @form.hidden_field @field.id_input_foreign_key %>
82
+ <% end %>
83
+ <% end %>
84
+ <% create_href = create_path(Avo.resource_manager.get_resource_by_model_class(type.to_s)) %>
85
+ <% if !disabled && create_href.present? %>
86
+ <%= link_to t("avo.create_new_item", item: type.to_s.downcase),
87
+ create_href,
88
+ class: "text-sm"
89
+ %>
90
+ <% end %>
91
+ <% end %>
92
+ </div>
93
+ <% end %>
94
+ </div>
95
+ <% else %>
96
+ <%= field_wrapper **field_wrapper_args do %>
97
+ <% if @field.is_searchable? %>
98
+ <%= render Avo::Pro::SearchableAssociations::AutocompleteComponent.new form: @form,
99
+ field: @field,
100
+ model_key: @field.target_resource&.model_key,
101
+ foreign_key: @field.id_input_foreign_key,
102
+ resource: @resource,
103
+ disabled: disabled,
104
+ classes: classes("w-full"),
105
+ view: view,
106
+ style: @field.get_html(:style, view: view, element: :input)
107
+ %>
108
+ <% else %>
109
+ <%= @form.select @field.id_input_foreign_key, @field.options,
110
+ {
111
+ include_blank: @field.placeholder,
112
+ value: @field.value
113
+ },
114
+ {
115
+ class: classes("w-full"),
116
+ data: @field.get_html(:data, view: view, element: :input),
117
+ disabled: disabled,
118
+ style: @field.get_html(:style, view: view, element: :input)
119
+ }
120
+ %>
121
+ <%
122
+ # If the select field is disabled, no value will be sent. It's how HTML works.
123
+ # Thus the extra hidden field to actually send the related id to the server.
124
+ if disabled %>
125
+ <%= @form.hidden_field @field.id_input_foreign_key %>
126
+ <% end %>
127
+ <% end %>
128
+ <% if !disabled && create_path.present? %>
129
+ <%= link_to t("avo.create_new_item", item: @field.name.downcase), create_path, class: "text-sm" %>
112
130
  <% end %>
113
131
  <% end %>
114
132
  <% end %>
115
- <% end %>
133
+ </div>
@@ -57,6 +57,17 @@ class Avo::Fields::BelongsToField::EditComponent < Avo::Fields::EditComponent
57
57
  @field.get_html(:data, view: view, element: :input).fetch(:action, nil)
58
58
  end
59
59
 
60
+ def create_path(target_resource = nil)
61
+ return nil if @resource.blank?
62
+
63
+ helpers.new_resource_path(**{
64
+ via_relation: @field.id.to_s,
65
+ resource: target_resource || @field.target_resource,
66
+ via_record_id: resource.record.to_param,
67
+ via_belongs_to_resource_class: resource.class.name
68
+ }.compact)
69
+ end
70
+
60
71
  private
61
72
 
62
73
  def visit_through_association?
@@ -1,16 +1,18 @@
1
1
  <%= field_wrapper **field_wrapper_args do %>
2
2
  <% if field.value_as_array? %>
3
3
  <div class="flex gap-4">
4
- <%= @form.text_field @field.as_lat_long_field_id(:lat),
5
- value: @field.as_lat_long_value(:lat),
6
- class: classes("w-full"),
7
- placeholder: @field.as_lat_long_placeholder(:lat)
8
- %>
9
- <%= @form.text_field @field.as_lat_long_field_id(:long),
10
- value: @field.as_lat_long_value(:long),
11
- class: classes("w-full"),
12
- placeholder: @field.as_lat_long_placeholder(:long)
13
- %>
4
+ <%= @form.fields_for @field.id do |coordinates_form| %>
5
+ <%= coordinates_form.text_field @field.as_lat_long_field_id(:lat),
6
+ value: @field.as_lat_long_value(:lat),
7
+ class: classes("w-full"),
8
+ placeholder: @field.as_lat_long_placeholder(:lat)
9
+ %>
10
+ <%= coordinates_form.text_field @field.as_lat_long_field_id(:long),
11
+ value: @field.as_lat_long_value(:long),
12
+ class: classes("w-full"),
13
+ placeholder: @field.as_lat_long_placeholder(:long)
14
+ %>
15
+ <% end %>
14
16
  </div>
15
17
  <% else %>
16
18
  <%= @form.text_field @field.id,
@@ -8,7 +8,7 @@
8
8
  <%= content_tag :div,
9
9
  class: "relative w-full pb-3/4 rounded-t overflow-hidden #{html(:cover, :classes)}",
10
10
  style: html(:cover, :style) do %>
11
- <%== item_selector_input(floating: true, size: :lg) if @resource.record_selector%>
11
+ <%= item_selector_input(floating: true, size: :lg) if @resource.record_selector%>
12
12
  <%= render_cover %>
13
13
  <% end %>
14
14
  <div class="grid grid-cols-1 place-content-between p-4 h-full">
@@ -12,7 +12,7 @@
12
12
  <div class="absolute z-30 w-full ml-10 pt-px hidden" data-item-select-all-target="selectAllOverlay">
13
13
  <div class="bg-white flex items-center h-9 mt-0.5">
14
14
  <div class="mt-1.5" data-item-select-all-target="unselectedMessage">
15
- <%= t "avo.x_records_selected_from_a_total_of_x_html", selected: pagy.in, count: pagy.count %>
15
+ <%= selected_page_label %>
16
16
  <%= a_link request.url,
17
17
  size: :xs,
18
18
  color: :primary,
@@ -23,7 +23,7 @@
23
23
  <% end %>
24
24
  </div>
25
25
  <div class="mt-1.5" data-item-select-all-target="selectedMessage" class="hidden">
26
- <%= t "avo.x_records_selected_from_all_pages_html", count: pagy.count %>
26
+ <%= selected_all_label %>
27
27
  <%= a_link request.url,
28
28
  size: :xs,
29
29
  color: :primary,
@@ -23,4 +23,20 @@ class Avo::Index::ResourceTableComponent < ViewComponent::Base
23
23
 
24
24
  Avo::Services::EncryptionService.encrypt(message: @query, purpose: :select_all, serializer: Marshal)
25
25
  end
26
+
27
+ def selected_page_label
28
+ if @resource.pagination_type.countless?
29
+ t "avo.x_records_selected_from_page_html", selected: pagy.in
30
+ else
31
+ t "avo.x_records_selected_from_a_total_of_x_html", selected: pagy.in, count: pagy.count
32
+ end
33
+ end
34
+
35
+ def selected_all_label
36
+ if @resource.pagination_type.countless?
37
+ t "avo.records_selected_from_all_pages_html"
38
+ else
39
+ t "avo.x_records_selected_from_all_pages_html", count: pagy.count
40
+ end
41
+ end
26
42
  end
@@ -9,7 +9,7 @@
9
9
  <% if @resource.record_selector %>
10
10
  <td class="w-10">
11
11
  <div class="flex justify-center h-full">
12
- <%== item_selector_input floating: false %>
12
+ <%= item_selector_input floating: false %>
13
13
  </div>
14
14
  </td>
15
15
  <% end %>
@@ -0,0 +1,25 @@
1
+ <%= render Avo::PanelComponent.new(**args) do |c| %>
2
+ <% if @is_main_panel %>
3
+ <% c.with_tools do %>
4
+ <% controls.each do |control| %>
5
+ <%= render_control control %>
6
+ <% end %>
7
+ <% end %>
8
+ <% end %>
9
+ <% c.with_body do %>
10
+ <% content_tag :div, class: "divide-y overflow-auto" do %>
11
+ <%= render Avo::Items::VisibleItemsComponent.new resource: @resource, item: @item, view: @view, form: @form %>
12
+ <% end %>
13
+ <% end %>
14
+ <% if sidebars.any? { |sidebar| sidebar.visible_items.any? } %>
15
+ <% c.with_sidebar do %>
16
+ <div class="justify-between space-y-4">
17
+ <% sidebars.each_with_index do |sidebar, index| %>
18
+ <div class="<%= helpers.white_panel_classes if sidebar.panel_wrapper? %>">
19
+ <%= render Avo::ResourceSidebarComponent.new resource: @resource, sidebar: sidebar, params: params, view: view, form: @form, index: index %>
20
+ </div>
21
+ <% end %>
22
+ </div>
23
+ <% end %>
24
+ <% end %>
25
+ <% end %>
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Items::PanelComponent < Avo::ResourceComponent
4
+ include Avo::ApplicationHelper
5
+
6
+ def initialize(form:, item:, is_main_panel:, resource:, view:, actions: nil, index: nil, parent_component: nil, parent_record: nil, parent_resource: nil, reflection: nil)
7
+ @actions = actions
8
+ @form = form
9
+ @index = index
10
+ @is_main_panel = is_main_panel
11
+ @item = item
12
+ @parent_component = parent_component
13
+ @parent_record = parent_record
14
+ @parent_resource = parent_resource
15
+ @reflection = reflection
16
+ @resource = resource
17
+ @view = view
18
+ end
19
+
20
+ delegate :controls,
21
+ :title,
22
+ :back_path,
23
+ :edit_path,
24
+ :can_see_the_destroy_button?,
25
+ :can_see_the_save_button?,
26
+ :view_for,
27
+ :display_breadcrumbs,
28
+ to: :@parent_component
29
+
30
+ def args
31
+ if @is_main_panel
32
+ {
33
+ name: title,
34
+ description: @resource.description,
35
+ display_breadcrumbs: display_breadcrumbs,
36
+ index: 0,
37
+ data: {panel_id: "main"}
38
+ }
39
+ else
40
+ {name: @item.name, description: @item.description, index: @index}
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ <% if item.is_panel? || item.is_main_panel? %>
2
+ <%= render panel_component %>
3
+ <% elsif item.is_tool? %>
4
+ <% if item&.partial.present? %>
5
+ <%= render item.partial, tool: item, form: @form %>
6
+ <% end %>
7
+ <% elsif item.is_tab_group? %>
8
+ <%= render tab_group_component %>
9
+ <% elsif item.is_field? %>
10
+ <%= render field_component %>
11
+ <% elsif item.is_row? %>
12
+ <%= render Avo::RowComponent.new do |c| %>
13
+ <% c.with_body do %>
14
+ <%= render Avo::Items::VisibleItemsComponent.new resource: @resource, item: @item, view: @view, form: @form %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Items::SwitcherComponent < Avo::BaseComponent
4
+ include Turbo::FramesHelper
5
+
6
+ attr_reader :resource
7
+ attr_reader :reflection
8
+ attr_reader :index
9
+ attr_reader :item
10
+ attr_reader :view
11
+
12
+ def initialize(
13
+ resource: nil,
14
+ reflection: nil,
15
+ item: nil,
16
+ index: nil,
17
+ view: nil,
18
+ form: nil,
19
+ parent_resource: nil,
20
+ parent_record: nil,
21
+ parent_component: nil,
22
+ actions: nil,
23
+ field_component_extra_args: {}
24
+ )
25
+ @resource = resource
26
+ @reflection = reflection
27
+ @form = form
28
+ @index = index
29
+ @item = item
30
+ @view = view
31
+ @parent_resource = parent_resource
32
+ @parent_record = parent_record
33
+ @parent_component = parent_component
34
+ @actions = actions
35
+ @field_component_extra_args = field_component_extra_args
36
+ end
37
+
38
+ def form
39
+ @form || nil
40
+ end
41
+
42
+ def render?
43
+ # Stops rendering if the field should be hidden in reflections
44
+ if item.is_field?
45
+ return false if in_reflection? && item.hidden_in_reflection?
46
+ end
47
+
48
+ true
49
+ end
50
+
51
+ def in_reflection?
52
+ @reflection.present?
53
+ end
54
+
55
+ def tab_group_component
56
+ Avo::TabGroupComponent.new resource: @resource, group: item.hydrate(view: view), index: index, params: params, form: form, view: view
57
+ end
58
+
59
+ def field_component
60
+ final_item = item.dup.hydrate(resource: @resource, record: @resource.record, user: resource.user, view: view)
61
+ final_item.component_for_view(@view).new(field: final_item, resource: @resource, index: index, form: form, **@field_component_extra_args)
62
+ end
63
+
64
+ def panel_component
65
+ Avo::Items::PanelComponent.new(
66
+ actions: @actions,
67
+ form: form,
68
+ index: index,
69
+ is_main_panel: item.is_main_panel?,
70
+ item: item.hydrate(view: view),
71
+ parent_component: @parent_component,
72
+ parent_record: @parent_record,
73
+ parent_resource: @parent_resource,
74
+ reflection: @reflection,
75
+ resource: @resource,
76
+ view: view
77
+ )
78
+ end
79
+ end
@@ -0,0 +1,10 @@
1
+ <% @item.visible_items.each_with_index do |field, index| %>
2
+ <%= render Avo::Items::SwitcherComponent.new(
3
+ resource: @resource,
4
+ item: field,
5
+ index: index,
6
+ view: @view,
7
+ form: @form,
8
+ field_component_extra_args: @field_component_extra_args,
9
+ )%>
10
+ <% end %>
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Items::VisibleItemsComponent < ViewComponent::Base
4
+ def initialize(resource:, item:, view:, form:, field_component_extra_args: {})
5
+ @resource = resource
6
+ @item = item
7
+ @view = view
8
+ @form = form
9
+ @field_component_extra_args = field_component_extra_args
10
+ end
11
+ end
@@ -3,15 +3,19 @@
3
3
  data-modal-target="modal"
4
4
  >
5
5
  <div aria-expanded="true" class="modal-overlay absolute w-full h-full bg-opacity-25 bg-gray-800 flex justify-center items-center" data-action="click->modal#close"></div>
6
- <div aria-expanded="true" role="dialog" aria-modal="true" class="modal-body rounded-lg inset-auto w-11/12 lg:w-1/2 sm:max-w-168 min-h-1/4 bg-white flex z-50 relative shadow-modal overflow-auto max-h-full">
6
+ <div aria-expanded="true" role="dialog" aria-modal="true" class="modal-body rounded-lg inset-auto bg-white flex z-50 relative shadow-modal overflow-auto <%= width_classes %> <%= height_classes %>">
7
7
  <div class="flex-1 flex flex-col justify-between">
8
8
  <div>
9
- <div class="p-6 pb-0 text-2xl tracking-normal font-semibold text-black">
10
- <%= heading %>
11
- </div>
12
- <div class="p-6 text-base text-gray-500">
13
- <%= content %>
14
- </div>
9
+ <% if heading? %>
10
+ <div class="p-6 pb-0 text-2xl tracking-normal font-semibold text-black">
11
+ <%= heading %>
12
+ </div>
13
+ <% end %>
14
+ <% if content? %>
15
+ <div class="p-6 text-base text-gray-500">
16
+ <%= content %>
17
+ </div>
18
+ <% end %>
15
19
  </div>
16
20
  <% if controls? %>
17
21
  <div class="flex justify-end items-baseline space-x-4 p-4 bg-gray-100">
@@ -3,4 +3,25 @@
3
3
  class Avo::ModalComponent < ViewComponent::Base
4
4
  renders_one :heading
5
5
  renders_one :controls
6
+
7
+ attr_reader :width
8
+ attr_reader :body_class
9
+
10
+ def initialize(width: :md, body_class: nil)
11
+ @width = width
12
+ @body_class = body_class
13
+ end
14
+
15
+ def width_classes
16
+ case width.to_sym
17
+ when :md
18
+ "w-11/12 lg:w-1/2 sm:max-w-168"
19
+ when :xl
20
+ "w-11/12 lg:w-3/4"
21
+ end
22
+ end
23
+
24
+ def height_classes
25
+ "max-h-full min-h-1/4 max-h-11/12"
26
+ end
6
27
  end