avo 2.9.2.pre1 → 2.10.0
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +5 -7
- data/README.md +4 -0
- data/app/assets/stylesheets/css/buttons.css +4 -1
- data/app/components/avo/actions_component.rb +6 -2
- data/app/components/avo/base_component.rb +2 -0
- data/app/components/avo/button_component.rb +3 -1
- data/app/components/avo/fields/common/key_value_component.html.erb +2 -2
- data/app/components/avo/fields/common/single_file_viewer_component.rb +1 -1
- data/app/components/avo/fields/date_field/edit_component.html.erb +1 -0
- data/app/components/avo/fields/date_time_field/edit_component.html.erb +10 -25
- data/app/components/avo/fields/date_time_field/index_component.html.erb +1 -9
- data/app/components/avo/fields/date_time_field/show_component.html.erb +1 -9
- data/app/components/avo/fields/edit_component.rb +5 -0
- data/app/components/avo/fields/show_component.rb +1 -1
- data/app/components/avo/index/ordering/button_component.rb +2 -12
- data/app/components/avo/index/resource_controls_component.html.erb +2 -2
- data/app/components/avo/index/resource_controls_component.rb +5 -1
- data/app/components/avo/index/resource_table_component.html.erb +1 -1
- data/app/components/avo/index/table_row_component.html.erb +1 -1
- data/app/components/avo/item_switcher_component.html.erb +19 -0
- data/app/components/avo/item_switcher_component.rb +45 -0
- data/app/components/avo/panel_component.html.erb +23 -24
- data/app/components/avo/panel_component.rb +8 -5
- data/app/components/avo/tab_group_component.html.erb +53 -0
- data/app/components/avo/tab_group_component.rb +51 -0
- data/app/components/avo/tab_switcher_component.html.erb +21 -0
- data/app/components/avo/tab_switcher_component.rb +86 -0
- data/app/components/avo/views/resource_edit_component.html.erb +34 -56
- data/app/components/avo/views/resource_edit_component.rb +11 -1
- data/app/components/avo/views/resource_index_component.html.erb +1 -1
- data/app/components/avo/views/resource_index_component.rb +3 -3
- data/app/components/avo/views/resource_show_component.html.erb +58 -89
- data/app/components/avo/views/resource_show_component.rb +2 -2
- data/app/controllers/avo/actions_controller.rb +1 -1
- data/app/controllers/avo/application_controller.rb +20 -3
- data/app/helpers/avo/application_helper.rb +0 -6
- data/app/helpers/avo/url_helpers.rb +1 -1
- data/app/javascript/avo.js +5 -1
- data/app/javascript/js/controllers/fields/date_field_controller.js +25 -87
- data/app/javascript/js/controllers/loading_button_controller.js +25 -21
- data/app/javascript/js/controllers/tabs_controller.js +86 -0
- data/app/javascript/js/controllers.js +2 -0
- data/app/views/avo/base/index.html.erb +1 -1
- data/app/views/avo/base/show.html.erb +1 -1
- data/app/views/avo/cards/show.html.erb +1 -1
- data/app/views/avo/debug/index.html.erb +1 -1
- data/app/views/avo/home/_actions.html.erb +1 -1
- data/app/views/avo/home/_dashboards.html.erb +19 -0
- data/app/views/avo/home/_filters.html.erb +1 -1
- data/app/views/avo/home/_resources.html.erb +1 -1
- data/app/views/avo/home/failed_to_load.html.erb +1 -1
- data/app/views/avo/home/index.html.erb +14 -2
- data/app/views/avo/partials/_javascript.html.erb +1 -1
- data/app/views/avo/partials/_tabs_toggle.html.erb +20 -0
- data/app/views/avo/private/design.html.erb +1 -1
- data/config/routes.rb +1 -1
- data/db/migrate/20210421064037_add_color_to_teams.rb +5 -0
- data/db/migrate/20210423075924_add_progress_to_projects.rb +5 -0
- data/db/migrate/20210525143134_add_slug_to_users.rb +6 -0
- data/lib/avo/app.rb +11 -4
- data/lib/avo/base_action.rb +2 -19
- data/lib/avo/base_card.rb +1 -7
- data/lib/avo/base_resource.rb +1 -95
- data/lib/avo/base_resource_tool.rb +3 -1
- data/lib/avo/concerns/handles_field_args.rb +1 -1
- data/lib/avo/concerns/has_fields.rb +247 -50
- data/lib/avo/concerns/has_html_attributes.rb +1 -1
- data/lib/avo/concerns/is_resource_item.rb +36 -0
- data/lib/avo/concerns/model_class_constantized.rb +23 -0
- data/lib/avo/dashboards/base_dashboard.rb +1 -1
- data/lib/avo/dsl/field_parser.rb +83 -0
- data/lib/avo/fields/base_field.rb +19 -2
- data/lib/avo/fields/date_field.rb +2 -0
- data/lib/avo/fields/date_time_field.rb +9 -21
- data/lib/avo/fields/field_extensions/visible_in_different_views.rb +18 -1
- data/lib/avo/fields/has_base_field.rb +20 -1
- data/lib/avo/fields/has_one_field.rb +4 -1
- data/lib/avo/grid_collector.rb +6 -3
- data/lib/avo/items_holder.rb +68 -0
- data/lib/avo/licensing/h_q.rb +10 -0
- data/lib/avo/main_panel.rb +3 -0
- data/lib/avo/menu/builder.rb +8 -7
- data/lib/avo/panel.rb +25 -0
- data/lib/avo/panel_builder.rb +23 -0
- data/lib/avo/services/uri_service.rb +71 -0
- data/lib/avo/tab.rb +78 -0
- data/lib/avo/tab_builder.rb +25 -0
- data/lib/avo/tab_group.rb +40 -0
- data/lib/avo/tab_group_builder.rb +43 -0
- data/lib/avo/version.rb +1 -1
- data/lib/avo.rb +1 -0
- data/lib/generators/avo/templates/locales/avo.fr.yml +115 -0
- data/lib/generators/avo/templates/resource/controller.tt +2 -0
- data/lib/generators/avo/templates/resource_tools/partial.tt +1 -1
- data/lib/generators/avo/templates/tool/view.tt +1 -1
- data/public/avo-assets/avo.css +27 -3
- data/public/avo-assets/avo.js +77 -77
- data/public/avo-assets/avo.js.map +3 -3
- metadata +28 -13
- data/app/assets/builds/action_cable.js +0 -2
- data/app/assets/builds/action_cable.js.map +0 -7
- data/app/assets/builds/application.js +0 -2
- data/app/assets/builds/application.js.map +0 -7
- data/app/assets/builds/avo.css +0 -9028
- data/app/assets/builds/avo.js +0 -512
- data/app/assets/builds/avo.js.map +0 -7
- data/app/assets/builds/avo_custom.js +0 -6
- data/app/assets/builds/avo_custom.js.map +0 -7
- data/lib/avo/concerns/has_tools.rb +0 -47
@@ -4,8 +4,11 @@ module Avo
|
|
4
4
|
extend ActiveSupport::DescendantsTracker
|
5
5
|
extend Avo::Fields::FieldExtensions::HasFieldName
|
6
6
|
|
7
|
-
include Avo::
|
7
|
+
include Avo::Concerns::IsResourceItem
|
8
|
+
include Avo::Concerns::HandlesFieldArgs
|
9
|
+
|
8
10
|
include ActionView::Helpers::UrlHelper
|
11
|
+
include Avo::Fields::FieldExtensions::VisibleInDifferentViews
|
9
12
|
|
10
13
|
include Avo::Concerns::HandlesFieldArgs
|
11
14
|
include Avo::Concerns::HasHTMLAttributes
|
@@ -48,8 +51,9 @@ module Avo
|
|
48
51
|
attr_reader :panel_name
|
49
52
|
|
50
53
|
class_attribute :field_name_attribute
|
54
|
+
class_attribute :item_type, default: :field
|
51
55
|
|
52
|
-
def initialize(id,
|
56
|
+
def initialize(id, **args, &block)
|
53
57
|
super(id, **args, &block)
|
54
58
|
|
55
59
|
@id = id
|
@@ -206,9 +210,14 @@ module Avo
|
|
206
210
|
|
207
211
|
# Try and build the component class or fallback to a blank one
|
208
212
|
def component_for_view(view = :index)
|
213
|
+
# Use the edit variant for all "update" views
|
214
|
+
view = :edit if view.in? [:new, :create, :update]
|
215
|
+
|
209
216
|
component_class = "::Avo::Fields::#{view_component_name}::#{view.to_s.camelize}Component"
|
210
217
|
component_class.constantize
|
211
218
|
rescue
|
219
|
+
# When returning nil, a race condition happens and throws an error in some environments.
|
220
|
+
# See https://github.com/avo-hq/avo/pull/365
|
212
221
|
::Avo::BlankFieldComponent
|
213
222
|
end
|
214
223
|
|
@@ -228,6 +237,14 @@ module Avo
|
|
228
237
|
true
|
229
238
|
end
|
230
239
|
|
240
|
+
def visible_in_reflection?
|
241
|
+
true
|
242
|
+
end
|
243
|
+
|
244
|
+
def hidden_in_reflection?
|
245
|
+
!visible_in_reflection?
|
246
|
+
end
|
247
|
+
|
231
248
|
private
|
232
249
|
|
233
250
|
def model_or_class(model)
|
@@ -3,6 +3,7 @@ module Avo
|
|
3
3
|
class DateField < TextField
|
4
4
|
attr_reader :first_day_of_week
|
5
5
|
attr_reader :picker_format
|
6
|
+
attr_reader :disable_mobile
|
6
7
|
attr_reader :format
|
7
8
|
attr_reader :relative
|
8
9
|
|
@@ -13,6 +14,7 @@ module Avo
|
|
13
14
|
@picker_format = args[:picker_format].present? ? args[:picker_format] : "Y-m-d"
|
14
15
|
@format = args[:format].present? ? args[:format] : :long
|
15
16
|
@relative = args[:relative].present? ? args[:relative] : false
|
17
|
+
@disable_mobile = args[:disable_mobile].present? ? args[:disable_mobile] : false
|
16
18
|
end
|
17
19
|
|
18
20
|
def formatted_value
|
@@ -2,29 +2,25 @@ module Avo
|
|
2
2
|
module Fields
|
3
3
|
class DateTimeField < DateField
|
4
4
|
attr_reader :format
|
5
|
-
attr_reader :picker_format
|
6
5
|
attr_reader :time_24hr
|
7
6
|
attr_reader :timezone
|
8
7
|
|
9
8
|
def initialize(id, **args, &block)
|
10
9
|
super(id, **args, &block)
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
add_string_prop args, :timezone
|
11
|
+
@picker_format = args[:picker_format].present? ? args[:picker_format] : "Y-m-d H:i:S"
|
12
|
+
@time_24hr = args[:time_24hr].present? ? args[:time_24hr] : false
|
13
|
+
@timezone = args[:timezone].present? ? args[:timezone] : Rails.application.config.time_zone
|
16
14
|
end
|
17
15
|
|
18
16
|
def formatted_value
|
19
17
|
return nil if value.nil?
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
value.utc.iso8601
|
19
|
+
if @format.is_a?(Symbol)
|
20
|
+
value.to_time.in_time_zone(timezone).to_formatted_s(@format)
|
21
|
+
else
|
22
|
+
value.to_time.in_time_zone(timezone).strftime(@format)
|
23
|
+
end
|
28
24
|
end
|
29
25
|
|
30
26
|
def fill_field(model, key, value, params)
|
@@ -36,18 +32,10 @@ module Avo
|
|
36
32
|
|
37
33
|
return model if value.blank?
|
38
34
|
|
39
|
-
model[id] =
|
35
|
+
model[id] = value.to_time.in_time_zone(Rails.application.config.time_zone)
|
40
36
|
|
41
37
|
model
|
42
38
|
end
|
43
|
-
|
44
|
-
def utc_time(value)
|
45
|
-
if timezone.present?
|
46
|
-
ActiveSupport::TimeZone.new(timezone).local_to_utc(Time.parse(value))
|
47
|
-
else
|
48
|
-
value
|
49
|
-
end
|
50
|
-
end
|
51
39
|
end
|
52
40
|
end
|
53
41
|
end
|
@@ -7,13 +7,20 @@ module Avo
|
|
7
7
|
attr_accessor :show_on_new
|
8
8
|
attr_accessor :show_on_edit
|
9
9
|
|
10
|
-
def initialize(id, **args, &block)
|
10
|
+
def initialize(id = nil, **args, &block)
|
11
11
|
@show_on_index = @show_on_index.nil? ? true : @show_on_index
|
12
12
|
@show_on_show = @show_on_show.nil? ? true : @show_on_show
|
13
13
|
@show_on_new = @show_on_new.nil? ? true : @show_on_new
|
14
14
|
@show_on_edit = @show_on_edit.nil? ? true : @show_on_edit
|
15
15
|
end
|
16
16
|
|
17
|
+
# Validates if the field is visible on certain view
|
18
|
+
def visible_on?(view)
|
19
|
+
raise "No view specified on visibility check." if view.blank?
|
20
|
+
|
21
|
+
send :"show_on_#{view.to_s}"
|
22
|
+
end
|
23
|
+
|
17
24
|
def show_on(*where)
|
18
25
|
return show_on_all if where.include? :all
|
19
26
|
|
@@ -44,6 +51,16 @@ module Avo
|
|
44
51
|
end
|
45
52
|
end
|
46
53
|
|
54
|
+
# When submitting the form on creation, the new page will be create but we don't have a visibility marker for create so we'll default to new
|
55
|
+
def show_on_create
|
56
|
+
show_on_new
|
57
|
+
end
|
58
|
+
|
59
|
+
# When submitting the form on update, the new page will be create but we don't have a visibility marker for update so we'll default to edit
|
60
|
+
def show_on_update
|
61
|
+
show_on_edit
|
62
|
+
end
|
63
|
+
|
47
64
|
private
|
48
65
|
|
49
66
|
def show_on_view(view)
|
@@ -29,7 +29,10 @@ module Avo
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def frame_url
|
32
|
-
|
32
|
+
Avo::Services::URIService.parse(@resource.record_path)
|
33
|
+
.append_path(id.to_s)
|
34
|
+
.append_query(turbo_frame: turbo_frame.to_s)
|
35
|
+
.to_s
|
33
36
|
end
|
34
37
|
|
35
38
|
# The value
|
@@ -59,6 +62,22 @@ module Avo
|
|
59
62
|
def placeholder
|
60
63
|
@placeholder || I18n.t("avo.choose_an_option")
|
61
64
|
end
|
65
|
+
|
66
|
+
def has_own_panel?
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
def visible_in_reflection?
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
# Adds the view override component
|
75
|
+
# has_one, has_many, has_and_belongs_to_many fields don't have edit views
|
76
|
+
def component_for_view(view = :index)
|
77
|
+
view = :show if view.in? [:new, :create, :update, :edit]
|
78
|
+
|
79
|
+
super view
|
80
|
+
end
|
62
81
|
end
|
63
82
|
end
|
64
83
|
end
|
@@ -22,7 +22,10 @@ module Avo
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def frame_url
|
25
|
-
|
25
|
+
Avo::Services::URIService.parse(@resource.record_path)
|
26
|
+
.append_paths(id, value.id)
|
27
|
+
.append_query(turbo_frame: turbo_frame)
|
28
|
+
.to_s
|
26
29
|
end
|
27
30
|
|
28
31
|
def fill_field(model, key, value, params)
|
data/lib/avo/grid_collector.rb
CHANGED
@@ -13,15 +13,18 @@ module Avo
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def cover(field_name, as:, **args, &block)
|
16
|
-
|
16
|
+
field_parser = Avo::Dsl::FieldParser.new(id: field_name, as: as, order_index: items_index, **args, &block).parse
|
17
|
+
self.cover_field = field_parser.instance if field_parser.valid?
|
17
18
|
end
|
18
19
|
|
19
20
|
def title(field_name, as:, **args, &block)
|
20
|
-
|
21
|
+
field_parser = Avo::Dsl::FieldParser.new(id: field_name, as: as, order_index: items_index, **args, &block).parse
|
22
|
+
self.title_field = field_parser.instance if field_parser.valid?
|
21
23
|
end
|
22
24
|
|
23
25
|
def body(field_name, as:, **args, &block)
|
24
|
-
|
26
|
+
field_parser = Avo::Dsl::FieldParser.new(id: field_name, as: as, order_index: items_index, **args, &block).parse
|
27
|
+
self.body_field = field_parser.instance if field_parser.valid?
|
25
28
|
end
|
26
29
|
|
27
30
|
def hydrate(model:, view:, resource:)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Avo
|
2
|
+
class ItemsHolder
|
3
|
+
attr_reader :tools
|
4
|
+
attr_accessor :items
|
5
|
+
attr_accessor :invalid_fields
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@items = []
|
9
|
+
@items_index = 0
|
10
|
+
@invalid_fields = []
|
11
|
+
end
|
12
|
+
|
13
|
+
# Adds a field to the bag
|
14
|
+
def field(field_name, **args, &block)
|
15
|
+
field_parser = Avo::Dsl::FieldParser.new(id: field_name, order_index: @items_index, **args, &block).parse
|
16
|
+
|
17
|
+
if field_parser.invalid?
|
18
|
+
as = args.fetch(:as, nil)
|
19
|
+
|
20
|
+
# End execution ehre and add the field to the invalid_fileds payload so we know to wanr the developer about that.
|
21
|
+
# @todo: Make sure this warning is still active
|
22
|
+
return add_invalid_field({
|
23
|
+
name: field_name,
|
24
|
+
as: as,
|
25
|
+
# resource: resource_class.name,
|
26
|
+
message: "There's an invalid field configuration for this resource. <br/> <code class='px-1 py-px rounded bg-red-600'>field :#{field_name}, as: #{as}</code>"
|
27
|
+
})
|
28
|
+
end
|
29
|
+
|
30
|
+
add_item field_parser.instance
|
31
|
+
end
|
32
|
+
|
33
|
+
def tabs(instance)
|
34
|
+
add_item instance
|
35
|
+
end
|
36
|
+
|
37
|
+
def tab(name, **args, &block)
|
38
|
+
add_item Avo::TabBuilder.parse_block(name: name, **args, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def tool(klass, **args)
|
42
|
+
instance = klass.new(**args)
|
43
|
+
add_item instance
|
44
|
+
end
|
45
|
+
|
46
|
+
def panel(panel_name = nil, **args, &block)
|
47
|
+
panel = Avo::PanelBuilder.parse_block(name: panel_name, **args, &block)
|
48
|
+
|
49
|
+
add_item panel
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_item(instance)
|
53
|
+
@items << instance
|
54
|
+
|
55
|
+
increment_order_index
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def add_invalid_field(payload)
|
61
|
+
@invalid_fields << payload
|
62
|
+
end
|
63
|
+
|
64
|
+
def increment_order_index
|
65
|
+
@items_index += 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/avo/licensing/h_q.rb
CHANGED
@@ -98,6 +98,7 @@ module Avo
|
|
98
98
|
**other_metadata(:filters),
|
99
99
|
main_menu_present: Avo.configuration.main_menu.present?,
|
100
100
|
profile_menu_present: Avo.configuration.profile_menu.present?,
|
101
|
+
**config_metadata
|
101
102
|
}
|
102
103
|
rescue
|
103
104
|
{}
|
@@ -178,6 +179,15 @@ module Avo
|
|
178
179
|
}
|
179
180
|
end
|
180
181
|
|
182
|
+
def config_metadata
|
183
|
+
{
|
184
|
+
config: {
|
185
|
+
root_path: Avo.configuration.root_path,
|
186
|
+
app_name: Avo.configuration.app_name
|
187
|
+
}
|
188
|
+
}
|
189
|
+
end
|
190
|
+
|
181
191
|
def cache_and_return_error(error, exception_message = "")
|
182
192
|
cache_response response: {error: error, exception_message: exception_message}.stringify_keys, time: 5.minutes.to_i
|
183
193
|
end
|
data/lib/avo/menu/builder.rb
CHANGED
@@ -5,6 +5,13 @@ class Avo::Menu::Builder
|
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
+
delegate :context, to: ::Avo::App
|
9
|
+
delegate :current_user, to: ::Avo::App
|
10
|
+
delegate :params, to: ::Avo::App
|
11
|
+
delegate :request, to: ::Avo::App
|
12
|
+
delegate :root_path, to: ::Avo::App
|
13
|
+
delegate :view_context, to: ::Avo::App
|
14
|
+
|
8
15
|
def initialize(name: nil, items: [])
|
9
16
|
@menu = Avo::Menu::Menu.new
|
10
17
|
|
@@ -60,7 +67,7 @@ class Avo::Menu::Builder
|
|
60
67
|
# Add all the tools
|
61
68
|
def all_tools(**args)
|
62
69
|
Avo::App.tools_for_navigation.each do |tool|
|
63
|
-
link tool.humanize, path:
|
70
|
+
link tool.humanize, path: root_path(paths: [tool])
|
64
71
|
end
|
65
72
|
end
|
66
73
|
|
@@ -68,10 +75,4 @@ class Avo::Menu::Builder
|
|
68
75
|
def build
|
69
76
|
@menu
|
70
77
|
end
|
71
|
-
|
72
|
-
protected
|
73
|
-
|
74
|
-
def root_path
|
75
|
-
Avo::App.root_path
|
76
|
-
end
|
77
78
|
end
|
data/lib/avo/panel.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class Avo::Panel
|
2
|
+
include Avo::Concerns::IsResourceItem
|
3
|
+
|
4
|
+
class_attribute :item_type, default: :panel
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
attr_reader :description
|
8
|
+
attr_accessor :items_holder
|
9
|
+
|
10
|
+
delegate :items, :add_item, to: :items_holder
|
11
|
+
|
12
|
+
def initialize(name: nil, description: nil)
|
13
|
+
@name = name
|
14
|
+
@description = description
|
15
|
+
@items_holder = Avo::ItemsHolder.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_item(item)
|
19
|
+
@items << item
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_items?
|
23
|
+
@items.present?
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Avo::PanelBuilder
|
2
|
+
class << self
|
3
|
+
def parse_block(**args, &block)
|
4
|
+
Docile.dsl_eval(new(**args), &block).build
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
delegate :field, to: :items_holder
|
9
|
+
delegate :items, to: :items_holder
|
10
|
+
|
11
|
+
attr_reader :items_holder
|
12
|
+
|
13
|
+
def initialize(name: nil, **args)
|
14
|
+
@panel = Avo::Panel.new(name: name, **args)
|
15
|
+
@items_holder = Avo::ItemsHolder.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Fetch the tab
|
19
|
+
def build
|
20
|
+
@panel.items_holder = @items_holder
|
21
|
+
@panel
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Avo
|
2
|
+
module Services
|
3
|
+
class URIService
|
4
|
+
class << self
|
5
|
+
def parse(path)
|
6
|
+
self.new path
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :uri
|
11
|
+
|
12
|
+
def initialize(path = '')
|
13
|
+
@uri = Addressable::URI.parse(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def append_paths(*paths)
|
17
|
+
paths = Array.wrap(paths).flatten
|
18
|
+
|
19
|
+
return self if paths.blank?
|
20
|
+
|
21
|
+
# Add the intermediary forward slash
|
22
|
+
@uri.path = @uri.path.concat("/") unless @uri.path.ends_with? "/"
|
23
|
+
|
24
|
+
# Add the paths to the URI
|
25
|
+
@uri.merge!(path: @uri.path.concat(join_paths(paths)))
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
alias_method :append_path, :append_paths
|
30
|
+
|
31
|
+
def append_query(params)
|
32
|
+
params = if params.is_a? Hash
|
33
|
+
params.map do |key, value|
|
34
|
+
"#{key}=#{value}"
|
35
|
+
end
|
36
|
+
else
|
37
|
+
{}
|
38
|
+
end
|
39
|
+
|
40
|
+
return self if params.blank?
|
41
|
+
|
42
|
+
# Add the query params to the URI
|
43
|
+
@uri.merge!(query: [@uri.query, *params].compact.join("&"))
|
44
|
+
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
@uri.to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def join_paths(paths)
|
55
|
+
paths.map do |path|
|
56
|
+
sanitize_path path
|
57
|
+
end
|
58
|
+
.join("/")
|
59
|
+
end
|
60
|
+
|
61
|
+
# Removes the forward slash if it's present at the start of the path
|
62
|
+
def sanitize_path(path)
|
63
|
+
if path.to_s.starts_with? '/'
|
64
|
+
path = path[1..-1]
|
65
|
+
end
|
66
|
+
|
67
|
+
path
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/avo/tab.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
class Avo::Tab
|
2
|
+
include Avo::Concerns::IsResourceItem
|
3
|
+
include Avo::Fields::FieldExtensions::VisibleInDifferentViews
|
4
|
+
|
5
|
+
class_attribute :item_type, default: :tab
|
6
|
+
delegate :items, :add_item, to: :items_holder
|
7
|
+
|
8
|
+
attr_reader :name
|
9
|
+
attr_reader :view
|
10
|
+
attr_accessor :description
|
11
|
+
attr_accessor :items_holder
|
12
|
+
attr_accessor :holds_one_field
|
13
|
+
|
14
|
+
def initialize(name: nil, description: nil, view: nil, holds_one_field: false, **args)
|
15
|
+
# Initialize the visibility markers
|
16
|
+
super
|
17
|
+
|
18
|
+
@name = name
|
19
|
+
@description = description
|
20
|
+
@holds_one_field = holds_one_field
|
21
|
+
@items_holder = Avo::ItemsHolder.new
|
22
|
+
@view = view
|
23
|
+
|
24
|
+
show_on args[:show_on] if args[:show_on].present?
|
25
|
+
hide_on args[:hide_on] if args[:hide_on].present?
|
26
|
+
only_on args[:only_on] if args[:only_on].present?
|
27
|
+
except_on args[:except_on] if args[:except_on].present?
|
28
|
+
end
|
29
|
+
|
30
|
+
def hydrate(view: nil)
|
31
|
+
@view = view
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def turbo_frame_id(parent: nil)
|
37
|
+
id = "#{Avo::Tab.to_s.parameterize} #{name}".parameterize
|
38
|
+
|
39
|
+
return id if parent.nil?
|
40
|
+
|
41
|
+
"#{parent.turbo_frame_id} #{id}".parameterize
|
42
|
+
end
|
43
|
+
|
44
|
+
def empty?
|
45
|
+
visible_items.blank?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Checks for visibility on itself or on theone field it holds
|
49
|
+
def visible_on?(view)
|
50
|
+
if holds_one_field
|
51
|
+
super(view) && items.first.visible_on?(view)
|
52
|
+
else
|
53
|
+
super(view)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def items
|
58
|
+
if self.items_holder.present?
|
59
|
+
self.items_holder.items
|
60
|
+
else
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def visible_items
|
66
|
+
items.map do |item|
|
67
|
+
if item.is_field?
|
68
|
+
visible = item.visible_on?(view)
|
69
|
+
# Remove the fields that shouldn't be visible in this view
|
70
|
+
# eg: has_many fields on edit
|
71
|
+
item = nil unless visible
|
72
|
+
end
|
73
|
+
|
74
|
+
item
|
75
|
+
end
|
76
|
+
.compact
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Avo::TabBuilder
|
2
|
+
class << self
|
3
|
+
def parse_block(**args, &block)
|
4
|
+
Docile.dsl_eval(new(**args), &block).build
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :items_holder
|
9
|
+
|
10
|
+
delegate :field, to: :items_holder
|
11
|
+
delegate :tool, to: :items_holder
|
12
|
+
delegate :panel, to: :items_holder
|
13
|
+
delegate :items, to: :items_holder
|
14
|
+
|
15
|
+
def initialize(name: nil, **args)
|
16
|
+
@tab = Avo::Tab.new(name: name, **args)
|
17
|
+
@items_holder = Avo::ItemsHolder.new
|
18
|
+
end
|
19
|
+
|
20
|
+
# Fetch the tab
|
21
|
+
def build
|
22
|
+
@tab.items_holder = @items_holder
|
23
|
+
@tab
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Avo::TabGroup
|
2
|
+
include Avo::Concerns::HasFields
|
3
|
+
include Avo::Concerns::IsResourceItem
|
4
|
+
|
5
|
+
class_attribute :item_type, default: :tab_group
|
6
|
+
|
7
|
+
attr_reader :view
|
8
|
+
attr_accessor :index
|
9
|
+
attr_accessor :items_holder
|
10
|
+
|
11
|
+
def initialize(index: 0, view: nil)
|
12
|
+
@index = index
|
13
|
+
@items_holder = Avo::ItemsHolder.new
|
14
|
+
@view = view
|
15
|
+
end
|
16
|
+
|
17
|
+
def hydrate(view: nil)
|
18
|
+
@view = view
|
19
|
+
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def visible_items
|
24
|
+
items.map do |item|
|
25
|
+
item.hydrate view: view
|
26
|
+
end
|
27
|
+
.select do |item|
|
28
|
+
# Remove items hidden in this view
|
29
|
+
item.visible_on? view
|
30
|
+
end
|
31
|
+
.select do |item|
|
32
|
+
# Remove empty items
|
33
|
+
!item.empty?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def turbo_frame_id
|
38
|
+
"#{Avo::TabGroup.to_s.parameterize} #{index}".parameterize
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Avo::TabGroupBuilder
|
2
|
+
class << self
|
3
|
+
def parse_block(&block)
|
4
|
+
Docile.dsl_eval(new, &block).build
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :items_holder
|
9
|
+
|
10
|
+
delegate :tab, to: :items_holder
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@group = Avo::TabGroup.new
|
14
|
+
@items_holder = Avo::ItemsHolder.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def field(field_name, **args, &block)
|
18
|
+
parsed = Avo::Dsl::FieldParser.new(id: field_name, order_index: @items_index, **args, &block).parse
|
19
|
+
field_instance = parsed.instance
|
20
|
+
|
21
|
+
name = field_instance.name
|
22
|
+
tab = Avo::Tab.new name: name
|
23
|
+
|
24
|
+
if field_instance.has_own_panel?
|
25
|
+
tab.items_holder.add_item parsed.instance
|
26
|
+
tab.holds_one_field = true
|
27
|
+
else
|
28
|
+
# If the field is not in a panel, create one and add it
|
29
|
+
panel = Avo::Panel.new name: name
|
30
|
+
panel.items_holder.add_item parsed.instance
|
31
|
+
# Add that panel to the bag
|
32
|
+
tab.items_holder.add_item panel
|
33
|
+
end
|
34
|
+
|
35
|
+
@items_holder.tabs tab
|
36
|
+
end
|
37
|
+
|
38
|
+
# Fetch the tab
|
39
|
+
def build
|
40
|
+
@group.items_holder = @items_holder
|
41
|
+
@group
|
42
|
+
end
|
43
|
+
end
|
data/lib/avo/version.rb
CHANGED
data/lib/avo.rb
CHANGED