advanced_select 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +814 -0
- data/Rakefile +4 -0
- data/app/assets/config/manifest.js +2 -0
- data/app/assets/stylesheets/advanced_select/advanced_select.css +220 -0
- data/app/javascript/advanced_select/advanced_select_controller.js +379 -0
- data/app/javascript/advanced_select/index.js +1 -0
- data/app/views/advanced_select/_default_option_content.html.erb +6 -0
- data/app/views/advanced_select/_option.html.erb +18 -0
- data/app/views/advanced_select/_options.html.erb +31 -0
- data/app/views/advanced_select/_select.html.erb +74 -0
- data/app/views/advanced_select/_summary.html.erb +14 -0
- data/config/locales/en.yml +7 -0
- data/config/locales/tr.yml +7 -0
- data/lib/advanced_select/class_map.rb +55 -0
- data/lib/advanced_select/engine.rb +28 -0
- data/lib/advanced_select/helper.rb +153 -0
- data/lib/advanced_select/version.rb +3 -0
- data/lib/advanced_select.rb +8 -0
- data/lib/generators/advanced_select/install/install_generator.rb +161 -0
- data/lib/generators/advanced_select/install/templates/advanced_select.css +220 -0
- data/lib/generators/advanced_select/install/templates/advanced_select_controller.js +379 -0
- data/lib/generators/advanced_select/option_content/option_content_generator.rb +11 -0
- data/lib/generators/advanced_select/option_content/templates/option_content.html.erb +8 -0
- data/lib/tasks/advanced_select/tasks.rake +9 -0
- metadata +139 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<div class="<%= advanced_select_class(class_map, :root) %>"
|
|
2
|
+
data-controller="advanced-select"
|
|
3
|
+
data-advanced-select-url-value="<%= options_url %>"
|
|
4
|
+
data-advanced-select-target-id-value="<%= target_id %>"
|
|
5
|
+
data-advanced-select-name-value="<%= name %>"
|
|
6
|
+
data-advanced-select-input-id-value="<%= id %>"
|
|
7
|
+
data-advanced-select-placeholder-value="<%= placeholder %>"
|
|
8
|
+
data-advanced-select-loading-text-value="<%= t("shared.advanced_select.loading") %>"
|
|
9
|
+
data-advanced-select-error-text-value="<%= t("shared.advanced_select.error") %>"
|
|
10
|
+
data-advanced-select-dependent-fields-value="<%= dependent_fields.to_json %>"
|
|
11
|
+
data-advanced-select-multiple-value="<%= multiple %>"
|
|
12
|
+
data-advanced-select-searchable-value="<%= searchable %>"
|
|
13
|
+
data-advanced-select-add-mode-value="<%= add_mode %>"
|
|
14
|
+
data-advanced-select-placeholder-class="<%= advanced_select_class(class_map, :placeholder) %>"
|
|
15
|
+
data-advanced-select-value-class="<%= advanced_select_class(class_map, :value) %>"
|
|
16
|
+
data-advanced-select-token-class="<%= advanced_select_class(class_map, :token) %>"
|
|
17
|
+
data-advanced-select-loading-class="<%= advanced_select_class(class_map, :loading) %>"
|
|
18
|
+
data-advanced-select-error-class="<%= advanced_select_class(class_map, :error) %>"
|
|
19
|
+
data-advanced-select-option-active-class="<%= advanced_select_state_class(class_map, :option_active) %>"
|
|
20
|
+
data-advanced-select-add-option-active-class="<%= advanced_select_state_class(class_map, :add_option_active) %>"
|
|
21
|
+
data-advanced-select-option-selected-class="<%= advanced_select_state_class(class_map, :option_selected) %>"
|
|
22
|
+
data-advanced-select-selected-value="<%= advanced_select_selected_value(selected_options) %>">
|
|
23
|
+
<div data-advanced-select-target="hiddenFields">
|
|
24
|
+
<% if multiple %>
|
|
25
|
+
<% selected_options.each do |option| %>
|
|
26
|
+
<%= hidden_field_tag name, option.fetch(:value, option.fetch(:id)) %>
|
|
27
|
+
<% end %>
|
|
28
|
+
<% else %>
|
|
29
|
+
<%= hidden_field_tag name, selected_options.first&.fetch(:value, selected_options.first&.fetch(:id)), id: id %>
|
|
30
|
+
<% end %>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<button type="button"
|
|
34
|
+
id="<%= "#{id}_trigger" %>"
|
|
35
|
+
class="<%= advanced_select_class(class_map, :trigger) %>"
|
|
36
|
+
aria-haspopup="listbox"
|
|
37
|
+
aria-expanded="false"
|
|
38
|
+
aria-controls="<%= "#{id}_dropdown" %>"
|
|
39
|
+
data-advanced-select-target="trigger"
|
|
40
|
+
data-action="advanced-select#toggle keydown->advanced-select#keydown">
|
|
41
|
+
<span id="<%= "#{id}_summary" %>" class="<%= advanced_select_class(class_map, :summary) %>" data-advanced-select-target="summary">
|
|
42
|
+
<%= render partial: "advanced_select/summary", locals: { selected_options: selected_options, multiple: multiple, placeholder: placeholder, class_map: class_map } %>
|
|
43
|
+
</span>
|
|
44
|
+
<span id="<%= "#{id}_caret" %>" class="<%= [advanced_select_class(class_map, :caret), ("hidden" if selected_options.any?)].compact.join(" ") %>" data-advanced-select-target="caret">⌄</span>
|
|
45
|
+
<span id="<%= "#{id}_clear" %>" class="<%= [advanced_select_class(class_map, :clear), ("hidden" if selected_options.empty?)].compact.join(" ") %>" data-advanced-select-target="clear" data-action="click->advanced-select#clear">×</span>
|
|
46
|
+
</button>
|
|
47
|
+
|
|
48
|
+
<div id="<%= "#{id}_dropdown" %>"
|
|
49
|
+
class="<%= [advanced_select_class(class_map, :dropdown), "hidden"].join(" ") %>"
|
|
50
|
+
data-advanced-select-target="dropdown">
|
|
51
|
+
<% if searchable %>
|
|
52
|
+
<%= tag.input type: "search",
|
|
53
|
+
id: "#{id}_search",
|
|
54
|
+
autocomplete: "off",
|
|
55
|
+
placeholder: placeholder,
|
|
56
|
+
class: advanced_select_class(class_map, :search),
|
|
57
|
+
data: {
|
|
58
|
+
advanced_select_target: "search",
|
|
59
|
+
action: "input->advanced-select#search keydown->advanced-select#keydown"
|
|
60
|
+
} %>
|
|
61
|
+
<% end %>
|
|
62
|
+
|
|
63
|
+
<%= render partial: "advanced_select/options", locals: {
|
|
64
|
+
target_id: target_id,
|
|
65
|
+
selected_options: selected_options,
|
|
66
|
+
options: advanced_select_options_for_render(options, selected_options, searchable),
|
|
67
|
+
multiple: multiple,
|
|
68
|
+
add_mode: add_mode,
|
|
69
|
+
query: nil,
|
|
70
|
+
option_content_partial: option_content_partial,
|
|
71
|
+
class_map: class_map
|
|
72
|
+
} %>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<% if selected_options.any? %>
|
|
2
|
+
<% if multiple %>
|
|
3
|
+
<% selected_options.first(2).each do |option| %>
|
|
4
|
+
<span class="<%= advanced_select_class(class_map, :token) %>"><%= option.fetch(:display_label) %></span>
|
|
5
|
+
<% end %>
|
|
6
|
+
<% if selected_options.size > 2 %>
|
|
7
|
+
<span class="<%= advanced_select_class(class_map, :token) %>"><%= "& +#{selected_options.size - 2}" %></span>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% else %>
|
|
10
|
+
<span class="<%= advanced_select_class(class_map, :value) %>"><%= selected_options.first.fetch(:display_label) %></span>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% else %>
|
|
13
|
+
<span class="<%= advanced_select_class(class_map, :placeholder) %>"><%= placeholder %></span>
|
|
14
|
+
<% end %>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module AdvancedSelect
|
|
2
|
+
class ClassMap
|
|
3
|
+
DEFAULTS = {
|
|
4
|
+
root: "ui-advanced-select",
|
|
5
|
+
trigger: "ui-advanced-select-trigger",
|
|
6
|
+
summary: "ui-advanced-select-summary",
|
|
7
|
+
placeholder: "ui-advanced-select-placeholder",
|
|
8
|
+
value: "ui-advanced-select-value",
|
|
9
|
+
token: "ui-advanced-select-token",
|
|
10
|
+
caret: "ui-advanced-select-caret",
|
|
11
|
+
clear: "ui-advanced-select-clear",
|
|
12
|
+
dropdown: "ui-advanced-select-dropdown",
|
|
13
|
+
search: "ui-advanced-select-search",
|
|
14
|
+
options: "ui-advanced-select-options",
|
|
15
|
+
option: "ui-advanced-select-option",
|
|
16
|
+
option_active: "ui-advanced-select-option-active",
|
|
17
|
+
option_selected: "",
|
|
18
|
+
option_check: "ui-advanced-select-option-check",
|
|
19
|
+
option_content: "ui-advanced-select-option-content",
|
|
20
|
+
option_description: "ui-advanced-select-option-description",
|
|
21
|
+
group_label: "ui-advanced-select-group-label",
|
|
22
|
+
add_option: "ui-advanced-select-add-option",
|
|
23
|
+
add_option_active: "",
|
|
24
|
+
empty: "ui-advanced-select-empty",
|
|
25
|
+
loading: "ui-advanced-select-loading",
|
|
26
|
+
error: "ui-advanced-select-error"
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
def initialize(classes = {}, append_classes = {})
|
|
30
|
+
@classes = normalize(classes)
|
|
31
|
+
@append_classes = normalize(append_classes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def class_name(*keys)
|
|
35
|
+
keys.compact.map { |key| class_for(key.to_sym) }.compact.reject(&:empty?).join(" ")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def state_class(key)
|
|
39
|
+
class_name(key)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def normalize(classes)
|
|
45
|
+
classes.to_h.each_with_object({}) do |(key, value), normalized|
|
|
46
|
+
class_name = value.to_s.squish
|
|
47
|
+
normalized[key.to_sym] = class_name if class_name.present?
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def class_for(key)
|
|
52
|
+
[@classes.fetch(key) { DEFAULTS[key] }, @append_classes[key]].compact.reject(&:empty?).join(" ")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module AdvancedSelect
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
initializer "advanced_select.helper" do
|
|
4
|
+
ActiveSupport.on_load(:action_view) do
|
|
5
|
+
include AdvancedSelect::Helper
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
initializer "advanced_select.assets" do |app|
|
|
10
|
+
if app.config.respond_to?(:assets)
|
|
11
|
+
javascript_path = root.join("app/javascript")
|
|
12
|
+
controller_asset = "advanced_select/advanced_select_controller.js"
|
|
13
|
+
|
|
14
|
+
unless app.config.assets.paths.map(&:to_s).include?(javascript_path.to_s)
|
|
15
|
+
app.config.assets.paths << javascript_path
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
unless app.config.assets.precompile.include?(controller_asset)
|
|
19
|
+
app.config.assets.precompile << controller_asset
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
rake_tasks do
|
|
25
|
+
load root.join("lib/tasks/advanced_select/tasks.rake")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
module AdvancedSelect
|
|
2
|
+
module Helper
|
|
3
|
+
def advanced_select_tag(name, id:, selected:, options:, placeholder:, options_url: nil, multiple: false, searchable: true, add_mode: false, dependent_fields: {}, option_content_partial: nil, classes: {}, append_classes: {})
|
|
4
|
+
selected_options = advanced_select_selected_options(selected)
|
|
5
|
+
class_map = advanced_select_class_map(classes, append_classes)
|
|
6
|
+
|
|
7
|
+
render partial: "advanced_select/select", locals: {
|
|
8
|
+
name: name,
|
|
9
|
+
id: id,
|
|
10
|
+
options_url: options_url,
|
|
11
|
+
selected_options: selected_options,
|
|
12
|
+
options: options,
|
|
13
|
+
placeholder: placeholder,
|
|
14
|
+
multiple: multiple,
|
|
15
|
+
searchable: searchable && options_url.present?,
|
|
16
|
+
add_mode: add_mode,
|
|
17
|
+
dependent_fields: dependent_fields,
|
|
18
|
+
target_id: "#{id}_options",
|
|
19
|
+
option_content_partial: option_content_partial,
|
|
20
|
+
class_map: class_map
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def advanced_select_options_tag(target_id:, selected:, options:, multiple: false, add_mode: false, query: nil, option_content_partial: nil, classes: {}, append_classes: {})
|
|
25
|
+
render partial: "advanced_select/options", locals: {
|
|
26
|
+
target_id: target_id,
|
|
27
|
+
selected_options: advanced_select_selected_options(selected),
|
|
28
|
+
options: options,
|
|
29
|
+
multiple: multiple,
|
|
30
|
+
add_mode: add_mode,
|
|
31
|
+
query: query,
|
|
32
|
+
option_content_partial: option_content_partial,
|
|
33
|
+
class_map: advanced_select_class_map(classes, append_classes)
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def advanced_select_class(class_map, *keys)
|
|
38
|
+
class_map.class_name(*keys)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def advanced_select_state_class(class_map, key)
|
|
42
|
+
class_map.state_class(key)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def advanced_select_selected_options(selected)
|
|
46
|
+
advanced_select_array(selected).map do |option|
|
|
47
|
+
{
|
|
48
|
+
id: option.fetch(:id).to_s,
|
|
49
|
+
value: advanced_select_option_value(option),
|
|
50
|
+
label: advanced_select_option_label(option),
|
|
51
|
+
display_label: advanced_select_option_display_label(option)
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def advanced_select_selected_value(selected_options)
|
|
57
|
+
selected_options.map do |option|
|
|
58
|
+
option.merge(displayLabel: option.fetch(:display_label))
|
|
59
|
+
end.to_json
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def advanced_select_options_for_render(options, selected_options, searchable)
|
|
63
|
+
searchable ? selected_options.presence || options : options
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def advanced_select_add_option?(options, selected_options, add_mode, query)
|
|
67
|
+
return false unless add_mode && query.present?
|
|
68
|
+
|
|
69
|
+
query_label = advanced_select_normalized_label(query)
|
|
70
|
+
advanced_select_matched_labels(options, selected_options).none? do |label|
|
|
71
|
+
advanced_select_normalized_label(label) == query_label
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def advanced_select_option_groups(options)
|
|
76
|
+
options.map do |option|
|
|
77
|
+
if option.key?(:options)
|
|
78
|
+
{ label: option.fetch(:label), options: option.fetch(:options) }
|
|
79
|
+
else
|
|
80
|
+
{ label: nil, options: [option] }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def advanced_select_option_selected?(option, selected_options)
|
|
86
|
+
selected_options.any? { |selected_option| selected_option.fetch(:id).to_s == option.fetch(:id).to_s }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def advanced_select_options_empty?(options)
|
|
90
|
+
advanced_select_flat_options(options).empty?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def advanced_select_option_label(option)
|
|
94
|
+
option.fetch(:label, option.fetch(:display_label, option.fetch(:value, option.fetch(:id)))).to_s
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def advanced_select_option_value(option)
|
|
98
|
+
option.fetch(:value, option.fetch(:id)).to_s
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def advanced_select_option_display_label(option)
|
|
102
|
+
option.fetch(:display_label, advanced_select_display_label(advanced_select_option_label(option))).to_s
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def advanced_select_option_description(option)
|
|
106
|
+
option[:description].to_s
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def advanced_select_display_label(label)
|
|
110
|
+
label.to_s.split(" > ").last
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def advanced_select_array(value)
|
|
114
|
+
case value
|
|
115
|
+
when nil
|
|
116
|
+
[]
|
|
117
|
+
when Array
|
|
118
|
+
value.compact
|
|
119
|
+
else
|
|
120
|
+
[value]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def advanced_select_matched_labels(options, selected_options)
|
|
125
|
+
option_labels = advanced_select_flat_options(options).flat_map do |option|
|
|
126
|
+
label = advanced_select_option_label(option)
|
|
127
|
+
[label, advanced_select_option_display_label(option)]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
selected_labels = selected_options.flat_map do |option|
|
|
131
|
+
[advanced_select_option_label(option), advanced_select_option_display_label(option)]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
option_labels + selected_labels
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def advanced_select_normalized_label(label)
|
|
138
|
+
I18n.transliterate(label.to_s.squish).downcase
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def advanced_select_flat_options(options)
|
|
142
|
+
options.flat_map { |option| option.key?(:options) ? option.fetch(:options) : option }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def advanced_select_class_map(classes, append_classes = {})
|
|
146
|
+
if classes.is_a?(AdvancedSelect::ClassMap)
|
|
147
|
+
classes
|
|
148
|
+
else
|
|
149
|
+
AdvancedSelect::ClassMap.new(classes, append_classes)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
module AdvancedSelect
|
|
2
|
+
module Generators
|
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
|
4
|
+
VALID_SETUPS = %w[importmap jsbundling].freeze
|
|
5
|
+
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
class_option :setup,
|
|
8
|
+
type: :string,
|
|
9
|
+
default: "importmap",
|
|
10
|
+
desc: "Host app setup: importmap or jsbundling"
|
|
11
|
+
|
|
12
|
+
def validate_options
|
|
13
|
+
return if VALID_SETUPS.include?(setup)
|
|
14
|
+
|
|
15
|
+
raise Thor::Error, "Invalid --setup=#{setup.inspect}. Expected one of: #{VALID_SETUPS.join(', ')}."
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def install_stimulus_controller
|
|
19
|
+
case setup
|
|
20
|
+
when "importmap"
|
|
21
|
+
pin_importmap_controller
|
|
22
|
+
when "jsbundling"
|
|
23
|
+
copy_file "advanced_select_controller.js", "app/javascript/controllers/advanced_select_controller.js"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def install_stylesheet
|
|
28
|
+
if setup == "jsbundling"
|
|
29
|
+
copy_file "advanced_select.css", "app/assets/stylesheets/advanced_select.css"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def register_stimulus_controller
|
|
34
|
+
case setup
|
|
35
|
+
when "importmap"
|
|
36
|
+
register_importmap_controller
|
|
37
|
+
when "jsbundling"
|
|
38
|
+
register_manifest_controller
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def import_stylesheet
|
|
43
|
+
case setup
|
|
44
|
+
when "importmap"
|
|
45
|
+
import_application_stylesheet
|
|
46
|
+
when "jsbundling"
|
|
47
|
+
append_postcss_import
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def show_next_steps
|
|
52
|
+
say "AdvancedSelect installed.", :green
|
|
53
|
+
say "Setup: #{setup}"
|
|
54
|
+
say "Host apps still own routes, controllers, queries, and Turbo Stream option endpoints."
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def pin_importmap_controller
|
|
60
|
+
path = "config/importmap.rb"
|
|
61
|
+
unless File.exist?(target_path(path))
|
|
62
|
+
say "Could not find #{path}; pin advanced_select/advanced_select_controller manually."
|
|
63
|
+
return
|
|
64
|
+
end
|
|
65
|
+
return if file_contains?(path, 'pin "advanced_select/advanced_select_controller"')
|
|
66
|
+
|
|
67
|
+
append_to_file path, <<~RUBY
|
|
68
|
+
pin "advanced_select/advanced_select_controller", to: "advanced_select/advanced_select_controller.js"
|
|
69
|
+
RUBY
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def register_importmap_controller
|
|
73
|
+
path = "app/javascript/controllers/index.js"
|
|
74
|
+
unless File.exist?(target_path(path))
|
|
75
|
+
raise Thor::Error, "Could not find #{path} for --setup=importmap."
|
|
76
|
+
end
|
|
77
|
+
return if file_contains?(path, "advanced-select")
|
|
78
|
+
|
|
79
|
+
append_to_file path, <<~JS
|
|
80
|
+
|
|
81
|
+
import AdvancedSelectController from "advanced_select/advanced_select_controller"
|
|
82
|
+
application.register("advanced-select", AdvancedSelectController)
|
|
83
|
+
JS
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def register_manifest_controller
|
|
87
|
+
unless File.exist?(target_path("app/javascript/controllers/index.js"))
|
|
88
|
+
raise Thor::Error, "Could not find app/javascript/controllers/index.js for --setup=jsbundling."
|
|
89
|
+
end
|
|
90
|
+
return if file_contains?("app/javascript/controllers/index.js", "advanced-select")
|
|
91
|
+
|
|
92
|
+
append_to_file "app/javascript/controllers/index.js", <<~JS
|
|
93
|
+
|
|
94
|
+
import AdvancedSelectController from "./advanced_select_controller"
|
|
95
|
+
application.register("advanced-select", AdvancedSelectController)
|
|
96
|
+
JS
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def append_postcss_import
|
|
100
|
+
path = "app/assets/stylesheets/application.postcss.css"
|
|
101
|
+
unless File.exist?(target_path(path))
|
|
102
|
+
raise Thor::Error, "Could not find #{path} for --setup=jsbundling."
|
|
103
|
+
end
|
|
104
|
+
return if file_contains?(path, "advanced_select.css")
|
|
105
|
+
|
|
106
|
+
insert_css_import(path)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def import_application_stylesheet
|
|
110
|
+
path = "app/assets/stylesheets/application.css"
|
|
111
|
+
unless File.exist?(target_path(path))
|
|
112
|
+
say "Could not find #{path}; require advanced_select/advanced_select through your host app stylesheet entrypoint."
|
|
113
|
+
return
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
unless sprockets_manifest?(path) && normalize_sprockets_requires(path)
|
|
117
|
+
say "Could not safely patch #{path}; require advanced_select/advanced_select through your host app stylesheet entrypoint."
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def file_contains?(path, content)
|
|
122
|
+
File.exist?(target_path(path)) && File.read(target_path(path)).include?(content)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def sprockets_manifest?(path)
|
|
126
|
+
content = File.read(target_path(path))
|
|
127
|
+
content.include?("/*") && content.include?("*/") && content.include?("*=")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def normalize_sprockets_requires(path)
|
|
131
|
+
lines = File.readlines(target_path(path))
|
|
132
|
+
lines.reject! { |line| line.match?(%r{^\s*\*=\s*require\s+advanced_select/advanced_select\s*$}) }
|
|
133
|
+
|
|
134
|
+
insert_at = lines.index { |line| line.match?(%r{^\s*\*=\s*require_tree\s+\.\s*$}) }
|
|
135
|
+
insert_at ||= lines.index { |line| line.match?(%r{^\s*\*=\s*require_self\s*$}) }
|
|
136
|
+
insert_at ||= lines.index { |line| line.strip == "*/" }
|
|
137
|
+
return false unless insert_at
|
|
138
|
+
|
|
139
|
+
lines.insert(insert_at, " *= require advanced_select/advanced_select\n")
|
|
140
|
+
File.write(target_path(path), lines.join)
|
|
141
|
+
true
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def insert_css_import(path)
|
|
145
|
+
lines = File.readlines(target_path(path))
|
|
146
|
+
import_index = lines.rindex { |line| line.strip.start_with?("@import") }
|
|
147
|
+
lines.insert((import_index || -1) + 1, "@import \"advanced_select.css\";\n")
|
|
148
|
+
|
|
149
|
+
File.write(target_path(path), lines.join)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def setup
|
|
153
|
+
options[:setup].to_s
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def target_path(path)
|
|
157
|
+
File.join(destination_root, path)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|