effective_bootstrap 0.9.9 → 0.9.14
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 +4 -4
- data/README.md +50 -2
- data/app/assets/javascripts/effective_bootstrap.js +2 -0
- data/app/assets/javascripts/effective_bootstrap/other.js.coffee +7 -0
- data/app/assets/javascripts/effective_has_many/initialize.js.coffee +77 -0
- data/app/assets/javascripts/effective_has_many/input.js +2 -0
- data/app/assets/javascripts/effective_has_many/jquery.sortable.js +696 -0
- data/app/assets/javascripts/effective_password/initialize.js.coffee +9 -0
- data/app/assets/javascripts/effective_password/input.js +1 -0
- data/app/assets/stylesheets/effective_bootstrap.scss +1 -0
- data/app/assets/stylesheets/effective_checks/input.scss +1 -1
- data/app/assets/stylesheets/effective_has_many/input.scss +42 -0
- data/app/helpers/effective_form_builder_helper.rb +2 -2
- data/app/models/effective/form_builder.rb +14 -0
- data/app/models/effective/form_inputs/checks.rb +19 -8
- data/app/models/effective/form_inputs/has_many.rb +207 -0
- data/app/models/effective/form_inputs/password_field.rb +16 -2
- data/lib/effective_bootstrap/version.rb +1 -1
- metadata +23 -2
@@ -0,0 +1,9 @@
|
|
1
|
+
# Prevent non-currency buttons from being pressed
|
2
|
+
$(document).on 'click', 'button[data-effective-password]', (event) ->
|
3
|
+
$obj = $(event.currentTarget)
|
4
|
+
$input = $obj.closest('.input-group')
|
5
|
+
|
6
|
+
$input.find('input').attr('type', $obj.data('effective-password'))
|
7
|
+
$input.find('button[data-effective-password]').toggle()
|
8
|
+
|
9
|
+
false
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require ./initialize
|
@@ -0,0 +1,42 @@
|
|
1
|
+
body.dragging,
|
2
|
+
body.dragging * {
|
3
|
+
cursor: move !important;
|
4
|
+
}
|
5
|
+
|
6
|
+
.form-has-many {
|
7
|
+
.has-many-placeholder {
|
8
|
+
position: relative;
|
9
|
+
height: 2rem;
|
10
|
+
|
11
|
+
&:before {
|
12
|
+
position: absolute;
|
13
|
+
content: '';
|
14
|
+
background-image: asset-data-url('icons/arrow-right-circle.svg');
|
15
|
+
background-repeat: no-repeat;
|
16
|
+
height: 2rem;
|
17
|
+
width: 2rem;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
.has-many-fields.dragged {
|
22
|
+
position: absolute;
|
23
|
+
opacity: 0;
|
24
|
+
z-index: 2000;
|
25
|
+
.has-many-move { display: none; }
|
26
|
+
}
|
27
|
+
|
28
|
+
.has-many-move svg { margin-top: 6px; }
|
29
|
+
.has-many-move { display: none; }
|
30
|
+
|
31
|
+
.has-many-remove { margin-top: 1.5rem; }
|
32
|
+
.has-many-move { margin-top: 1.5rem; }
|
33
|
+
}
|
34
|
+
|
35
|
+
.form-has-many.reordering {
|
36
|
+
.has-many-move { display: inline-block; }
|
37
|
+
}
|
38
|
+
|
39
|
+
.form-has-many.tight {
|
40
|
+
.has-many-remove { margin-top: 0; }
|
41
|
+
.has-many-move { margin-top: 0; }
|
42
|
+
}
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module EffectiveFormBuilderHelper
|
2
2
|
def effective_form_with(**options, &block)
|
3
3
|
# Compute the default ID
|
4
|
-
subject = Array(options[:
|
5
|
-
class_name = subject.class.name.parameterize.underscore
|
4
|
+
subject = Array(options[:model] || options[:scope]).last
|
5
|
+
class_name = (options[:scope] || subject.class.name.parameterize.underscore)
|
6
6
|
unique_id = options.except(:model).hash.abs
|
7
7
|
|
8
8
|
html_id = if subject.kind_of?(Symbol)
|
@@ -203,5 +203,19 @@ module Effective
|
|
203
203
|
Effective::FormLogics::ShowIfAny.new(*args, builder: self).to_html(&block)
|
204
204
|
end
|
205
205
|
|
206
|
+
# Has Many
|
207
|
+
def has_many(name, collection = nil, options = {}, &block)
|
208
|
+
association = object.class.reflect_on_all_associations.find { |a| a.name == name }
|
209
|
+
raise("expected #{object.class.name} to has_many :#{name}") if association.blank?
|
210
|
+
|
211
|
+
nested_attributes_options = (object.class.nested_attributes_options || {})[name]
|
212
|
+
raise("expected #{object.class.name} to accepts_nested_attributes_for :#{name}") if nested_attributes_options.blank?
|
213
|
+
|
214
|
+
options = collection if collection.kind_of?(Hash)
|
215
|
+
options.merge!(collection: collection) if collection && !collection.kind_of?(Hash)
|
216
|
+
|
217
|
+
Effective::FormInputs::HasMany.new(name, options, builder: self).to_html(&block)
|
218
|
+
end
|
219
|
+
|
206
220
|
end
|
207
221
|
end
|
@@ -50,18 +50,29 @@ module Effective
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def build_label
|
53
|
-
return BLANK if options[:label] == false
|
53
|
+
return BLANK if options[:label] == false && !actions?
|
54
54
|
return BLANK if name.kind_of?(NilClass)
|
55
55
|
|
56
|
-
text =
|
56
|
+
text = begin
|
57
|
+
if options[:label] == false
|
58
|
+
nil
|
59
|
+
elsif options[:label].key?(:text)
|
60
|
+
options[:label].delete(:text)
|
61
|
+
elsif object.present?
|
62
|
+
object.class.human_attribute_name(name)
|
63
|
+
end || BLANK
|
64
|
+
end.html_safe
|
65
|
+
|
66
|
+
actions = if !disabled? && actions?
|
67
|
+
content_tag(:div, class: 'effective-checks-actions text-muted') do
|
68
|
+
link_to('Select All', '#', 'data-effective-checks-all': true) + ' - ' + link_to('Select None', '#', 'data-effective-checks-none': true)
|
69
|
+
end
|
70
|
+
end
|
57
71
|
|
58
72
|
content_tag(:label, options[:label]) do
|
59
|
-
text
|
60
|
-
unless disabled? || !actions?
|
61
|
-
link_to('Select All', '#', 'data-effective-checks-all': true) + ' - ' + link_to('Select None', '#', 'data-effective-checks-none': true)
|
62
|
-
end
|
63
|
-
end
|
73
|
+
[text, actions].compact.join.html_safe
|
64
74
|
end
|
75
|
+
|
65
76
|
end
|
66
77
|
|
67
78
|
def build_item(builder)
|
@@ -91,7 +102,7 @@ module Effective
|
|
91
102
|
|
92
103
|
def actions? # default true
|
93
104
|
return @actions unless @actions.nil?
|
94
|
-
@actions = (options.delete(:actions) != false)
|
105
|
+
@actions = (options[:input].delete(:actions) != false)
|
95
106
|
end
|
96
107
|
|
97
108
|
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
module Effective
|
2
|
+
module FormInputs
|
3
|
+
class HasMany < Effective::FormInput
|
4
|
+
BLANK = ''.html_safe
|
5
|
+
|
6
|
+
def to_html(&block)
|
7
|
+
object.send(name).build() if build? && collection.blank?
|
8
|
+
|
9
|
+
errors = (@builder.error(name) if errors?) || BLANK
|
10
|
+
|
11
|
+
errors + content_tag(:div, options[:input]) do
|
12
|
+
has_many_fields_for(block) + has_many_links_for(block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def input_html_options
|
17
|
+
{ class: 'form-has-many mb-4' }
|
18
|
+
end
|
19
|
+
|
20
|
+
def input_js_options
|
21
|
+
{ sortable: true }
|
22
|
+
end
|
23
|
+
|
24
|
+
def collection
|
25
|
+
Array(options[:input][:collection] || object.send(name))
|
26
|
+
end
|
27
|
+
|
28
|
+
# cards: false
|
29
|
+
def display
|
30
|
+
@display ||= (options[:input].delete(:cards) ? :cards : :rows)
|
31
|
+
end
|
32
|
+
|
33
|
+
# build: true
|
34
|
+
def build?
|
35
|
+
return @build unless @build.nil?
|
36
|
+
|
37
|
+
@build ||= begin
|
38
|
+
build = options[:input].delete(:build)
|
39
|
+
build.nil? ? true : build
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# add: true
|
44
|
+
def add?
|
45
|
+
return @add unless @add.nil?
|
46
|
+
|
47
|
+
@add ||= begin
|
48
|
+
add = options[:input].delete(:add)
|
49
|
+
add.nil? ? true : add
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# errors: true
|
54
|
+
def errors?
|
55
|
+
return @errors unless @errors.nil?
|
56
|
+
|
57
|
+
@errors ||= begin
|
58
|
+
errors = options[:input].delete(:errors)
|
59
|
+
errors.nil? ? true : errors
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# remove: true
|
64
|
+
def remove?
|
65
|
+
return @remove unless @remove.nil?
|
66
|
+
|
67
|
+
@remove ||= begin
|
68
|
+
remove = options[:input].delete(:remove)
|
69
|
+
|
70
|
+
if remove != nil
|
71
|
+
remove
|
72
|
+
else
|
73
|
+
opts = (object.class.nested_attributes_options[name] || {})
|
74
|
+
opts[:update_only] != true && opts[:allow_destroy] != false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# reorder: true
|
80
|
+
def reorder?
|
81
|
+
return @reorder unless @reorder.nil?
|
82
|
+
|
83
|
+
@reorder ||= begin
|
84
|
+
reorder = options[:input].delete(:reorder)
|
85
|
+
|
86
|
+
if reorder != nil
|
87
|
+
reorder
|
88
|
+
else
|
89
|
+
build_resource().class.columns_hash['position']&.type == :integer
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def has_many_fields_for(block)
|
97
|
+
collection.map { |resource| render_resource(resource, block) }.join.html_safe
|
98
|
+
end
|
99
|
+
|
100
|
+
def has_many_links_for(block)
|
101
|
+
return BLANK unless add? || reorder?
|
102
|
+
|
103
|
+
content_tag(:div, class: 'has-many-links text-center mt-2') do
|
104
|
+
[*(link_to_add(block) if add?), *(link_to_reorder(block) if reorder?)].join(' ').html_safe
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def render_resource(resource, block)
|
109
|
+
remove = BLANK
|
110
|
+
|
111
|
+
content = @builder.fields_for(name, resource) do |form|
|
112
|
+
fields = block.call(form)
|
113
|
+
|
114
|
+
remove += form.super_hidden_field(:_destroy) if remove? && resource.persisted?
|
115
|
+
remove += form.super_hidden_field(:position) if reorder? && !fields.include?('][position]')
|
116
|
+
|
117
|
+
fields
|
118
|
+
end
|
119
|
+
|
120
|
+
remove += link_to_remove(resource) if remove?
|
121
|
+
|
122
|
+
content_tag(:div, render_fields(content, remove), class: 'has-many-fields')
|
123
|
+
end
|
124
|
+
|
125
|
+
def render_fields(content, remove)
|
126
|
+
case display
|
127
|
+
when :rows
|
128
|
+
content_tag(:div, class: 'form-row') do
|
129
|
+
(reorder? ? content_tag(:div, has_many_move, class: 'col-auto') : BLANK) +
|
130
|
+
content_tag(:div, content, class: 'col mr-auto') +
|
131
|
+
content_tag(:div, remove, class: 'col-auto')
|
132
|
+
end
|
133
|
+
when :cards
|
134
|
+
raise('unsupported')
|
135
|
+
else
|
136
|
+
content + remove
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def render_template(block)
|
141
|
+
resource = build_resource()
|
142
|
+
index = collection.length
|
143
|
+
|
144
|
+
html = render_resource(resource, block)
|
145
|
+
|
146
|
+
unless html.include?("#{name}_attributes][#{index}]")
|
147
|
+
raise('unexpected index. unable to render resource template.')
|
148
|
+
end
|
149
|
+
|
150
|
+
html.gsub!("#{name}_attributes][#{index}]", "#{name}_attributes][HASMANYINDEX]")
|
151
|
+
html.gsub!("#{name}_attributes_#{index}_", "#{name}_attributes_HASMANYINDEX_")
|
152
|
+
|
153
|
+
html.html_safe
|
154
|
+
end
|
155
|
+
|
156
|
+
def link_to_add(block)
|
157
|
+
content_tag(
|
158
|
+
:button,
|
159
|
+
icon('plus-circle') + 'Add Another',
|
160
|
+
class: 'has-many-add btn btn-secondary',
|
161
|
+
title: 'Add Another',
|
162
|
+
data: {
|
163
|
+
'effective-form-has-many-add': true,
|
164
|
+
'effective-form-has-many-template': render_template(block)
|
165
|
+
}
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
169
|
+
def link_to_reorder(block)
|
170
|
+
content_tag(
|
171
|
+
:button,
|
172
|
+
icon('list') + 'Reorder',
|
173
|
+
class: 'has-many-reorder btn btn-secondary',
|
174
|
+
title: 'Reorder',
|
175
|
+
data: {
|
176
|
+
'effective-form-has-many-reorder': true,
|
177
|
+
}
|
178
|
+
)
|
179
|
+
end
|
180
|
+
|
181
|
+
def link_to_remove(resource)
|
182
|
+
content_tag(
|
183
|
+
:button,
|
184
|
+
icon('trash-2') + 'Remove',
|
185
|
+
class: 'has-many-remove btn btn-danger',
|
186
|
+
title: 'Remove',
|
187
|
+
data: {
|
188
|
+
'confirm': "Remove #{resource}?",
|
189
|
+
'effective-form-has-many-remove': true,
|
190
|
+
}
|
191
|
+
)
|
192
|
+
end
|
193
|
+
|
194
|
+
def has_many_move
|
195
|
+
@has_many_move ||= content_tag(:span, icon('move'), class: 'has-many-move')
|
196
|
+
end
|
197
|
+
|
198
|
+
def build_resource
|
199
|
+
# Using .new() here seems like it should work but it doesn't. It changes the index
|
200
|
+
@build_resource ||= object.send(name).build().tap do |resource|
|
201
|
+
object.send(name).delete(resource)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -3,11 +3,25 @@ module Effective
|
|
3
3
|
class PasswordField < Effective::FormInput
|
4
4
|
|
5
5
|
def input_html_options
|
6
|
-
{ class: 'form-control', id: tag_id }
|
6
|
+
{ class: 'form-control effective_password', id: tag_id }
|
7
7
|
end
|
8
8
|
|
9
9
|
def input_group_options
|
10
|
-
{ input_group: { class: 'input-group' },
|
10
|
+
{ input_group: { class: 'input-group' }, append: eyes }
|
11
|
+
end
|
12
|
+
|
13
|
+
def eyes
|
14
|
+
content_tag(:button, icon('eye'),
|
15
|
+
class: 'btn input-group-text',
|
16
|
+
title: 'Show password',
|
17
|
+
'data-effective-password': 'text'
|
18
|
+
) +
|
19
|
+
content_tag(:button, icon('eye-off'),
|
20
|
+
class: 'btn input-group-text',
|
21
|
+
title: 'Hide password',
|
22
|
+
style: 'display: none;',
|
23
|
+
'data-effective-password': 'password'
|
24
|
+
)
|
11
25
|
end
|
12
26
|
|
13
27
|
def feedback_options
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_bootstrap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: haml
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: Everything you need to get set up with bootstrap 4.
|
98
112
|
email:
|
99
113
|
- info@codeandeffect.com
|
@@ -419,10 +433,15 @@ files:
|
|
419
433
|
- app/assets/javascripts/effective_editor/quill.js
|
420
434
|
- app/assets/javascripts/effective_file/initialize.js.coffee
|
421
435
|
- app/assets/javascripts/effective_file/input.js
|
436
|
+
- app/assets/javascripts/effective_has_many/initialize.js.coffee
|
437
|
+
- app/assets/javascripts/effective_has_many/input.js
|
438
|
+
- app/assets/javascripts/effective_has_many/jquery.sortable.js
|
422
439
|
- app/assets/javascripts/effective_integer/initialize.js.coffee
|
423
440
|
- app/assets/javascripts/effective_integer/input.js
|
424
441
|
- app/assets/javascripts/effective_number_text/initialize.js.coffee
|
425
442
|
- app/assets/javascripts/effective_number_text/input.js
|
443
|
+
- app/assets/javascripts/effective_password/initialize.js.coffee
|
444
|
+
- app/assets/javascripts/effective_password/input.js
|
426
445
|
- app/assets/javascripts/effective_percent/initialize.js.coffee
|
427
446
|
- app/assets/javascripts/effective_percent/input.js
|
428
447
|
- app/assets/javascripts/effective_phone/initialize.js.coffee
|
@@ -584,6 +603,7 @@ files:
|
|
584
603
|
- app/assets/stylesheets/effective_editor/overrides.scss
|
585
604
|
- app/assets/stylesheets/effective_editor/quill.scss
|
586
605
|
- app/assets/stylesheets/effective_file/input.scss
|
606
|
+
- app/assets/stylesheets/effective_has_many/input.scss
|
587
607
|
- app/assets/stylesheets/effective_radio/input.scss
|
588
608
|
- app/assets/stylesheets/effective_rich_text_area/input.scss
|
589
609
|
- app/assets/stylesheets/effective_select/bootstrap-theme.css
|
@@ -613,6 +633,7 @@ files:
|
|
613
633
|
- app/models/effective/form_inputs/file_field.rb
|
614
634
|
- app/models/effective/form_inputs/float_field.rb
|
615
635
|
- app/models/effective/form_inputs/form_group.rb
|
636
|
+
- app/models/effective/form_inputs/has_many.rb
|
616
637
|
- app/models/effective/form_inputs/hidden_field.rb
|
617
638
|
- app/models/effective/form_inputs/integer_field.rb
|
618
639
|
- app/models/effective/form_inputs/number_field.rb
|