avo 2.5.2.pre.5 → 2.6.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 +2 -0
- data/Gemfile.lock +4 -3
- data/app/assets/stylesheets/avo.css +2 -0
- data/app/assets/stylesheets/css/tags.css +16 -0
- data/app/components/avo/actions_component.html.erb +1 -1
- data/app/components/avo/button_component.rb +49 -18
- data/app/components/avo/card_component.rb +12 -0
- data/app/components/avo/fields/concerns/item_labels.rb +40 -0
- data/app/components/avo/fields/tags_field/edit_component.html.erb +27 -0
- data/app/components/avo/fields/tags_field/edit_component.rb +4 -0
- data/app/components/avo/fields/tags_field/index_component.html.erb +10 -0
- data/app/components/avo/fields/tags_field/index_component.rb +9 -0
- data/app/components/avo/fields/tags_field/show_component.html.erb +7 -0
- data/app/components/avo/fields/tags_field/show_component.rb +7 -0
- data/app/components/avo/fields/tags_field/tag_component.html.erb +9 -0
- data/app/components/avo/fields/tags_field/tag_component.rb +11 -0
- data/app/components/avo/index/resource_table_component.html.erb +1 -1
- data/app/components/avo/sidebar/group_component.html.erb +3 -3
- data/app/components/avo/sidebar/heading_component.html.erb +2 -2
- data/app/components/avo/sidebar/link_component.html.erb +1 -1
- data/app/components/avo/sidebar/link_component.rb +1 -1
- data/app/components/avo/sidebar_profile_component.html.erb +1 -1
- data/app/components/avo/views/resource_index_component.html.erb +2 -2
- data/app/components/avo/views/resource_show_component.html.erb +1 -1
- data/app/controllers/avo/base_controller.rb +0 -19
- data/app/controllers/avo/private_controller.rb +1 -0
- data/app/javascript/js/application.js +1 -1
- data/app/javascript/js/controllers/base_controller.js +22 -0
- data/app/javascript/js/controllers/fields/tags_field_controller.js +86 -0
- data/app/javascript/js/controllers/fields/tags_field_helpers.js +47 -0
- data/app/javascript/js/controllers/menu_controller.js +2 -2
- data/app/javascript/js/controllers.js +2 -0
- data/app/views/avo/dashboards/_chartkick_card.html.erb +1 -1
- data/app/views/avo/dashboards/_metric_card.html.erb +1 -1
- data/app/views/avo/partials/_navbar.html.erb +3 -3
- data/app/views/avo/private/_links_and_buttons.html.erb +1 -1
- data/app/views/layouts/avo/application.html.erb +1 -1
- data/db/factories.rb +2 -0
- data/lib/avo/base_resource.rb +6 -0
- data/lib/avo/concerns/handles_field_args.rb +36 -0
- data/lib/avo/fields/base_field.rb +2 -1
- data/lib/avo/fields/tags_field.rb +82 -0
- data/lib/avo/hosts/record_host.rb +7 -0
- data/lib/avo/licensing/pro_license.rb +2 -1
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/templates/cards/chartkick_card.tt +1 -1
- data/lib/generators/avo/templates/cards/chartkick_card_sample.tt +1 -1
- data/lib/generators/avo/templates/cards/metric_card.tt +1 -1
- data/lib/generators/avo/templates/cards/metric_card_sample.tt +1 -1
- data/lib/generators/avo/templates/locales/avo.en.yml +4 -0
- data/lib/tasks/avo_tasks.rake +7 -3
- data/public/avo-assets/avo.css +722 -71
- data/public/avo-assets/avo.js +211 -122
- data/public/avo-assets/avo.js.map +3 -3
- metadata +20 -7
- data/app/assets/builds/avo.css +0 -9032
- data/app/assets/builds/avo.js +0 -423
- data/app/assets/builds/avo.js.map +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 886b0051c7a9b11b46dcd99cdb419559f1b7326d8b8a90a3514d110935bf6d05
|
4
|
+
data.tar.gz: 4e21cf6d49133dc5551fdfea9759366bedfd3d487dcfd1df562a40b808383ef2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33608aef95479841514e639bd5e08c81f05a1fa4c73fa3067b59530774e7f11024a5f3e6f9c27e6a10125f9c6d9fef3684436b8b12deeb7c21d988ffa18379ed
|
7
|
+
data.tar.gz: ec6b40cda212f0a2ab04d0e672d099a5ebb672b2351cdfa0d8354c7b1ee3334704b5f128421e642d88da09b56fac98178ed7b7da93a370ee8cc53645419559df
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
avo (2.
|
4
|
+
avo (2.6.0)
|
5
5
|
active_link_to
|
6
6
|
addressable
|
7
7
|
breadcrumbs_on_rails
|
@@ -86,6 +86,8 @@ GEM
|
|
86
86
|
minitest (>= 5.1)
|
87
87
|
tzinfo (~> 2.0)
|
88
88
|
zeitwerk (~> 2.3)
|
89
|
+
acts-as-taggable-on (9.0.1)
|
90
|
+
activerecord (>= 6.0, < 7.1)
|
89
91
|
acts_as_list (1.0.4)
|
90
92
|
activerecord (>= 4.2)
|
91
93
|
addressable (2.8.0)
|
@@ -248,8 +250,6 @@ GEM
|
|
248
250
|
nokogiri (1.13.4)
|
249
251
|
mini_portile2 (~> 2.8.0)
|
250
252
|
racc (~> 1.4)
|
251
|
-
nokogiri (1.13.4-x86_64-linux)
|
252
|
-
racc (~> 1.4)
|
253
253
|
orm_adapter (0.5.0)
|
254
254
|
pagy (5.10.1)
|
255
255
|
activesupport
|
@@ -421,6 +421,7 @@ PLATFORMS
|
|
421
421
|
DEPENDENCIES
|
422
422
|
active_link_to
|
423
423
|
active_median
|
424
|
+
acts-as-taggable-on (~> 9.0)
|
424
425
|
acts_as_list
|
425
426
|
addressable
|
426
427
|
annotate
|
@@ -4,6 +4,7 @@
|
|
4
4
|
@import './../../../node_modules/trix/dist/trix.css';
|
5
5
|
@import './../../../node_modules/flatpickr/dist/flatpickr.css';
|
6
6
|
@import './../../../node_modules/@algolia/autocomplete-theme-classic/dist/theme.css';
|
7
|
+
@import './../../../node_modules/@yaireo/tagify/dist/tagify.css';
|
7
8
|
|
8
9
|
@import 'tailwindcss/base';
|
9
10
|
|
@@ -17,6 +18,7 @@
|
|
17
18
|
@import './css/search.css';
|
18
19
|
@import './css/active-storage.css';
|
19
20
|
@import './css/spinner.css';
|
21
|
+
@import './css/tags.css';
|
20
22
|
|
21
23
|
@import './css/components/status.css';
|
22
24
|
@import './css/components/code.css';
|
@@ -35,7 +35,7 @@
|
|
35
35
|
'actions-picker-target': action.standalone ? 'standaloneAction' : 'resourceAction',
|
36
36
|
'disabled': disabled,
|
37
37
|
},
|
38
|
-
class: "flex items-center px-4 py-
|
38
|
+
class: "flex items-center px-4 py-3 w-full font-semibold text-sm #{disabled ? 'text-gray-500' : 'text-black hover:bg-blue-500 hover:text-white'}" do %>
|
39
39
|
<%= svg 'play', class: 'h-5 mr-1 inline' %> <%= action.action_name %>
|
40
40
|
<% end %>
|
41
41
|
<% end %>
|
@@ -38,14 +38,15 @@ class Avo::ButtonComponent < ViewComponent::Base
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def button_classes
|
41
|
-
classes = "button-component inline-flex flex-grow-0 items-center
|
41
|
+
classes = "button-component inline-flex flex-grow-0 items-center font-semibold leading-6 fill-current whitespace-nowrap transition duration-100 transform transition duration-100 cursor-pointer disabled:cursor-not-allowed disabled:opacity-70 border justify-center active:outline active:outline-1 #{@class}"
|
42
42
|
|
43
43
|
classes += " rounded" if @rounded.present?
|
44
44
|
|
45
45
|
classes += style_classes
|
46
46
|
|
47
|
-
classes +=
|
48
|
-
classes +=
|
47
|
+
classes += horizontal_padding_classes
|
48
|
+
classes += vertical_padding_classes
|
49
|
+
classes += text_size_classes
|
49
50
|
|
50
51
|
classes
|
51
52
|
end
|
@@ -57,10 +58,14 @@ class Avo::ButtonComponent < ViewComponent::Base
|
|
57
58
|
def full_content
|
58
59
|
result = ""
|
59
60
|
icon_classes = @icon_class
|
60
|
-
|
61
|
+
# space out the icon from the text if text is present
|
62
|
+
icon_classes += " mr-1" if content.present?
|
63
|
+
# add the icon height
|
64
|
+
icon_classes += icon_size_classes
|
61
65
|
|
66
|
+
# Add the icon
|
62
67
|
result += helpers.svg(@icon, class: icon_classes) if @icon.present?
|
63
|
-
|
68
|
+
|
64
69
|
if content.present?
|
65
70
|
result += "<span>#{content}</span>"
|
66
71
|
end
|
@@ -96,21 +101,51 @@ class Avo::ButtonComponent < ViewComponent::Base
|
|
96
101
|
|
97
102
|
private
|
98
103
|
|
99
|
-
def
|
104
|
+
def vertical_padding_classes
|
100
105
|
case @size.to_sym
|
101
106
|
when :xs
|
102
107
|
" py-0"
|
103
108
|
when :sm
|
104
109
|
" py-1"
|
105
110
|
when :md
|
106
|
-
" py-
|
111
|
+
" py-1.5"
|
107
112
|
when :lg
|
113
|
+
" py-2"
|
114
|
+
when :xl
|
108
115
|
" py-3"
|
109
116
|
else
|
110
117
|
""
|
111
118
|
end
|
112
119
|
end
|
113
120
|
|
121
|
+
def horizontal_padding_classes
|
122
|
+
return " px-1" if @compact
|
123
|
+
|
124
|
+
case @size.to_sym
|
125
|
+
when :xs
|
126
|
+
" px-2"
|
127
|
+
when :sm
|
128
|
+
" px-3"
|
129
|
+
when :md
|
130
|
+
" px-3"
|
131
|
+
when :lg
|
132
|
+
" px-5"
|
133
|
+
when :xl
|
134
|
+
" px-6"
|
135
|
+
else
|
136
|
+
"px-4"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def text_size_classes
|
141
|
+
case @size.to_sym
|
142
|
+
when :xs
|
143
|
+
" text-xs"
|
144
|
+
else
|
145
|
+
" text-sm"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
114
149
|
def style_classes
|
115
150
|
case @style
|
116
151
|
when :primary
|
@@ -118,29 +153,25 @@ class Avo::ButtonComponent < ViewComponent::Base
|
|
118
153
|
when :outline
|
119
154
|
" bg-white text-#{@color}-500 border-#{@color}-500 hover:bg-#{@color}-100 active:bg-#{@color}-100 active:border-#{@color}-500 active:outline-#{@color}-500"
|
120
155
|
when :text
|
121
|
-
" text-#{@color}-500 active:outline-#{@color}-500
|
156
|
+
" text-#{@color}-500 active:outline-#{@color}-500 hover:bg-gray-100 border-transparent"
|
122
157
|
else
|
123
158
|
""
|
124
159
|
end
|
125
160
|
end
|
126
161
|
|
127
|
-
def
|
162
|
+
def icon_size_classes
|
128
163
|
icon_classes = ""
|
129
164
|
|
130
165
|
case @size
|
131
166
|
when :xs
|
132
|
-
icon_classes += " h-4"
|
133
|
-
# When icon is solo we need to add an offset
|
134
|
-
icon_classes += " my-1" if content.blank?
|
167
|
+
icon_classes += " h-4 my-1"
|
135
168
|
when :sm
|
136
|
-
icon_classes += " h-4"
|
137
|
-
# When icon is solo we need to add an offset
|
138
|
-
icon_classes += " my-1" if content.blank?
|
169
|
+
icon_classes += " h-4 my-1"
|
139
170
|
when :md
|
140
|
-
icon_classes += " h-
|
141
|
-
# When icon is solo we need to add an offset
|
142
|
-
icon_classes += " my-0.5" if content.blank?
|
171
|
+
icon_classes += " h-4 my-1"
|
143
172
|
when :lg
|
173
|
+
icon_classes += " h-5 my-0.5"
|
174
|
+
when :xl
|
144
175
|
icon_classes += " h-6"
|
145
176
|
end
|
146
177
|
|
@@ -3,9 +3,21 @@
|
|
3
3
|
class Avo::CardComponent < ViewComponent::Base
|
4
4
|
def initialize(card: nil)
|
5
5
|
@card = card
|
6
|
+
|
7
|
+
init_card
|
6
8
|
end
|
7
9
|
|
8
10
|
def render?
|
9
11
|
!@card.nil?
|
10
12
|
end
|
13
|
+
|
14
|
+
# Initializing the card byt running the query method.
|
15
|
+
# We'll still keep the query block around for compatibility reasons.
|
16
|
+
def init_card
|
17
|
+
if @card.respond_to? :query
|
18
|
+
@card.query
|
19
|
+
elsif @card.query_block.present?
|
20
|
+
@card.compute_result
|
21
|
+
end
|
22
|
+
end
|
11
23
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Avo
|
2
|
+
module Fields
|
3
|
+
module Concerns
|
4
|
+
module ItemLabels
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def value_for_item(item)
|
8
|
+
if @field.acts_as_taggable_on.present?
|
9
|
+
item["value"]
|
10
|
+
else
|
11
|
+
item
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def label_from_item(item)
|
16
|
+
value = value_for_item item
|
17
|
+
|
18
|
+
if suggestions_are_a_hash?
|
19
|
+
return suggestions_by_id[value.to_s][:label] if suggestions_by_id[value.to_s].present?
|
20
|
+
end
|
21
|
+
value
|
22
|
+
end
|
23
|
+
|
24
|
+
def suggestions_by_id
|
25
|
+
return {} unless suggestions_are_a_hash?
|
26
|
+
|
27
|
+
@field.suggestions.map do |suggestion|
|
28
|
+
[suggestion[:value].to_s, suggestion]
|
29
|
+
end.to_h
|
30
|
+
end
|
31
|
+
|
32
|
+
def suggestions_are_a_hash?
|
33
|
+
return false if @field.suggestions.blank?
|
34
|
+
|
35
|
+
@field.suggestions.first.is_a? Hash
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
|
2
|
+
<div data-controller="tags-field">
|
3
|
+
<%# dummy field %>
|
4
|
+
<%= text_field_tag "#{@field.id}-dummy", '',
|
5
|
+
class: helpers.input_classes('w-full', has_error: @field.model_errors.include?(@field.id)),
|
6
|
+
placeholder: @field.placeholder,
|
7
|
+
disabled: @field.readonly,
|
8
|
+
value: '',
|
9
|
+
data: {
|
10
|
+
'tags-field-target': 'fakeInput',
|
11
|
+
} %>
|
12
|
+
<%# real field %>
|
13
|
+
<%= @form.text_field @field.id,
|
14
|
+
class: helpers.input_classes('hidden w-full', has_error: @field.model_errors.include?(@field.id)),
|
15
|
+
placeholder: @field.placeholder,
|
16
|
+
disabled: @field.readonly,
|
17
|
+
value: @field.field_value.to_json,
|
18
|
+
data: {
|
19
|
+
'tags-field-target': 'input',
|
20
|
+
'whitelist-items': @field.suggestions.to_json,
|
21
|
+
'disallowed-items': @field.disallowed.to_json,
|
22
|
+
'enforce-suggestions': @field.enforce_suggestions ? 1 : 0,
|
23
|
+
'delimiters': @field.delimiters,
|
24
|
+
'close-on-select': @field.close_on_select ? 1 : 0,
|
25
|
+
} %>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<%= index_field_wrapper field: @field do %>
|
2
|
+
<div class="flex gap-1 items-center flex-nowrap">
|
3
|
+
<% value.take(3).each do |item| %>
|
4
|
+
<%= render Avo::Fields::TagsField::TagComponent.new(label: label_from_item(item)) %>
|
5
|
+
<% end %>
|
6
|
+
<% if value.count > 3 %>
|
7
|
+
<%= render Avo::Fields::TagsField::TagComponent.new(label: '...', title: I18n.t('avo.x_items_more', count: value.count - 3)) %>
|
8
|
+
<% end %>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<%= show_field_wrapper field: @field, index: @index do %>
|
2
|
+
<div class="flex gap-1 items-center flex-wrap">
|
3
|
+
<% @field.field_value.each do |item| %>
|
4
|
+
<%= render Avo::Fields::TagsField::TagComponent.new(label: label_from_item(item)) %>
|
5
|
+
<% end %>
|
6
|
+
</div>
|
7
|
+
<% end %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<div class="w-full ">
|
2
|
-
<table class="w-full px-4 bg-white
|
2
|
+
<table class="w-full px-4 bg-white" data-resource-name='<%= @resource.model_key %>' data-controller='item-select-all'>
|
3
3
|
<%= render partial: 'avo/partials/table_header', locals: {fields: @resource.get_fields(reflection: @reflection)} %>
|
4
4
|
<tbody class="divide-y">
|
5
5
|
<% @resources.each_with_index do |resource, index| %>
|
@@ -8,18 +8,18 @@
|
|
8
8
|
>
|
9
9
|
<% if item.name.present? %>
|
10
10
|
<div
|
11
|
-
class="flex justify-between px-10 pt-2 pb-0 text-gray-400"
|
11
|
+
class="flex justify-between px-10 pr-2 pt-2 pb-0 text-gray-400"
|
12
12
|
>
|
13
13
|
<div class="flex items-center text-xs uppercase font-semibold leading-none">
|
14
14
|
<%= item.name %>
|
15
15
|
</div>
|
16
16
|
<% if collapsable %>
|
17
|
-
<div class="cursor-pointer <%= 'rotate-
|
17
|
+
<div class="cursor-pointer <%= 'rotate-90' if collapsed %>"
|
18
18
|
data-action="click->menu#triggerCollapse"
|
19
19
|
data-menu-target="svg"
|
20
20
|
data-menu-key-param="<%= key %>"
|
21
21
|
>
|
22
|
-
<%= helpers.svg 'heroicons/outline/chevron-down', class: "h-4"%>
|
22
|
+
<%= helpers.svg 'heroicons/outline/chevron-down', class: "h-4 mr-0.5"%>
|
23
23
|
</div>
|
24
24
|
<% end %>
|
25
25
|
</div>
|
@@ -1,9 +1,9 @@
|
|
1
|
-
<div class="flex justify-between px-
|
1
|
+
<div class="flex justify-between px-4 pr-2 py-1 text-gray-500">
|
2
2
|
<div class="flex items-center text-sm uppercase font-semibold leading-none space-x-1">
|
3
3
|
<span class="min-w-[20px]"><%= icon %></span> <span><%= label %></span>
|
4
4
|
</div>
|
5
5
|
<% if collapsable %>
|
6
|
-
<div class="cursor-pointer <%= 'rotate-
|
6
|
+
<div class="cursor-pointer <%= 'rotate-90' if collapsed %>"
|
7
7
|
data-action="click->menu#triggerCollapse"
|
8
8
|
data-menu-key-param="<%= key %>"
|
9
9
|
data-menu-target="svg"
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<%= send link_method, path, class: classes, active: active, target: target do %>
|
3
3
|
<%= label %>
|
4
4
|
<% if target == :_blank %>
|
5
|
-
<%= helpers.svg('heroicons/outline/external-link', class: 'self-center ml-auto h-3') %>
|
5
|
+
<%= helpers.svg('heroicons/outline/external-link', class: 'self-center ml-auto h-3 mr-2') %>
|
6
6
|
<% end %>
|
7
7
|
<% end %>
|
8
8
|
<% else %>
|
@@ -23,6 +23,6 @@ class Avo::Sidebar::LinkComponent < ViewComponent::Base
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def classes
|
26
|
-
"px-4 flex-1 flex mx-6 leading-none py-2 text-black rounded font-medium hover:bg-gray-100"
|
26
|
+
"px-4 pr-0 flex-1 flex mx-6 leading-none py-2 text-black rounded font-medium hover:bg-gray-100"
|
27
27
|
end
|
28
28
|
end
|
@@ -59,14 +59,14 @@
|
|
59
59
|
<% c.bare_content do %>
|
60
60
|
<% if view_type.to_sym == :table %>
|
61
61
|
<% if @models.present? %>
|
62
|
-
<div class="mt-
|
62
|
+
<div class="mt-4">
|
63
63
|
<%= render Avo::PaginatorComponent.new pagy: @pagy, turbo_frame: @turbo_frame || 'none', index_params: @index_params, resource: @resource, parent_model: @parent_model %>
|
64
64
|
</div>
|
65
65
|
<% end %>
|
66
66
|
<% end %>
|
67
67
|
<% if view_type.to_sym == :grid %>
|
68
68
|
<%= render Avo::Index::ResourceGridComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model) %>
|
69
|
-
<div class="mt-
|
69
|
+
<div class="mt-6">
|
70
70
|
<%= render Avo::PaginatorComponent.new pagy: @pagy, turbo_frame: @turbo_frame || 'none', index_params: @index_params, resource: @resource, parent_model: @parent_model %>
|
71
71
|
</div>
|
72
72
|
<% end %>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<div data-model-id="<%= @resource.model.id %>"
|
2
2
|
data-selected-resources-name="<%= @resource.model_key %>"
|
3
3
|
data-selected-resources='["<%= @resource.model.id %>"]'
|
4
|
-
class="space-y-
|
4
|
+
class="space-y-12"
|
5
5
|
>
|
6
6
|
<% @resource.panels.each_with_index do |resource_panel, index| %>
|
7
7
|
<%= render Avo::PanelComponent.new(title: title, description: @resource.resource_description, display_breadcrumbs: @reflection.blank?, index: index) do |c| %>
|
@@ -11,8 +11,6 @@ module Avo
|
|
11
11
|
before_action :set_edit_title_and_breadcrumbs, only: [:edit, :update]
|
12
12
|
before_action :fill_model, only: [:create, :update]
|
13
13
|
before_action :authorize_action
|
14
|
-
# before_action :reset_pagination_if_filters_changed, only: :index
|
15
|
-
# before_action :cache_applied_filters, only: :index
|
16
14
|
|
17
15
|
def index
|
18
16
|
@page_title = @resource.plural_name.humanize
|
@@ -360,23 +358,6 @@ module Avo
|
|
360
358
|
filter_defaults.merge(@applied_filters)
|
361
359
|
end
|
362
360
|
|
363
|
-
# Caching these so we know when the filters have changed so we reset the pagination
|
364
|
-
# def cache_applied_filters
|
365
|
-
# # puts ["Rails.session->", session].inspect
|
366
|
-
# session[:avo_applied_filters] = params[:filters]
|
367
|
-
# # ::Avo::App.cache_store.delete(applied_filters_cache_key) if params[:filters].nil?
|
368
|
-
|
369
|
-
# # ::Avo::App.cache_store.write(applied_filters_cache_key, params[:filters], expires_in: 1.day)
|
370
|
-
# end
|
371
|
-
|
372
|
-
# def reset_pagination_if_filters_changed
|
373
|
-
# params[:page] = 1 if params[:filters] != session[:avo_applied_filters]
|
374
|
-
# end
|
375
|
-
|
376
|
-
# def applied_filters_cache_key
|
377
|
-
# "avo.base_controller.#{@resource.model_key}.applied_filters"
|
378
|
-
# end
|
379
|
-
|
380
361
|
def set_edit_title_and_breadcrumbs
|
381
362
|
@resource = @resource.hydrate(model: @model, view: :edit, user: _current_user)
|
382
363
|
@page_title = @resource.default_panel_name.to_s
|
@@ -4,7 +4,7 @@ import { Application } from '@hotwired/stimulus'
|
|
4
4
|
const application = Application.start()
|
5
5
|
|
6
6
|
// Configure Stimulus development experience
|
7
|
-
application.debug =
|
7
|
+
application.debug = window?.localStorage.getItem('avo.debug')
|
8
8
|
window.Stimulus = application
|
9
9
|
|
10
10
|
// Register stimulus-components controller
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
/**
|
5
|
+
* Helper that parses the data attribute value to JSON
|
6
|
+
*/
|
7
|
+
getJsonAttribute(target, attribute, defaultValue = []) {
|
8
|
+
let result = defaultValue
|
9
|
+
try {
|
10
|
+
result = JSON.parse(target.getAttribute(attribute))
|
11
|
+
} catch (error) {}
|
12
|
+
|
13
|
+
return result
|
14
|
+
}
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Parses the attribute to boolean
|
18
|
+
*/
|
19
|
+
getBooleanAttribute(target, attribute) {
|
20
|
+
return target.getAttribute(attribute) === '1'
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import { first, isObject, merge } from 'lodash'
|
2
|
+
import Tagify from '@yaireo/tagify'
|
3
|
+
|
4
|
+
import BaseController from '../base_controller'
|
5
|
+
|
6
|
+
import { suggestionItemTemplate, tagTemplate } from './tags_field_helpers'
|
7
|
+
|
8
|
+
export default class extends BaseController {
|
9
|
+
static targets = ['input', 'fakeInput'];
|
10
|
+
|
11
|
+
tagify = null;
|
12
|
+
|
13
|
+
get whitelistItems() {
|
14
|
+
return this.getJsonAttribute(this.inputTarget, 'data-whitelist-items', [])
|
15
|
+
}
|
16
|
+
|
17
|
+
get disallowedItems() {
|
18
|
+
return this.getJsonAttribute(this.inputTarget, 'data-disallowed-items', [])
|
19
|
+
}
|
20
|
+
|
21
|
+
get enforceSuggestions() {
|
22
|
+
return this.getBooleanAttribute(this.inputTarget, 'data-enforce-suggestions')
|
23
|
+
}
|
24
|
+
|
25
|
+
get closeOnSelect() {
|
26
|
+
return this.getBooleanAttribute(this.inputTarget, 'data-close-on-select')
|
27
|
+
}
|
28
|
+
|
29
|
+
get delimiters() {
|
30
|
+
return this.getJsonAttribute(this.inputTarget, 'data-delimiters', [])
|
31
|
+
}
|
32
|
+
|
33
|
+
get suggestionsAreObjects() {
|
34
|
+
return isObject(first(this.whitelistItems))
|
35
|
+
}
|
36
|
+
|
37
|
+
get tagifyOptions() {
|
38
|
+
let options = {
|
39
|
+
whitelist: this.whitelistItems,
|
40
|
+
blacklist: this.disallowedItems,
|
41
|
+
enforceWhitelist: this.enforceSuggestions,
|
42
|
+
delimiters: this.delimiters.join('|'),
|
43
|
+
maxTags: 10,
|
44
|
+
dropdown: {
|
45
|
+
maxItems: 20,
|
46
|
+
enabled: 0,
|
47
|
+
closeOnSelect: this.closeOnSelect,
|
48
|
+
},
|
49
|
+
}
|
50
|
+
|
51
|
+
if (this.suggestionsAreObjects) {
|
52
|
+
options = merge(options, {
|
53
|
+
tagTextProp: 'label',
|
54
|
+
dropdown: {
|
55
|
+
searchKeys: ['label'],
|
56
|
+
},
|
57
|
+
templates: {
|
58
|
+
tag: tagTemplate,
|
59
|
+
dropdownItem: suggestionItemTemplate,
|
60
|
+
},
|
61
|
+
})
|
62
|
+
}
|
63
|
+
|
64
|
+
return options
|
65
|
+
}
|
66
|
+
|
67
|
+
connect() {
|
68
|
+
if (this.hasInputTarget) {
|
69
|
+
this.hideFakeInput()
|
70
|
+
this.showRealInput()
|
71
|
+
this.initTagify()
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
initTagify() {
|
76
|
+
this.tagify = new Tagify(this.inputTarget, this.tagifyOptions)
|
77
|
+
}
|
78
|
+
|
79
|
+
hideFakeInput() {
|
80
|
+
this.fakeInputTarget.classList.add('hidden')
|
81
|
+
}
|
82
|
+
|
83
|
+
showRealInput() {
|
84
|
+
this.inputTarget.classList.remove('hidden')
|
85
|
+
}
|
86
|
+
}
|