effective_bootstrap 0.9.5 → 0.9.10

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.
@@ -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.