effective_bootstrap 0.9.5 → 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,6 +3,7 @@
3
3
  @import 'effective_checks/input';
4
4
  @import 'effective_datetime/input';
5
5
  @import 'effective_file/input';
6
+ @import 'effective_has_many/input';
6
7
  @import 'effective_rich_text_area/input';
7
8
  @import 'effective_select/input';
8
9
  @import 'effective_select_or_text/input';
@@ -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
+ }
@@ -2,7 +2,7 @@ module EffectiveFormBuilderHelper
2
2
  def effective_form_with(**options, &block)
3
3
  # Compute the default ID
4
4
  subject = Array(options[:scope] || options[:model]).last
5
- class_name = subject.class.name.underscore
5
+ class_name = 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)
@@ -56,6 +56,17 @@ module EffectiveFormBuilderHelper
56
56
  # Assign default ID
57
57
  options[:id] ||= (options[:html].delete(:id) || html_id) unless options.key?(:id)
58
58
 
59
+ # Assign url if engine present
60
+ options[:url] ||= if options[:engine] && options[:model].present?
61
+ resource = Effective::Resource.new(options[:model])
62
+
63
+ if subject.respond_to?(:persisted?) && subject.persisted?
64
+ resource.action_path(:update, subject)
65
+ elsif subject.respond_to?(:new_record?) && subject.new_record?
66
+ resource.action_path(:create)
67
+ end
68
+ end
69
+
59
70
  without_error_proc do
60
71
  form_with(**options.merge(builder: Effective::FormBuilder), &block)
61
72
  end
@@ -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
@@ -15,11 +15,12 @@ module Effective
15
15
 
16
16
  def input_js_options
