avo 1.20.2.pre.2 → 1.22.0.pre.1
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 +57 -57
- data/README.md +1 -1
- data/app/assets/builds/avo.css +405 -224
- data/app/assets/builds/avo.js +16 -6
- data/app/assets/builds/avo.js.map +2 -2
- data/app/assets/stylesheets/avo.css +1 -1
- data/app/assets/stylesheets/css/fonts.css +53 -0
- data/app/assets/stylesheets/css/pagination.css +9 -9
- data/app/assets/stylesheets/css/search.css +3 -2
- data/app/assets/svgs/dashboards-icon.svg +6 -0
- data/app/assets/svgs/resources-icon.svg +13 -0
- data/app/assets/svgs/three-dots.svg +5 -0
- data/app/assets/svgs/tools-icon.svg +3 -0
- data/app/components/avo/button_component.html.erb +1 -0
- data/app/components/avo/button_component.rb +111 -0
- data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +14 -7
- data/app/components/avo/fields/belongs_to_field/autocomplete_component.rb +47 -3
- data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +59 -38
- data/app/components/avo/fields/belongs_to_field/edit_component.rb +37 -0
- data/app/components/avo/fields/boolean_field/index_component.html.erb +1 -1
- data/app/components/avo/index/field_wrapper_component.html.erb +8 -2
- data/app/components/avo/index/field_wrapper_component.rb +2 -1
- data/app/components/avo/index/resource_table_component.html.erb +3 -3
- data/app/components/avo/index/table_row_component.html.erb +1 -1
- data/app/components/avo/navigation_heading_component.html.erb +2 -2
- data/app/components/avo/navigation_heading_component.rb +5 -1
- data/app/components/avo/navigation_link_component.html.erb +2 -2
- data/app/components/avo/navigation_link_component.rb +5 -0
- data/app/components/avo/panel_component.html.erb +5 -3
- data/app/components/avo/sidebar_profile_component.html.erb +28 -0
- data/app/components/avo/sidebar_profile_component.rb +43 -0
- data/app/components/avo/views/resource_index_component.html.erb +8 -7
- data/app/components/avo/views/resource_show_component.html.erb +10 -0
- data/app/controllers/avo/application_controller.rb +14 -12
- data/app/controllers/avo/base_controller.rb +4 -1
- data/app/helpers/avo/application_helper.rb +7 -5
- data/app/helpers/avo/resources_helper.rb +2 -2
- data/app/javascript/js/controllers/fields/date_field_controller.js +10 -2
- data/app/javascript/js/controllers/item_selector_controller.js +29 -19
- data/app/javascript/js/controllers/search_controller.js +8 -3
- data/app/views/avo/base/_actions.html.erb +4 -3
- data/app/views/avo/base/_filters.html.erb +3 -1
- data/app/views/avo/partials/_global_search.html.erb +2 -2
- data/app/views/avo/partials/_logo.html.erb +3 -1
- data/app/views/avo/partials/_navbar.html.erb +11 -0
- data/app/views/avo/partials/_paginator.html.erb +4 -3
- data/app/views/avo/partials/_resource_search.html.erb +1 -1
- data/app/views/avo/partials/_sidebar.html.erb +35 -0
- data/app/views/avo/partials/_table_header.html.erb +3 -3
- data/app/views/avo/partials/_turbo_frame_wrap.html.erb +7 -1
- data/app/views/avo/partials/_view_toggle_button.html.erb +22 -16
- data/app/views/layouts/avo/application.html.erb +3 -13
- data/db/factories.rb +7 -3
- data/lib/avo/app.rb +4 -3
- data/lib/avo/base_resource.rb +16 -14
- data/lib/avo/version.rb +1 -1
- data/public/avo-assets/avo.css +405 -224
- data/public/avo-assets/avo.js +16 -6
- data/public/avo-assets/avo.js.map +2 -2
- data/public/avo-assets/fonts/inter-v7-latin-500.eot +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-500.svg +351 -0
- data/public/avo-assets/fonts/inter-v7-latin-500.ttf +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-500.woff +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-500.woff2 +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-600.eot +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-600.svg +351 -0
- data/public/avo-assets/fonts/inter-v7-latin-600.ttf +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-600.woff +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-600.woff2 +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-700.eot +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-700.svg +352 -0
- data/public/avo-assets/fonts/inter-v7-latin-700.ttf +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-700.woff +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-700.woff2 +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-regular.eot +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-regular.svg +351 -0
- data/public/avo-assets/fonts/inter-v7-latin-regular.ttf +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-regular.woff +0 -0
- data/public/avo-assets/fonts/inter-v7-latin-regular.woff2 +0 -0
- data/public/avo-assets/logo.png +0 -0
- metadata +32 -5
- data/app/assets/images/avo/logo.png +0 -0
- data/app/views/avo/partials/_profile_dropdown.html.erb +0 -25
- data/app/views/avo/sidebar/_sidebar.html.erb +0 -33
@@ -24,3 +24,56 @@
|
|
24
24
|
url('/avo-assets/fonts/nunito-v16-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */
|
25
25
|
url('/avo-assets/fonts/nunito-v16-latin-700.svg#Nunito') format('svg'); /* Legacy iOS */
|
26
26
|
}
|
27
|
+
|
28
|
+
/* inter-regular - latin */
|
29
|
+
@font-face {
|
30
|
+
font-family: 'Inter';
|
31
|
+
font-style: normal;
|
32
|
+
font-weight: 400;
|
33
|
+
src: url('/avo-assets/fonts/inter-v7-latin-regular.eot'); /* IE9 Compat Modes */
|
34
|
+
src: local(''),
|
35
|
+
url('/avo-assets/fonts/inter-v7-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
36
|
+
url('/avo-assets/fonts/inter-v7-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
|
37
|
+
url('/avo-assets/fonts/inter-v7-latin-regular.woff') format('woff'), /* Modern Browsers */
|
38
|
+
url('/avo-assets/fonts/inter-v7-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
|
39
|
+
url('/avo-assets/fonts/inter-v7-latin-regular.svg#Inter') format('svg'); /* Legacy iOS */
|
40
|
+
}
|
41
|
+
/* inter-500 - latin */
|
42
|
+
@font-face {
|
43
|
+
font-family: 'Inter';
|
44
|
+
font-style: normal;
|
45
|
+
font-weight: 500;
|
46
|
+
src: url('/avo-assets/fonts/inter-v7-latin-500.eot'); /* IE9 Compat Modes */
|
47
|
+
src: local(''),
|
48
|
+
url('/avo-assets/fonts/inter-v7-latin-500.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
49
|
+
url('/avo-assets/fonts/inter-v7-latin-500.woff2') format('woff2'), /* Super Modern Browsers */
|
50
|
+
url('/avo-assets/fonts/inter-v7-latin-500.woff') format('woff'), /* Modern Browsers */
|
51
|
+
url('/avo-assets/fonts/inter-v7-latin-500.ttf') format('truetype'), /* Safari, Android, iOS */
|
52
|
+
url('/avo-assets/fonts/inter-v7-latin-500.svg#Inter') format('svg'); /* Legacy iOS */
|
53
|
+
}
|
54
|
+
/* inter-600 - latin */
|
55
|
+
@font-face {
|
56
|
+
font-family: 'Inter';
|
57
|
+
font-style: normal;
|
58
|
+
font-weight: 600;
|
59
|
+
src: url('/avo-assets/fonts/inter-v7-latin-600.eot'); /* IE9 Compat Modes */
|
60
|
+
src: local(''),
|
61
|
+
url('/avo-assets/fonts/inter-v7-latin-600.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
62
|
+
url('/avo-assets/fonts/inter-v7-latin-600.woff2') format('woff2'), /* Super Modern Browsers */
|
63
|
+
url('/avo-assets/fonts/inter-v7-latin-600.woff') format('woff'), /* Modern Browsers */
|
64
|
+
url('/avo-assets/fonts/inter-v7-latin-600.ttf') format('truetype'), /* Safari, Android, iOS */
|
65
|
+
url('/avo-assets/fonts/inter-v7-latin-600.svg#Inter') format('svg'); /* Legacy iOS */
|
66
|
+
}
|
67
|
+
/* inter-700 - latin */
|
68
|
+
@font-face {
|
69
|
+
font-family: 'Inter';
|
70
|
+
font-style: normal;
|
71
|
+
font-weight: 700;
|
72
|
+
src: url('/avo-assets/fonts/inter-v7-latin-700.eot'); /* IE9 Compat Modes */
|
73
|
+
src: local(''),
|
74
|
+
url('/avo-assets/fonts/inter-v7-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
75
|
+
url('/avo-assets/fonts/inter-v7-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
|
76
|
+
url('/avo-assets/fonts/inter-v7-latin-700.woff') format('woff'), /* Modern Browsers */
|
77
|
+
url('/avo-assets/fonts/inter-v7-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */
|
78
|
+
url('/avo-assets/fonts/inter-v7-latin-700.svg#Inter') format('svg'); /* Legacy iOS */
|
79
|
+
}
|
@@ -21,18 +21,18 @@
|
|
21
21
|
.pagy-nav-js .page a,
|
22
22
|
.pagy-combo-nav-js .page a,
|
23
23
|
.pagy-combo-nav-js .pagy-combo-input a {
|
24
|
-
@apply rounded-lg px-3 py-1 text-sm text-gray-500 font-semibold bg-
|
24
|
+
@apply rounded-lg px-3 py-1 text-sm text-gray-500 font-semibold bg-white shadow-md;
|
25
25
|
|
26
26
|
&:focus{
|
27
27
|
@apply border-blue-300;
|
28
28
|
}
|
29
29
|
|
30
30
|
&:hover{
|
31
|
-
@apply bg-gray-
|
31
|
+
@apply bg-gray-200;
|
32
32
|
}
|
33
33
|
|
34
34
|
&:active{
|
35
|
-
@apply bg-gray-
|
35
|
+
@apply bg-gray-200;
|
36
36
|
}
|
37
37
|
}
|
38
38
|
|
@@ -42,26 +42,26 @@
|
|
42
42
|
.pagy-nav .page.next.disabled,
|
43
43
|
.pagy-nav-js .page.next.disabled,
|
44
44
|
.pagy-combo-nav-js .page.next.disabled {
|
45
|
-
@apply text-gray-
|
45
|
+
@apply text-gray-300 cursor-default;
|
46
46
|
|
47
47
|
&:hover {
|
48
|
-
@apply text-gray-
|
48
|
+
@apply text-gray-300 bg-white;
|
49
49
|
}
|
50
50
|
|
51
51
|
&:active {
|
52
|
-
@apply text-gray-
|
52
|
+
@apply text-gray-300 bg-white;
|
53
53
|
}
|
54
54
|
}
|
55
55
|
|
56
56
|
.pagy-nav .page.active,
|
57
57
|
.pagy-nav-js .page.active {
|
58
|
-
@apply text-white cursor-default bg-gray-
|
58
|
+
@apply text-white cursor-default bg-gray-500;
|
59
59
|
|
60
60
|
&:hover {
|
61
|
-
@apply text-white bg-gray-
|
61
|
+
@apply text-white bg-gray-500;
|
62
62
|
}
|
63
63
|
|
64
64
|
&:active {
|
65
|
-
@apply bg-gray-
|
65
|
+
@apply bg-gray-500 text-white;
|
66
66
|
}
|
67
67
|
}
|
@@ -1,13 +1,14 @@
|
|
1
1
|
:root {
|
2
2
|
--aa-primary-color: --tw-ring-color;
|
3
3
|
--aa-selected-color: --tw-ring-color;
|
4
|
-
--aa-primary-color-rgb:
|
4
|
+
--aa-primary-color-rgb: 117, 125, 138;
|
5
5
|
}
|
6
6
|
|
7
7
|
.global-search {
|
8
8
|
.aa-DetachedSearchButton:focus,
|
9
9
|
.aa-DetachedSearchButton {
|
10
10
|
border: none !important;
|
11
|
+
@apply text-gray-500;
|
11
12
|
}
|
12
13
|
}
|
13
14
|
|
@@ -17,7 +18,7 @@
|
|
17
18
|
}
|
18
19
|
|
19
20
|
.aa-DetachedSearchButton {
|
20
|
-
@apply rounded
|
21
|
+
@apply rounded border-gray-300 ;
|
21
22
|
}
|
22
23
|
}
|
23
24
|
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<path d="M8.4 2H2V8.4H8.4V2Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
3
|
+
<path d="M18 2H11.6V8.4H18V2Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
4
|
+
<path d="M8.4 11.6001H2V18.0001H8.4V11.6001Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
5
|
+
<path d="M18 11.6001H11.6V18.0001H18V11.6001Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
6
|
+
</svg>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<g clip-path="url(#clip0_47_764)">
|
3
|
+
<path d="M1.52386 18V11.1428H6.8572" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
4
|
+
<path d="M19.0476 18H0" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
5
|
+
<path d="M6.85718 18.0001V6.57153H12.1905" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
6
|
+
<path d="M17.5238 2H12.1905V18H17.5238V2Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
7
|
+
</g>
|
8
|
+
<defs>
|
9
|
+
<clipPath id="clip0_47_764">
|
10
|
+
<rect width="20" height="20" fill="white"/>
|
11
|
+
</clipPath>
|
12
|
+
</defs>
|
13
|
+
</svg>
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<path d="M17.5702 5.2533C18.0073 6.27384 18.1143 7.4055 17.8762 8.48986C17.6381 9.57423 17.0669 10.557 16.2425 11.3006C15.4182 12.0442 14.3819 12.5114 13.2788 12.6367C12.1757 12.7621 11.061 12.5394 10.0908 11.9997V11.9997L5.41832 17.4135C5.04279 17.789 4.53348 18 4.0024 18C3.47133 18 2.96201 17.789 2.58649 17.4135C2.21097 17.038 2 16.5287 2 15.9976C2 15.4665 2.21097 14.9572 2.58649 14.5817L8.00028 9.90916C7.46064 8.93895 7.23791 7.82427 7.36328 6.72119C7.48864 5.6181 7.95582 4.58183 8.69941 3.75746C9.443 2.93308 10.4258 2.36188 11.5101 2.12381C12.5945 1.88574 13.7262 1.99273 14.7467 2.4298L11.2486 5.91962L11.7233 8.2767L14.0804 8.75144L17.5702 5.2533Z" stroke="#757D8A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
3
|
+
</svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= output %>
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Avo::ButtonComponent < ViewComponent::Base
|
4
|
+
def initialize(size: :md, type: :secondary, icon: nil, is_link: false, href: nil, **args)
|
5
|
+
@size = size
|
6
|
+
@type = type
|
7
|
+
@icon = icon
|
8
|
+
@href = href
|
9
|
+
@is_link = is_link
|
10
|
+
@args = args || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def args
|
14
|
+
if @args[:spinner]
|
15
|
+
@args[:"data-controller"] = "loading-button"
|
16
|
+
end
|
17
|
+
|
18
|
+
@args
|
19
|
+
end
|
20
|
+
|
21
|
+
def label
|
22
|
+
'lalb'
|
23
|
+
end
|
24
|
+
|
25
|
+
def is_link?
|
26
|
+
@is_link
|
27
|
+
end
|
28
|
+
|
29
|
+
def the_content
|
30
|
+
result = ""
|
31
|
+
|
32
|
+
result += @icon if @icon.present?
|
33
|
+
result += content if content.present?
|
34
|
+
|
35
|
+
result.html_safe
|
36
|
+
end
|
37
|
+
|
38
|
+
def output
|
39
|
+
if is_link?
|
40
|
+
output_link
|
41
|
+
else
|
42
|
+
output_button
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def output_link
|
47
|
+
if content.empty?
|
48
|
+
link_to label, @href, **args
|
49
|
+
else
|
50
|
+
link_to @href, **args do
|
51
|
+
content
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def output_button
|
57
|
+
if content.empty?
|
58
|
+
button_tag label, **args
|
59
|
+
else
|
60
|
+
button_tag **args do
|
61
|
+
the_content
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
# args[:class] = button_classes(args[:class], color: args[:color], variant: args[:variant], size: args[:size])
|
74
|
+
# if args[:spinner]
|
75
|
+
# args[:"data-controller"] = "loading-button"
|
76
|
+
# end
|
77
|
+
|
78
|
+
# locals = {
|
79
|
+
# label: label,
|
80
|
+
# args: args
|
81
|
+
# }
|
82
|
+
|
83
|
+
# if block
|
84
|
+
# render layout: "avo/partials/a_button", locals: locals do
|
85
|
+
# capture(&block)
|
86
|
+
# end
|
87
|
+
# else
|
88
|
+
# render partial: "avo/partials/a_button", locals: locals
|
89
|
+
# end
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
# <% if is_link? %>
|
95
|
+
# <% if content.empty? %>
|
96
|
+
# <%= link_to label, @href, **args %>
|
97
|
+
# <% else %>
|
98
|
+
# <%= link_to @href, **args do %>
|
99
|
+
# <% end %>
|
100
|
+
# <% end %>
|
101
|
+
# <% else %>
|
102
|
+
# <% if content.empty? %>
|
103
|
+
# <%= button_tag label, **args %>
|
104
|
+
# <% else %>
|
105
|
+
# <%= button_tag **args do %>
|
106
|
+
# <%= the_content %>
|
107
|
+
# <% end %>
|
108
|
+
# <% end %>
|
109
|
+
# <% end %>
|
110
|
+
|
111
|
+
end
|
@@ -8,15 +8,22 @@
|
|
8
8
|
<div class="relative w-full" autocomplete="off">
|
9
9
|
<%= @form.text_field @field.foreign_key,
|
10
10
|
value: field_label,
|
11
|
-
class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
|
11
|
+
class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
|
12
12
|
placeholder: @field.placeholder,
|
13
|
+
'data-search-target': 'button clearValue',
|
14
|
+
# This instructs the search_controller if it should enable/disabled this field when the user switches polymorphic associations
|
15
|
+
# It should not enable the field if the record is being created through an association
|
16
|
+
'data-should-be-disabled': @disabled,
|
13
17
|
disabled: true %>
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
<% unless @disabled %>
|
19
|
+
<div class="absolute top-1/2 left-auto right-3 mr-px -mt-2 cursor-pointer hidden text-gray-500"
|
20
|
+
data-tippy="tooltip"
|
21
|
+
data-search-target="clearButton"
|
22
|
+
title="<%= I18n.translate 'avo.clear_value' %>"
|
23
|
+
data-action="click->search#clearValue"
|
24
|
+
><%= helpers.svg 'x', class: 'h-4' %>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
20
27
|
</div>
|
21
28
|
<%= @form.hidden_field @foreign_key, value: field_value, 'data-search-target': 'hiddenId clearValue' %>
|
22
29
|
</div>
|
@@ -1,17 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Avo::Fields::BelongsToField::AutocompleteComponent < ViewComponent::Base
|
4
|
-
def initialize(form:, field:, type: nil,
|
4
|
+
def initialize(form:, field:, model_key:, foreign_key:, disabled:, type: nil, resource: nil, polymorphic_record: nil)
|
5
5
|
@form = form
|
6
6
|
@field = field
|
7
7
|
@type = type
|
8
8
|
@model_key = model_key
|
9
9
|
@foreign_key = foreign_key
|
10
|
+
@resource = resource
|
11
|
+
@disabled = disabled
|
12
|
+
@polymorphic_record = polymorphic_record
|
10
13
|
end
|
11
14
|
|
12
15
|
def field_label
|
13
16
|
if searchable?
|
14
|
-
|
17
|
+
# New records won't have the value (instantiated model) present but the polymorphic_type and polymorphic_id prefilled
|
18
|
+
if new_record? && has_polymorphic_association?
|
19
|
+
@polymorphic_record.send(polymorphic_fields[:label])
|
20
|
+
else
|
21
|
+
@field.value&.class == @type ? @field.field_label : nil
|
22
|
+
end
|
15
23
|
else
|
16
24
|
@field.field_label
|
17
25
|
end
|
@@ -19,7 +27,12 @@ class Avo::Fields::BelongsToField::AutocompleteComponent < ViewComponent::Base
|
|
19
27
|
|
20
28
|
def field_value
|
21
29
|
if searchable?
|
22
|
-
|
30
|
+
# New records won't have the value (instantiated model) present but the polymorphic_type and polymorphic_id prefilled
|
31
|
+
if new_record? && has_polymorphic_association?
|
32
|
+
@polymorphic_record.send(polymorphic_fields[:id])
|
33
|
+
else
|
34
|
+
@field.value&.class == @type ? @field.field_value : nil
|
35
|
+
end
|
23
36
|
else
|
24
37
|
@field.field_value
|
25
38
|
end
|
@@ -30,4 +43,35 @@ class Avo::Fields::BelongsToField::AutocompleteComponent < ViewComponent::Base
|
|
30
43
|
def searchable?
|
31
44
|
@type.present?
|
32
45
|
end
|
46
|
+
|
47
|
+
def new_record?
|
48
|
+
@resource.model.new_record?
|
49
|
+
end
|
50
|
+
|
51
|
+
def has_polymorphic_association?
|
52
|
+
polymorphic_class.present? && polymorphic_id.present?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Get the polymorphic class
|
56
|
+
def polymorphic_class
|
57
|
+
@resource.model["#{@field.foreign_key}_type"]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get the polymorphic id
|
61
|
+
def polymorphic_id
|
62
|
+
@resource.model["#{@field.foreign_key}_id"]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Get the resource for that polymorphic class
|
66
|
+
def polymorphic_resource
|
67
|
+
::Avo::App.get_resource_by_model_name polymorphic_class
|
68
|
+
end
|
69
|
+
|
70
|
+
# Extract the needed fields to identify the record for polymorphic associations
|
71
|
+
def polymorphic_fields
|
72
|
+
{
|
73
|
+
id: polymorphic_resource.id,
|
74
|
+
label: polymorphic_resource.title
|
75
|
+
}
|
76
|
+
end
|
33
77
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<%
|
2
|
-
if
|
2
|
+
if is_polymorphic?
|
3
3
|
|
4
4
|
# Set the model keys so we can pass them over
|
5
5
|
model_keys = @field.types.map do |type|
|
@@ -7,58 +7,73 @@
|
|
7
7
|
[type.to_s, resource.model_key]
|
8
8
|
end.to_h
|
9
9
|
%>
|
10
|
-
|
10
|
+
<div data-controller="belongs-to-field"
|
11
11
|
data-searchable="<%= @field.searchable %>"
|
12
12
|
data-association="<%= @field.id %>"
|
13
13
|
data-association-class="<%= @field&.target_resource&.model_class || nil %>"
|
14
14
|
>
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
15
|
+
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
|
16
|
+
<%= @form.select "#{@field.foreign_key}_type", @field.types.map { |type| [type.to_s.underscore.humanize, type.to_s] },
|
17
|
+
{
|
18
|
+
value: @field.value,
|
19
|
+
include_blank: @field.placeholder,
|
20
|
+
},
|
21
|
+
{
|
22
|
+
class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
|
23
|
+
disabled: disabled,
|
24
|
+
'data-belongs-to-field-target': "select",
|
25
|
+
'data-action': 'change->belongs-to-field#changeType'
|
26
|
+
}
|
27
|
+
%>
|
28
|
+
<%
|
29
29
|
# If the select field is disabled, no value will be sent. It's how HTML works.
|
30
30
|
# Thus the extra hidden field to actually send the related id to the server.
|
31
31
|
if disabled %>
|
32
|
-
|
33
|
-
<% end %>
|
32
|
+
<%= @form.hidden_field "#{@field.foreign_key}_type" %>
|
34
33
|
<% end %>
|
35
|
-
|
36
|
-
|
34
|
+
<% end %>
|
35
|
+
<% @field.types.each do |type| %>
|
36
|
+
<div class="hidden"
|
37
37
|
data-belongs-to-field-target="type"
|
38
38
|
data-type="<%= type %>"
|
39
39
|
>
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
40
|
+
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal, label: type.to_s.underscore.humanize do %>
|
41
|
+
<% if @field.searchable %>
|
42
|
+
<%= render Avo::Fields::BelongsToField::AutocompleteComponent.new form: @form,
|
43
|
+
field: @field,
|
44
|
+
type: type,
|
45
|
+
model_key: model_keys[type.to_s],
|
46
|
+
foreign_key: "#{@field.foreign_key}_id",
|
47
|
+
resource: @resource,
|
48
|
+
disabled: disabled,
|
49
|
+
polymorphic_record: polymorphic_record
|
50
|
+
%>
|
51
|
+
<% else %>
|
52
|
+
<%= @form.select "#{@field.foreign_key}_id", options_for_select(@field.values_for_type(type), @resource.present? && @resource.model.present? ? @resource.model["#{@field.foreign_key}_id"] : nil),
|
53
|
+
{
|
54
|
+
value: @resource.model["#{@field.foreign_key}_id"].to_s,
|
55
|
+
include_blank: @field.placeholder,
|
56
|
+
},
|
57
|
+
{
|
58
|
+
class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
|
59
|
+
disabled: disabled
|
60
|
+
}
|
61
|
+
%>
|
54
62
|
<% end %>
|
55
|
-
|
56
|
-
|
57
|
-
|
63
|
+
<% end %>
|
64
|
+
</div>
|
65
|
+
<% end %>
|
66
|
+
</div>
|
58
67
|
<% else %>
|
59
68
|
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
|
60
69
|
<% if @field.searchable %>
|
61
|
-
<%= render Avo::Fields::BelongsToField::AutocompleteComponent.new form: @form,
|
70
|
+
<%= render Avo::Fields::BelongsToField::AutocompleteComponent.new form: @form,
|
71
|
+
field: @field,
|
72
|
+
model_key: @field.target_resource&.model_key,
|
73
|
+
foreign_key: @field.foreign_key,
|
74
|
+
resource: @resource,
|
75
|
+
disabled: disabled
|
76
|
+
%>
|
62
77
|
<% else %>
|
63
78
|
<%= @form.select @field.foreign_key, @field.options.map { |o| [o[:label], o[:value]] },
|
64
79
|
{
|
@@ -69,6 +84,12 @@
|
|
69
84
|
disabled: disabled
|
70
85
|
}
|
71
86
|
%>
|
87
|
+
<%
|
88
|
+
# If the select field is disabled, no value will be sent. It's how HTML works.
|
89
|
+
# Thus the extra hidden field to actually send the related id to the server.
|
90
|
+
if disabled %>
|
91
|
+
<%= @form.hidden_field @field.foreign_key %>
|
92
|
+
<% end %>
|
72
93
|
<% end %>
|
73
94
|
<% end %>
|
74
95
|
<% end %>
|
@@ -1,6 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Avo::Fields::BelongsToField::EditComponent < Avo::Fields::EditComponent
|
4
|
+
def initialize(field: nil, resource: nil, index: 0, form: nil, displayed_in_modal: false)
|
5
|
+
super field: field, resource: resource, index: index, form: form, displayed_in_modal: displayed_in_modal
|
6
|
+
|
7
|
+
@polymorphic_record = nil
|
8
|
+
end
|
9
|
+
|
4
10
|
def disabled
|
5
11
|
return true if @field.readonly
|
6
12
|
return true if @field.target_resource.present? && @field.target_resource.model_class.name == params[:via_resource_class]
|
@@ -8,4 +14,35 @@ class Avo::Fields::BelongsToField::EditComponent < Avo::Fields::EditComponent
|
|
8
14
|
|
9
15
|
false
|
10
16
|
end
|
17
|
+
|
18
|
+
def is_polymorphic?
|
19
|
+
@field.types.present?
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_polymorphic_association?
|
23
|
+
polymorphic_class.present? && polymorphic_id.present?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get the polymorphic class
|
27
|
+
def polymorphic_class
|
28
|
+
@resource.model["#{@field.foreign_key}_type"]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Get the polymorphic id
|
32
|
+
def polymorphic_id
|
33
|
+
@resource.model["#{@field.foreign_key}_id"]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get the actual resource
|
37
|
+
def polymorphic_record
|
38
|
+
return unless has_polymorphic_association?
|
39
|
+
|
40
|
+
return unless is_polymorphic?
|
41
|
+
|
42
|
+
return @polymorphic_record if @polymorphic_record.present?
|
43
|
+
|
44
|
+
@polymorphic_record = polymorphic_class.safe_constantize.find polymorphic_id
|
45
|
+
|
46
|
+
@polymorphic_record
|
47
|
+
end
|
11
48
|
end
|
@@ -1,7 +1,13 @@
|
|
1
|
-
<td class="px-4 py-
|
1
|
+
<td class="px-4 py-3 leading-tight whitespace-nowrap h-full text-slate-800 <%= @classes %>" data-field-id="<%= @field.id %>" data-field-type="<%= @field.type %>">
|
2
2
|
<% if @field.value.blank? && @dash_if_blank %>
|
3
3
|
—
|
4
4
|
<% else %>
|
5
|
-
|
5
|
+
<% if @center_content %>
|
6
|
+
<div class="flex items-center justify-center">
|
7
|
+
<%= content %>
|
8
|
+
</div>
|
9
|
+
<% else %>
|
10
|
+
<%= content %>
|
11
|
+
<% end %>
|
6
12
|
<% end %>
|
7
13
|
</td>
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Avo::Index::FieldWrapperComponent < ViewComponent::Base
|
4
|
-
def initialize(field: nil, dash_if_blank: true, **args)
|
4
|
+
def initialize(field: nil, dash_if_blank: true, center_content: false, **args)
|
5
5
|
@field = field
|
6
6
|
@dash_if_blank = dash_if_blank
|
7
|
+
@center_content = center_content
|
7
8
|
@classes = args[:class].present? ? args[:class] : ""
|
8
9
|
@args = args
|
9
10
|
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
<div class="w-full">
|
1
|
+
<div class="w-full ">
|
2
2
|
<% if @resources.present?%>
|
3
|
-
<table class="w-full px-4 overflow-hidden" data-resource-name='<%= @resource.model_key %>' data-controller='item-select-all'>
|
3
|
+
<table class="w-full px-4 overflow-hidden bg-white rounded shadow " data-resource-name='<%= @resource.model_key %>' data-controller='item-select-all'>
|
4
4
|
<%= render partial: 'avo/partials/table_header', locals: {fields: @resource.get_fields(reflection: @reflection)} %>
|
5
|
-
<tbody>
|
5
|
+
<tbody class="divide-y">
|
6
6
|
<% @resources.each_with_index do |resource, index| %>
|
7
7
|
<% cache_if Avo.configuration.cache_resources_on_index_view, resource.cache_hash(@parent_model) do %>
|
8
8
|
<%= render Avo::Index::TableRowComponent.new(resource: resource, reflection: @reflection, parent_model: @parent_model) %>
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<div class="flex items-center
|
2
|
-
|
1
|
+
<div class="flex items-center p-4 text-gray-500 text-sm uppercase font-semibold leading-none">
|
2
|
+
<%= icon %> <%= label %>
|
3
3
|
</div>
|