17
17
  {
18
- modules: {
19
- toolbar: toolbar,
20
- imageResize: imageResize,
18
+ modules: {
19
+ toolbar: toolbar,
20
+ imageResize: imageResize,
21
21
  imageDropAndPaste: imageDropAndPaste,
22
- syntax: (content_mode == :code)
22
+ magicUrl: magicUrl,
23
+ syntax: (content_mode == :code)
23
24
  },
24
25
  theme: 'snow',
25
26
  placeholder: "Add #{name.to_s.pluralize}...",
@@ -42,7 +43,7 @@ module Effective
42
43
  end
43
44
 
44
45
  def imageDropAndPaste
45
- active_storage && !(content_mode == :code)
46
+ active_storage && !(content_mode == :code)
46
47
  end
47
48
 
48
49
  def imageResize
@@ -57,6 +58,10 @@ module Effective
57
58
  }
58
59
  end
59
60
 
61
+ def magicUrl
62
+ true
63
+ end
64
+
60
65
  def active_storage
61
66
  return @active_storage unless @active_storage.nil?
62
67
 
@@ -5,9 +5,9 @@ module Effective
5
5
  def build_input(&block)
6
6
  case attachments_style
7
7
  when :card
8
- build_attachments + build_uploads + super
8
+ build_existing_attachments + build_attachments + build_uploads + super
9
9
  when :table, :ck_assets
10
- super + build_uploads + build_attachments
10
+ super + build_existing_attachments + build_uploads + build_attachments
11
11
  else
12
12
  raise('unsupported attachments_style, try :card or :table')
13
13
  end
@@ -32,6 +32,16 @@ module Effective
32
32
  super(obj, name) && Array(object.public_send(name)).length == 0
33
33
  end
34
34
 
35
+ def build_existing_attachments
36
+ return ''.html_safe unless multiple?
37
+
38
+ attachments = object.send(name)
39
+
40
+ attachments.map.with_index do |attachment, index|
41
+ @builder.hidden_field(name, multiple: true, id: (tag_id + "_#{index}"), value: attachment.signed_id)
42
+ end.join.html_safe
43
+ end
44
+
35
45
  def build_attachments
36
46
  return ''.html_safe unless object.respond_to?(name) && object.send(name).respond_to?(:attached?) && object.send(name).attached?
37
47
 
@@ -0,0 +1,175 @@
1
+ module Effective
2
+ module FormInputs
3
+ class HasMany < Effective::FormInput
4
+ BLANK = ''.html_safe
5
+
6
+ def to_html(&block)
7
+ content_tag(:div, options[:input]) do
8
+ has_many_fields_for(block) + has_many_links_for(block)
9
+ end
10
+ end
11
+
12
+ def input_html_options
13
+ { class: 'form-has-many mb-4' }
14
+ end
15
+
16
+ def input_js_options
17
+ { sortable: true }
18
+ end
19
+
20
+ def collection
21
+ Array(options[:input][:collection] || object.send(name))
22
+ end
23
+
24
+ # cards: true
25
+ def display
26
+ @display ||= (options[:input].delete(:cards) ? :cards : :rows)
27
+ end
28
+
29
+ # add: false
30
+ def add?
31
+ return @add unless @add.nil?
32
+
33
+ @add ||= begin
34
+ add = options[:input].delete(:add)
35
+ add.nil? ? true : add
36
+ end
37
+ end
38
+
39
+ # remove: false
40
+ def remove?
41
+ return @remove unless @remove.nil?
42
+
43
+ @remove ||= begin
44
+ remove = options[:input].delete(:remove)
45
+
46
+ if remove != nil
47
+ remove
48
+ else
49
+ opts = (object.class.nested_attributes_options[name] || {})
50
+ opts[:update_only] != true && opts[:allow_destroy] != false
51
+ end
52
+ end
53
+ end
54
+
55
+ # reorder: true
56
+ def reorder?
57
+ return @reorder unless @reorder.nil?
58
+
59
+ @reorder ||= begin
60
+ reorder = options[:input].delete(:reorder)
61
+
62
+ if reorder != nil
63
+ reorder
64
+ else
65
+ build_resource().class.columns_hash['position']&.type == :integer
66
+ end
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def has_many_fields_for(block)
73
+ collection.map { |resource| render_resource(resource, block) }.join.html_safe
74
+ end
75
+
76
+ def has_many_links_for(block)
77
+ return BLANK unless add? || reorder?
78
+
79
+ content_tag(:div, class: 'has-many-links text-center mt-2') do
80
+ [(link_to_add(block) if add?), (link_to_reorder(block) if reorder?)].compact.join(' ').html_safe
81
+ end
82
+ end
83
+
84
+ def render_resource(resource, block)
85
+ remove = BLANK
86
+
87
+ content = @builder.fields_for(name, resource) do |form|
88
+ fields = block.call(form)
89
+
90
+ remove += form.super_hidden_field(:_destroy) if remove? && resource.persisted?
91
+ remove += form.super_hidden_field(:position) if reorder? && !fields.include?('][position]')
92
+
93
+ fields
94
+ end
95
+
96
+ remove += link_to_remove(resource) if remove?
97
+
98
+ content_tag(:div, render_fields(content, remove), class: 'has-many-fields')
99
+ end
100
+
101
+ def render_fields(content, remove)
102
+ case display
103
+ when :rows
104
+ content_tag(:div, class: 'form-row') do
105
+ (reorder? ? content_tag(:div, has_many_move, class: 'col-auto') : BLANK) +
106
+ content_tag(:div, content, class: 'col mr-auto') +
107
+ content_tag(:div, remove, class: 'col-auto')
108
+ end
109
+ when :cards
110
+ raise('unsupported')
111
+ else
112
+ content + remove
113
+ end
114
+ end
115
+
116
+ def render_template(block)
117
+ resource = build_resource()
118
+ index = object.send(name).index(resource)
119
+
120
+ html = render_resource(resource, block)
121
+ html.gsub!("#{name}_attributes][#{index}]", "#{name}_attributes][HASMANYINDEX]")
122
+ html.gsub!("#{name}_attributes_#{index}_", "#{name}_attributes_HASMANYINDEX_")
123
+
124
+ html.html_safe
125
+ end
126
+
127
+ def link_to_add(block)
128
+ content_tag(
129
+ :button,
130
+ icon('plus-circle') + 'Add Another',
131
+ class: 'has-many-add btn btn-secondary',
132
+ title: 'Add Another',
133
+ data: {
134
+ 'effective-form-has-many-add': true,
135
+ 'effective-form-has-many-template': render_template(block)
136
+ }
137
+ )
138
+ end
139
+
140
+ def link_to_reorder(block)
141
+ content_tag(
142
+ :button,
143
+ icon('list') + 'Reorder',
144
+ class: 'has-many-reorder btn btn-secondary',
145
+ title: 'Reorder',
146
+ data: {
147
+ 'effective-form-has-many-reorder': true,
148
+ }
149
+ )
150
+ end
151
+
152
+ def link_to_remove(resource)
153
+ content_tag(
154
+ :button,
155
+ icon('trash-2') + 'Remove',
156
+ class: 'has-many-remove btn btn-danger',
157
+ title: 'Remove',
158
+ data: {
159
+ 'confirm': "Remove #{resource}?",
160
+ 'effective-form-has-many-remove': true,
161
+ }
162
+ )
163
+ end
164
+
165
+ def has_many_move
166
+ @has_many_move ||= content_tag(:span, icon('move'), class: 'has-many-move')
167
+ end
168
+
169
+ def build_resource
170
+ @build_resource ||= object.send(name).build().tap { |resource| object.send(name).delete(resource) }
171
+ end
172
+
173
+ end
174
+ end
175
+ end
@@ -8,7 +8,7 @@ module EffectiveBootstrap
8
8
  end
9
9
 
10
10
  initializer 'effective_bootstrap.assets' do |app|
11
- app.config.assets.precompile += ['icons/*']
11
+ app.config.assets.precompile += ['effective_bootstrap_manifest.js', 'icons/*']
12
12
  end
13
13
 
14
14
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveBootstrap
2
- VERSION = '0.9.5'.freeze
2
+ VERSION = '0.9.10'.freeze
3
3
  end
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.5
4
+ version: 0.9.10
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: 2020-09-28 00:00:00.000000000 Z
11
+ date: 2021-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -103,6 +103,7 @@ extra_rdoc_files: []
103
103
  files:
104
104
  - MIT-LICENSE
105
105
  - README.md
106
+ - app/assets/config/effective_bootstrap_manifest.js
106
107
  - app/assets/images/icons/activity.svg
107
108
  - app/assets/images/icons/airplay.svg
108
109
  - app/assets/images/icons/alert-circle.svg
@@ -414,9 +415,13 @@ files:
414
415
  - app/assets/javascripts/effective_editor/image-resize.js
415
416
  - app/assets/javascripts/effective_editor/initialize.js.coffee
416
417
  - app/assets/javascripts/effective_editor/input.js
418
+ - app/assets/javascripts/effective_editor/magic-url.js
417
419
  - app/assets/javascripts/effective_editor/quill.js
418
420
  - app/assets/javascripts/effective_file/initialize.js.coffee
419
421
  - app/assets/javascripts/effective_file/input.js
422
+ - app/assets/javascripts/effective_has_many/initialize.js.coffee
423
+ - app/assets/javascripts/effective_has_many/input.js
424
+ - app/assets/javascripts/effective_has_many/jquery.sortable.js
420
425
  - app/assets/javascripts/effective_integer/initialize.js.coffee
421
426
  - app/assets/javascripts/effective_integer/input.js
422
427
  - app/assets/javascripts/effective_number_text/initialize.js.coffee
@@ -582,6 +587,7 @@ files:
582
587
  - app/assets/stylesheets/effective_editor/overrides.scss
583
588
  - app/assets/stylesheets/effective_editor/quill.scss
584
589
  - app/assets/stylesheets/effective_file/input.scss
590
+ - app/assets/stylesheets/effective_has_many/input.scss
585
591
  - app/assets/stylesheets/effective_radio/input.scss
586
592
  - app/assets/stylesheets/effective_rich_text_area/input.scss
587
593
  - app/assets/stylesheets/effective_select/bootstrap-theme.css
@@ -611,6 +617,7 @@ files:
611
617
  - app/models/effective/form_inputs/file_field.rb
612
618
  - app/models/effective/form_inputs/float_field.rb
613
619
  - app/models/effective/form_inputs/form_group.rb
620
+ - app/models/effective/form_inputs/has_many.rb
614
621
  - app/models/effective/form_inputs/hidden_field.rb
615
622
  - app/models/effective/form_inputs/integer_field.rb
616
623
  - app/models/effective/form_inputs/number_field.rb
@@ -666,7 +673,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
666
673
  - !ruby/object:Gem::Version
667
674
  version: '0'
668
675
  requirements: []
669
- rubygems_version: 3.1.4
676
+ rubygems_version: 3.1.2
670
677
  signing_key:
671
678
  specification_version: 4
672
679
  summary: Everything you need to get set up with bootstrap 4.