effective_bootstrap 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +391 -42
- data/app/assets/javascripts/effective_bootstrap.js +1 -1
- data/app/assets/javascripts/effective_bootstrap/base.js.coffee +31 -0
- data/app/assets/javascripts/effective_price/{input.js.coffee → initialize.js.coffee} +0 -0
- data/app/assets/javascripts/effective_price/input.js +1 -0
- data/app/assets/javascripts/effective_radio/initialize.js.coffee +24 -0
- data/app/assets/javascripts/effective_radio/input.js +1 -0
- data/app/assets/stylesheets/effective_bootstrap/forms.scss +6 -0
- data/app/assets/stylesheets/effective_radio/input.scss +14 -0
- data/app/helpers/effective_bootstrap_helper.rb +1 -1
- data/app/helpers/effective_icons_helper.rb +0 -1
- data/app/models/effective/form_inputs/radios.rb +31 -2
- data/app/models/effective/form_inputs/submit.rb +35 -10
- data/app/views/effective/style_guide/_feather_icons.html.haml +1 -1
- data/lib/effective_bootstrap/version.rb +1 -1
- metadata +20 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4366d401ed983ede60b16c9e82fbb76e393975be
|
4
|
+
data.tar.gz: e33da495ac5d246d803444c8ed3726250ad45416
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1983cdb4104022d4dc1f24987b418222fcf12688f7c6cf6c1e4553b09f0071e16ef75bc7d98506eb85d1c299183259bf75ec6caf9e6b633327f1ee087c454b22
|
7
|
+
data.tar.gz: 413e36f0d728563fe5eb845fc236dd086c3dc6581bd51729e57b2d62f160ce73d13ec671d80f63e358583f2b0ea78745a040708682dcd9506a22717499e04ee7
|
data/README.md
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
# Effective Bootstrap
|
2
2
|
|
3
|
-
Everything
|
3
|
+
Everything your Ruby on Rails 5.1+ application needs to get working with [Twitter Bootstrap 4](https://getbootstrap.com/).
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
Rails >= 5.1
|
5
|
+
- Bootstrap4 component view helpers.
|
6
|
+
- SVG icons based on [Inline SVG](https://github.com/jamesmartin/inline_svg), with [Feather Icons](https://feathericons.com) and [FontAwesome](https://fontawesome.com) svg icons to replace the old glyphicons.
|
7
|
+
- An html-exact form builder that builds on top of Rails' new `form_with` with numerous custom form inputs.
|
9
8
|
|
10
9
|
## Getting Started
|
11
10
|
|
@@ -27,85 +26,435 @@ rails generate effective_bootstrap:install
|
|
27
26
|
|
28
27
|
The generator will install an initializer which describes all configuration options.
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
Your `application.js` should include
|
29
|
+
Add the following to your `application.js`:
|
33
30
|
|
34
31
|
```ruby
|
35
32
|
//= require jquery3
|
36
33
|
//= require popper
|
37
34
|
//= require bootstrap
|
35
|
+
//= require effective_bootstrap
|
38
36
|
```
|
39
37
|
|
40
|
-
And `application.scss
|
38
|
+
And to your `application.scss`:
|
41
39
|
|
42
40
|
```sass
|
43
41
|
@import 'bootstrap';
|
42
|
+
@import 'effective_bootstrap';
|
43
|
+
```
|
44
|
+
|
45
|
+
## View Helpers
|
46
|
+
|
47
|
+
### Dropdown
|
48
|
+
|
49
|
+
https://getbootstrap.com/docs/4.0/components/dropdowns/
|
50
|
+
|
51
|
+
```haml
|
52
|
+
= dropdown do
|
53
|
+
= dropdown_link_to 'Something', root_path
|
54
|
+
= dropdown_divider
|
55
|
+
= dropdown_link_to 'Another', root_path
|
56
|
+
```
|
57
|
+
|
58
|
+
Options include: `dropdown(variation: :dropup|:dropleft|:dropright, split: true|false, right: true|false, btn: 'btn-secondary')`
|
59
|
+
|
60
|
+
### ListGroup
|
61
|
+
|
62
|
+
https://getbootstrap.com/docs/4.0/components/list-group/
|
63
|
+
|
64
|
+
```haml
|
65
|
+
= list_group do
|
66
|
+
= list_group_link_to 'Something', root_path
|
67
|
+
```
|
68
|
+
|
69
|
+
`list_group_link_to` will automatically insert the the `.active` class based on the request path.
|
70
|
+
|
71
|
+
### Navbar
|
72
|
+
|
73
|
+
https://getbootstrap.com/docs/4.0/components/navbar/
|
74
|
+
|
75
|
+
```haml
|
76
|
+
%nav.navbar.navbar-expand-lg.navbar-light.bg-light
|
77
|
+
%a.navbar-brand{href: '/'} Home
|
78
|
+
|
79
|
+
%button.navbar-toggler{type: 'button', data: {toggle: 'collapse', target: '#navContent', 'aria-controls': 'navContent', 'aria-label': 'Toggle navigation'}}
|
80
|
+
%span.navbar-toggler-icon
|
81
|
+
|
82
|
+
#navContent.collapse.navbar-collapse
|
83
|
+
%ul.navbar-nav.mr-auto
|
84
|
+
= nav_link_to 'About', '/about'
|
85
|
+
= nav_link_to 'Contact', '/conact'
|
86
|
+
|
87
|
+
%ul.navbar-nav
|
88
|
+
- if current_user.present?
|
89
|
+
= nav_dropdown('Account', right: true) do
|
90
|
+
= nav_link_to 'Settings', user_settings_path
|
91
|
+
|
92
|
+
- if can?(:access, :admin)
|
93
|
+
= nav_divider
|
94
|
+
= nav_link_to 'Site Admin', '/admin'
|
95
|
+
|
96
|
+
= nav_divider
|
97
|
+
= nav_link_to 'Sign Out', destroy_user_session_path, method: :delete
|
98
|
+
- else
|
99
|
+
= nav_link_to 'Sign In', new_user_session_path
|
44
100
|
```
|
45
101
|
|
46
|
-
|
102
|
+
`nav_link_to` will automatically insert the `.active` class based on the request path.
|
103
|
+
|
104
|
+
## Icon Helpers
|
105
|
+
|
106
|
+
Unfortunately, Bootstrap 4 dropped support for glyphicons, so we use a combination of [Inline SVG](https://github.com/jamesmartin/inline_svg), with [Feather Icons](https://feathericons.com) and [FontAwesome](https://fontawesome.com) .svg images (no webfonts) to get back this functionality, even better than it was before.
|
107
|
+
|
108
|
+
```haml
|
109
|
+
= icon('ok') # <svg class='eb-icon eb-icon-ok' ...>
|
110
|
+
```
|
111
|
+
|
112
|
+
```haml
|
113
|
+
= icon_to('ok', root_path) # <a href='/'><svg class='eb-icon eb-icon-ok' ...></a>
|
114
|
+
```
|
115
|
+
|
116
|
+
A full list of icons can be found here: [All effective_bootstrap icons](https://github.com/code-and-effect/effective_bootstrap/tree/master/app/assets/images/icons)
|
117
|
+
|
118
|
+
To overwrite or add an icon, just drop the `.svg` file into your application's `app/assets/images/icons/` directory.
|
47
119
|
|
48
|
-
|
120
|
+
There are also a few helpers for commonly used icons, they all take the form of `x_icon_to(new_thing_path)`:
|
49
121
|
|
50
|
-
|
122
|
+
- `new_icon_to`
|
123
|
+
- `show_icon_to`
|
124
|
+
- `edit_icon_to`
|
125
|
+
- `destroy_icon_to`
|
126
|
+
- `settings_icon_to`
|
127
|
+
- `ok_icon_to`
|
128
|
+
- `approve_icon_to`
|
129
|
+
- `remove_icon_to`
|
51
130
|
|
52
|
-
|
131
|
+
## Form Builder
|
132
|
+
|
133
|
+
Rails 5.1 has introduced a new `form_with` syntax, and soft-deprecated `form_tag` and `form_for`.
|
134
|
+
|
135
|
+
This gem includes a [Bootstrap4 Forms](https://getbootstrap.com/docs/4.0/components/forms/) html-exact form builder built on top of `form_with`.
|
136
|
+
|
137
|
+
The goal of this form builder is to output beautiful forms while matching the rails form syntax -- you should be able to change an existing `form_with` form to `effective_form_with` with no other changes.
|
138
|
+
|
139
|
+
Of course, just the regular form inputs are boring, and this gem extends numerous jQuery/Javascript libraries to level up some inputs.
|
140
|
+
|
141
|
+
This is an opinionated Bootstrap4 form builder.
|
142
|
+
|
143
|
+
## effective_form_with
|
144
|
+
|
145
|
+
Matches the Rails `form_with` tag syntax, with all its `:model`, `:scope`, `:url`, `:method`, etc.
|
146
|
+
|
147
|
+
As well, you can specify `layout: :vertical`, `layout: :horizontal`, or `layout: :inline` as per the different Bootstrap form layouts.
|
148
|
+
|
149
|
+
```haml
|
150
|
+
= effective_form_with(model: @user, layout: :horizontal) do |f|
|
151
|
+
= f.text_field :name
|
152
|
+
= f.submit
|
153
|
+
```
|
154
|
+
|
155
|
+
The default is `layout: :vertical`.
|
156
|
+
|
157
|
+
All standard form fields have been implemented as per [Rails 5.1 FormHelper](http://api.rubyonrails.org/v5.1/classes/ActionView/Helpers/FormHelper.html)
|
158
|
+
|
159
|
+
### Options
|
160
|
+
|
161
|
+
There are three sets of options hashes that you can pass into any form input:
|
162
|
+
|
163
|
+
- `wrapper: { class: 'something' }` are applied to the wrapping div tag.
|
164
|
+
- `input_html: { class: 'something' }` are applied to the input, select or textarea tag itself.
|
165
|
+
- `input_js: { key: value}` are passed to any custom form input will be used to initialize the Javascript library. For example:
|
53
166
|
|
54
167
|
```ruby
|
55
|
-
|
168
|
+
= effective_form_with(model: @user) do |f|
|
169
|
+
= f.date_field :updated_at, input_js: { useCurrent: 'day', showTodayButton: true }
|
170
|
+
```
|
171
|
+
|
172
|
+
will result in the following call to the Javascript library:
|
173
|
+
|
174
|
+
```javascript
|
175
|
+
$('input').datetimepicker(useCurrent: 'day', showTodayButton: true);
|
176
|
+
```
|
177
|
+
Any options passed in this way will be used to initialize the underlying javascript libraries.
|
178
|
+
|
179
|
+
## Basic form inputs
|
180
|
+
|
181
|
+
The following form inputs are supported, but don't have any kind of custom JavaScript
|
182
|
+
|
183
|
+
```haml
|
184
|
+
= f.check_box
|
185
|
+
= f.email_field
|
186
|
+
= f.error_field
|
187
|
+
= f.number_field
|
188
|
+
= f.password_field
|
189
|
+
= f.static_field
|
190
|
+
= f.text_area
|
191
|
+
= f.text_field
|
192
|
+
= f.url_field
|
193
|
+
```
|
194
|
+
|
195
|
+
## Custom date_field, datetime_field, time_field
|
196
|
+
|
197
|
+
These custom form inputs are all based on the following awesome project:
|
198
|
+
|
199
|
+
Bootstrap 3 Datepicker (https://github.com/Eonasdan/bootstrap-datetimepicker)
|
200
|
+
|
201
|
+
```haml
|
202
|
+
= f.date_field :updated_at
|
203
|
+
= f.datetime_field :updated_at
|
204
|
+
= f.time_field :updated_at
|
56
205
|
```
|
57
206
|
|
58
|
-
|
207
|
+
### Options
|
208
|
+
|
209
|
+
The default options used to initialize this form input are as follows:
|
59
210
|
|
60
211
|
```ruby
|
61
|
-
|
212
|
+
input_js: { showTodayButton: true, showClear: true, useCurrent: 'hour' }
|
62
213
|
```
|
63
214
|
|
64
|
-
|
215
|
+
For a full list of options, please refer to:
|
216
|
+
|
217
|
+
http://eonasdan.github.io/bootstrap-datetimepicker/Options/
|
218
|
+
|
219
|
+
### Set Date
|
220
|
+
|
221
|
+
Use the following JavaScript to set the date:
|
65
222
|
|
66
|
-
|
223
|
+
```javascript
|
224
|
+
$('#start_at').data('DateTimePicker').date('2016-05-08')
|
225
|
+
```
|
67
226
|
|
68
|
-
|
227
|
+
### Disabled Dates
|
69
228
|
|
70
|
-
|
229
|
+
Provide a String, Date, or Range to set the disabled dates.
|
71
230
|
|
72
231
|
```ruby
|
73
|
-
|
74
|
-
|
232
|
+
input_js: { disabledDates: '2020-01-01' }
|
233
|
+
input_js: { disabledDates: Time.zone.now }
|
234
|
+
input_js: { disabledDates: Time.zone.now.beginning_of_month..Time.zone.now.end_of_month }
|
235
|
+
input_js: { disabledDates: [Time.zone.now, Time.zone.now + 1.day] }
|
75
236
|
```
|
76
237
|
|
77
|
-
|
238
|
+
### Linked Dates
|
239
|
+
|
240
|
+
By default, when two matching date inputs named `start_*` and `end_*` are present on the same form, they will become linked.
|
241
|
+
|
242
|
+
The end date selector will have its date <= start_date disabled.
|
243
|
+
|
244
|
+
To disable this behaviour, call with `date_linked: false`.
|
78
245
|
|
79
246
|
```ruby
|
80
|
-
=
|
81
|
-
= f.input :updated_at, :as => :effective_date_time_picker, :input_js => {:format => 'dddd, MMMM Do YYYY', :showTodayButton => true}
|
247
|
+
= f.input :end_at, date_linked: false
|
82
248
|
```
|
83
249
|
|
84
|
-
|
250
|
+
### Events
|
85
251
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
252
|
+
The date picker library doesn't trigger a regular `change`. Instead you must watch for the `dp.change` event.
|
253
|
+
|
254
|
+
More info is available here:
|
255
|
+
|
256
|
+
http://eonasdan.github.io/bootstrap-datetimepicker/Events/
|
257
|
+
|
258
|
+
## Custom price_field
|
259
|
+
|
260
|
+
This custom form input uses no 3rd party jQuery plugins.
|
261
|
+
|
262
|
+
It displays a currency formatted value `100.00` but posts the "price as integer" value of `10000` to the server.
|
263
|
+
|
264
|
+
Think about this value as "the number of cents".
|
265
|
+
|
266
|
+
```haml
|
267
|
+
= f.price_field :price
|
90
268
|
```
|
91
269
|
|
92
|
-
|
270
|
+
This gem also includes a rails view helper `price_to_currency` that takes a value like `10000` and displays it as `$100.00`
|
271
|
+
|
272
|
+
## Custom select
|
273
|
+
|
274
|
+
This custom form input is based on the following awesome project:
|
93
275
|
|
276
|
+
Select2 (https://select2.github.io/)
|
94
277
|
|
278
|
+
### Usage
|
279
|
+
|
280
|
+
As a Rails Form Helper input:
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
= f.select :category, 10.times.map { |x| "Category #{x}"}
|
284
|
+
= f.select :categories, 10.times.map { |x| "Category #{x}"}, multiple: true
|
285
|
+
= f.select :categories, 10.times.map { |x| "Category #{x}"}, tags: true
|
286
|
+
= f.select :categories, {'Active': [['Post A', 1], ['Post B', 2]], 'Past': ['Post C', 3], ['Post D', 4]}, grouped: true
|
287
|
+
```
|
288
|
+
|
289
|
+
### Modes
|
290
|
+
|
291
|
+
The standard mode is a replacement for the default single select box.
|
292
|
+
|
293
|
+
Passing `multiple: true` will allow multiple selections to be made.
|
294
|
+
|
295
|
+
Passing `multiple: true, tags: true` will allow multiple selections to be made, and new value options to be created. This will allow you to both select existing tags and create new tags in the same form control.
|
296
|
+
|
297
|
+
Passing `grouped: true` will enable optgroup support. When in this mode, the collection should be a Hash of ActiveRecord Relations or Array of Arrays
|
298
|
+
|
299
|
+
```ruby
|
300
|
+
{'Active' => Post.active, 'Past' => Post.past}
|
301
|
+
{'Active' => [['Post A', 1], ['Post B', 2]], 'Past' => [['Post C', 3], ['Post D', 4]]}
|
302
|
+
```
|
303
|
+
|
304
|
+
Passing `polymorphic: true` will enable polymorphic support. In this mode, an additional 2 hidden input fields are created alongside the select field.
|
305
|
+
|
306
|
+
So calling
|
307
|
+
|
308
|
+
```ruby
|
309
|
+
= f.input :primary_contact, User.all.to_a + Member.all.to_a, polymorphic: true
|
310
|
+
```
|
311
|
+
|
312
|
+
will internally translate the collection into:
|
313
|
+
|
314
|
+
```ruby
|
315
|
+
[['User 1', 'User_1'], ['User 2', 'User_2'], ['Member 100', 'Member_100']]
|
95
316
|
```
|
96
|
-
= f.submit
|
97
|
-
= f.submit 'Save 2'
|
98
317
|
|
99
|
-
|
100
|
-
= f.submit 'Save', center: true
|
101
|
-
= f.submit 'Save', right: true
|
318
|
+
and instead of posting to the server with the parameter `:primary_contact`, it will instead post `{:primary_contact_id => 2, :primary_contact_type => 'User'}`.
|
102
319
|
|
103
|
-
|
104
|
-
= f.submit 'Save', center: true, border: false
|
105
|
-
= f.submit 'Save', left: true, border: false
|
320
|
+
Using both `polymorphic: true` and `grouped: true` is recommended. In this case the expected collection is as follows:
|
106
321
|
|
107
|
-
|
108
|
-
|
109
|
-
|
322
|
+
```ruby
|
323
|
+
= f.input :primary_contact, {'Users' => User.all, 'Members' => 'Member.all'}, polymorphic: true, grouped: true
|
324
|
+
```
|
325
|
+
|
326
|
+
### Options
|
327
|
+
|
328
|
+
The default options used to initialize this form input are as follows:
|
329
|
+
|
330
|
+
```ruby
|
331
|
+
{
|
332
|
+
:theme => 'bootstrap',
|
333
|
+
:minimumResultsForSearch => 6,
|
334
|
+
:tokenSeparators => [',', ' '],
|
335
|
+
:width => 'style',
|
336
|
+
:placeholder => 'Please choose',
|
337
|
+
:allowClear => !(options[:multiple]) # Only display the Clear 'x' on a single selection box
|
338
|
+
}
|
339
|
+
```
|
340
|
+
|
341
|
+
### Interesting Available Options
|
342
|
+
|
343
|
+
To limit the number of items that can be selected in a multiple select box:
|
344
|
+
|
345
|
+
```ruby
|
346
|
+
:maximumSelectionLength => 2
|
110
347
|
```
|
111
348
|
|
349
|
+
To hide the search box entirely:
|
350
|
+
|
351
|
+
```ruby
|
352
|
+
:minimumResultsForSearch => 'Infinity'
|
353
|
+
```
|
354
|
+
|
355
|
+
For a full list of options, please refer to: https://select2.github.io/options.html
|
356
|
+
|
357
|
+
|
358
|
+
The following `input_js: options` are not part of the standard select2 API, and are custom `effective_select` functionality only:
|
359
|
+
|
360
|
+
To add a css class to the select2 container or dropdown:
|
361
|
+
|
362
|
+
```ruby
|
363
|
+
:containerClass => 'custom-container-class'
|
364
|
+
:dropdownClass => 'custom-dropdown-class'
|
365
|
+
```
|
366
|
+
|
367
|
+
to display a glyphicon infront of each option value:
|
368
|
+
|
369
|
+
```ruby
|
370
|
+
f.input :user, User.all.map { |user| [user.to_s, user.to_param, { class: 'glyphicon-flag' }] }, template: :glyphicon
|
371
|
+
```
|
372
|
+
|
373
|
+
|
374
|
+
### Additional
|
375
|
+
|
376
|
+
Call with `single_selected: true` to ensure only the first selected option tag will be `<option selected="selected">`.
|
377
|
+
|
378
|
+
This can be useful when displaying multiple options with an identical value.
|
379
|
+
|
380
|
+
### Clear value
|
381
|
+
|
382
|
+
It's a bit tricky to clear the selected value
|
383
|
+
|
384
|
+
```coffeescript
|
385
|
+
$('select').val('').trigger('change.select2')
|
386
|
+
```
|
387
|
+
|
388
|
+
### Working with dynamic options
|
389
|
+
|
390
|
+
The following information applies to `effective_select` only, and is not part of the standard select2 API.
|
391
|
+
|
392
|
+
To totally hide (instead of just grey out) any disabled options from the select2 dropdown, initialize the input with:
|
393
|
+
|
394
|
+
```ruby
|
395
|
+
= f.input :category, User.all, hide_disabled: true
|
396
|
+
```
|
397
|
+
|
398
|
+
If you want to dynamically add/remove options from the select field after page load, you must use the `select2:reinitialize` event:
|
399
|
+
|
400
|
+
```coffeescript
|
401
|
+
# When something on my page changes
|
402
|
+
$(document).on 'change', '.something', (event) ->
|
403
|
+
$select = $(event.target).closest('form').find('select.i-want-to-change') # Find the select2 input to be updated
|
404
|
+
|
405
|
+
# Go through its options, and modify some of them.
|
406
|
+
# Using the above 'hide_disabled true' functionality, the following code hides the options from being displayed,
|
407
|
+
# but you could actually remove the options, add new ones, or update the values/texts. whatever.
|
408
|
+
$select.find('option').each (index, option) ->
|
409
|
+
$(option).prop('disabled', true) if index > 10
|
410
|
+
|
411
|
+
# Whenever the underlying options change, you need to manually trigger the following event:
|
412
|
+
$select.select2().trigger('select2:reinitialize')
|
413
|
+
```
|
414
|
+
|
415
|
+
### AJAX Support
|
416
|
+
|
417
|
+
There is currently no support for using AJAX to load remote data. This feature is supported by the underlying select2 library and will be implemented here at a future point.
|
418
|
+
|
419
|
+
## Custom submit and save
|
420
|
+
|
421
|
+
The `f.submit` puts in a wrapper and a default save button, and does the whole icon spin when submit thing.
|
422
|
+
|
423
|
+
The `f.save` is purely a input submit button.
|
424
|
+
|
425
|
+
```haml
|
426
|
+
= f.submit
|
427
|
+
= f.submit 'Save 2'
|
428
|
+
|
429
|
+
= f.submit 'Save', left: true
|
430
|
+
= f.submit 'Save', center: true
|
431
|
+
= f.submit 'Save', right: true
|
432
|
+
|
433
|
+
= f.submit 'Save', border: false
|
434
|
+
= f.submit 'Save', center: true, border: false
|
435
|
+
= f.submit 'Save', left: true, border: false
|
436
|
+
|
437
|
+
= f.submit(border: false) do
|
438
|
+
= f.save 'Save 1'
|
439
|
+
= f.save 'Save 2'
|
440
|
+
```
|
441
|
+
|
442
|
+
|
443
|
+
## License
|
444
|
+
|
445
|
+
MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
|
446
|
+
|
447
|
+
## Credits
|
448
|
+
|
449
|
+
The authors of this gem are not associated with any of the awesome projects used by this gem.
|
450
|
+
|
451
|
+
We are just extending these existing community projects for ease of use with Rails Form Helper and SimpleForm.
|
452
|
+
|
453
|
+
## Contributing
|
454
|
+
|
455
|
+
1. Fork it
|
456
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
457
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
458
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
459
|
+
5. Bonus points for test coverage
|
460
|
+
6. Create new Pull Request
|
@@ -1,4 +1,8 @@
|
|
1
1
|
this.EffectiveBootstrap ||= new class
|
2
|
+
remote_form_payload: '' # A fresh form
|
3
|
+
remote_form_flash: '' # Array of Arrays
|
4
|
+
remote_form_flash_payload: '' # $('<div alert>...</div>')
|
5
|
+
|
2
6
|
initialize: (target) ->
|
3
7
|
$(target || document).find('[data-input-js-options]:not(.initialized)').each (i, element) ->
|
4
8
|
$element = $(element)
|
@@ -29,14 +33,41 @@ this.EffectiveBootstrap ||= new class
|
|
29
33
|
else
|
30
34
|
$form.addClass('was-validated').addClass('form-is-invalid').removeClass('form-is-valid')
|
31
35
|
|
36
|
+
if valid and $form.data('remote')
|
37
|
+
$form.one 'ajax:success', (event) -> EffectiveBootstrap.loadRemoteForm($(event.target))
|
38
|
+
|
32
39
|
valid
|
33
40
|
|
41
|
+
submitting: ($form) ->
|
42
|
+
$form.addClass('form-is-valid').removeClass('form-is-invalid')
|
43
|
+
@disable($form)
|
44
|
+
|
34
45
|
disable: ($form) ->
|
35
46
|
$form.find('[type=submit]').prop('disabled', true)
|
36
47
|
|
37
48
|
enable: ($form) ->
|
38
49
|
$form.removeClass('form-is-valid').find('[type=submit]').removeAttr('disabled')
|
39
50
|
|
51
|
+
# Loads remote for payload that was placed here by effective_resources create.js.erb and update.js.erb
|
52
|
+
loadRemoteForm: ($target) ->
|
53
|
+
$form = @remote_form_payload.find('form')
|
54
|
+
$target.replaceWith($form)
|
55
|
+
|
56
|
+
for flash in @remote_form_flash
|
57
|
+
@flash($form, flash[0], @remote_form_flash_payload)
|
58
|
+
|
59
|
+
@remote_form_payload = ''; @remote_form_flash = ''; @remote_form_flash_payload = '';
|
60
|
+
|
61
|
+
flash: ($form, status, $alert) ->
|
62
|
+
$submit = $form.children('.form-actions')
|
63
|
+
|
64
|
+
if status == 'danger' || status == 'error'
|
65
|
+
$submit.find('.eb-icon-x').show().delay(1000).fadeOut('slow')
|
66
|
+
else
|
67
|
+
$submit.find('.eb-icon-check').show().delay(1000).fadeOut('slow')
|
68
|
+
|
69
|
+
$submit.prepend($alert) if $alert.length > 0
|
70
|
+
|
40
71
|
$ -> EffectiveBootstrap.initialize()
|
41
72
|
$(document).on 'turbolinks:load', -> EffectiveBootstrap.initialize()
|
42
73
|
$(document).on 'cocoon:after-insert', -> EffectiveBootstrap.initialize()
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require ./initialize
|
@@ -0,0 +1,24 @@
|
|
1
|
+
$(document).on 'change', '[data-toggle=cards] input[type=radio]', (event) ->
|
2
|
+
$input = $(event.currentTarget)
|
3
|
+
return unless $input.is(':checked')
|
4
|
+
|
5
|
+
$card = $input.closest('.card')
|
6
|
+
return unless $card.length > 0
|
7
|
+
|
8
|
+
$cards = $input.closest('.card-deck').children('.card')
|
9
|
+
return unless $cards.length > 0
|
10
|
+
|
11
|
+
$cards.removeClass('active').removeClass('border-secondary')
|
12
|
+
$cards.find('.card-header').removeClass('bg-secondary text-white')
|
13
|
+
|
14
|
+
$card.addClass('active').addClass('border-secondary')
|
15
|
+
$card.find('.card-header').addClass('bg-secondary text-white')
|
16
|
+
true
|
17
|
+
|
18
|
+
$(document).on 'click', '[data-toggle=cards] [data-toggle=card]', (event) ->
|
19
|
+
$card = $(event.currentTarget).closest('.card')
|
20
|
+
return unless $card.length > 0
|
21
|
+
|
22
|
+
val = $card.find('input:radio').val()
|
23
|
+
$card.find('input:radio').val([val]).trigger('change')
|
24
|
+
false
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require ./initialize
|
@@ -2,3 +2,17 @@ div.btn-group > .btn.first-button {
|
|
2
2
|
border-top-left-radius: 0.25rem;
|
3
3
|
border-bottom-left-radius: 0.25rem;
|
4
4
|
}
|
5
|
+
|
6
|
+
.effective-radios.card-deck {
|
7
|
+
.card-header { cursor: pointer; }
|
8
|
+
|
9
|
+
input[type=radio] { display: none; }
|
10
|
+
|
11
|
+
.visible-when-selected { display: none; }
|
12
|
+
.visible-when-unselected { display: block; }
|
13
|
+
|
14
|
+
.active {
|
15
|
+
.visible-when-selected { display: block; }
|
16
|
+
.visible-when-unselected { display: none; }
|
17
|
+
}
|
18
|
+
}
|
@@ -112,7 +112,7 @@ module EffectiveBootstrapHelper
|
|
112
112
|
# = nav_link_to 'Sign In', new_user_session_path
|
113
113
|
# = nav_dropdown 'Settings' do
|
114
114
|
# = nav_link_to 'Account Settings', user_settings_path
|
115
|
-
# =
|
115
|
+
# = nav_divider
|
116
116
|
# = nav_link_to 'Sign In', new_user_session_path, method: :delete
|
117
117
|
def nav_link_to(label, path, opts = {})
|
118
118
|
if @_nav_mode == :dropdown # We insert dropdown-items
|
@@ -1,10 +1,15 @@
|
|
1
1
|
# http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-select
|
2
|
+
|
3
|
+
# buttons: true
|
4
|
+
# cards: true
|
5
|
+
# custom: false
|
6
|
+
|
2
7
|
module Effective
|
3
8
|
module FormInputs
|
4
9
|
class Radios < CollectionInput
|
5
10
|
|
6
11
|
def build_wrapper(&block)
|
7
|
-
tag = buttons? ? :div : :fieldset
|
12
|
+
tag = (buttons? || cards?) ? :div : :fieldset
|
8
13
|
|
9
14
|
if layout == :horizontal
|
10
15
|
content_tag(tag, content_tag(:div, yield, class: 'row'), options[:wrapper])
|
@@ -22,6 +27,8 @@ module Effective
|
|
22
27
|
def build_button_group(&block)
|
23
28
|
if buttons?
|
24
29
|
content_tag(:div, yield, id: button_group_id, class: 'btn-group btn-group-toggle effective-radios', 'data-toggle': 'buttons')
|
30
|
+
elsif cards?
|
31
|
+
content_tag(:div, yield, id: button_group_id, class: 'card-deck effective-radios', 'data-toggle': 'cards')
|
25
32
|
else
|
26
33
|
yield
|
27
34
|
end
|
@@ -43,6 +50,8 @@ module Effective
|
|
43
50
|
def input_html_options
|
44
51
|
if buttons?
|
45
52
|
{ autocomplete: 'off' }
|
53
|
+
elsif cards?
|
54
|
+
{ autocomplete: 'off' }
|
46
55
|
elsif custom?
|
47
56
|
{ class: 'custom-control-input' }
|
48
57
|
else
|
@@ -59,6 +68,8 @@ module Effective
|
|
59
68
|
|
60
69
|
if buttons?
|
61
70
|
content_tag(:label, text, options[:label].merge(for: button_group_id))
|
71
|
+
elsif cards?
|
72
|
+
content_tag(:label, text, options[:label].merge(for: button_group_id))
|
62
73
|
elsif inline?
|
63
74
|
content_tag(:label, text, options[:label])
|
64
75
|
else
|
@@ -74,13 +85,24 @@ module Effective
|
|
74
85
|
opts[:class] = [opts[:class], ('active' if active_item?(builder)), ('first-button' if first_item?) ].compact.join(' ')
|
75
86
|
|
76
87
|
builder.label(opts) { builder.radio_button(id: item_id) + builder.text }
|
88
|
+
elsif cards?
|
89
|
+
opts = item_label_options.merge(for: item_id)
|
90
|
+
opts[:class] = [opts[:class], ('active border-secondary' if active_item?(builder)), ('first-card' if first_item?) ].compact.join(' ')
|
91
|
+
|
92
|
+
if active_item?(builder)
|
93
|
+
builder.label(opts) { builder.radio_button(id: item_id) + builder.text.sub('card-header', 'card-header bg-secondary text-white').html_safe }
|
94
|
+
else
|
95
|
+
builder.label(opts) { builder.radio_button(id: item_id) + builder.text }
|
96
|
+
end
|
77
97
|
else
|
78
98
|
build_item_wrap { builder.radio_button(id: item_id) + builder.label(item_label_options.merge(for: item_id)) }
|
79
99
|
end
|
80
100
|
end
|
81
101
|
|
82
102
|
def build_item_wrap(&block)
|
83
|
-
if
|
103
|
+
if cards?
|
104
|
+
content_tag(:div, yield, class: 'card')
|
105
|
+
elsif custom?
|
84
106
|
content_tag(:div, yield, class: 'custom-control custom-radio ' + (inline? ? 'custom-control-inline' : 'form-group'))
|
85
107
|
else
|
86
108
|
content_tag(:div, yield, class: 'form-check' + (inline? ? ' form-check-inline' : ''))
|
@@ -94,6 +116,8 @@ module Effective
|
|
94
116
|
def item_label_options
|
95
117
|
if buttons?
|
96
118
|
{ class: 'btn btn-outline-secondary' }
|
119
|
+
elsif cards?
|
120
|
+
{ class: 'card' }
|
97
121
|
elsif custom?
|
98
122
|
{ class: 'custom-control-label' }
|
99
123
|
else
|
@@ -106,6 +130,11 @@ module Effective
|
|
106
130
|
@buttons = (options.delete(:buttons) || false)
|
107
131
|
end
|
108
132
|
|
133
|
+
def cards? # default false
|
134
|
+
return @cards unless @cards.nil?
|
135
|
+
@cards= (options.delete(:cards) || false)
|
136
|
+
end
|
137
|
+
|
109
138
|
def button_group_id
|
110
139
|
"#{tag_id}_btn_group"
|
111
140
|
end
|
@@ -3,23 +3,26 @@ module Effective
|
|
3
3
|
class Submit < Effective::FormInput
|
4
4
|
|
5
5
|
def build_input(&block)
|
6
|
-
|
6
|
+
tags = [
|
7
|
+
icon('check', style: 'display: none;'),
|
8
|
+
icon('x', style: 'display: none;'),
|
9
|
+
icon('spinner'),
|
10
|
+
(block_given? ? capture(&block) : content_tag(:button, name, options[:input]))
|
11
|
+
]
|
12
|
+
|
13
|
+
(left? ? tags.reverse.join : tags.join).html_safe
|
7
14
|
end
|
8
15
|
|
9
16
|
def wrapper_options
|
10
|
-
|
11
|
-
left = options.delete(:left) || false
|
12
|
-
center = options.delete(:center) || false
|
13
|
-
right = options.delete(:right) || false
|
14
|
-
right = true unless (left || center)
|
17
|
+
@right = true unless (left? || center? || right?)
|
15
18
|
|
16
19
|
classes = [
|
17
20
|
('row' if layout == :horizontal),
|
18
21
|
'form-group form-actions',
|
19
|
-
('form-actions-bordered' if border),
|
20
|
-
('justify-content-start' if left && layout == :vertical),
|
21
|
-
('justify-content-center' if center && layout == :vertical),
|
22
|
-
('justify-content-end' if right && layout == :vertical)
|
22
|
+
('form-actions-bordered' if border?),
|
23
|
+
('justify-content-start' if left? && layout == :vertical),
|
24
|
+
('justify-content-center' if center? && layout == :vertical),
|
25
|
+
('justify-content-end' if right? && layout == :vertical)
|
23
26
|
].compact.join(' ')
|
24
27
|
|
25
28
|
{ class: classes }
|
@@ -33,6 +36,28 @@ module Effective
|
|
33
36
|
false
|
34
37
|
end
|
35
38
|
|
39
|
+
private
|
40
|
+
|
41
|
+
def border?
|
42
|
+
return @border unless @border.nil?
|
43
|
+
@border = options.key?(:border) ? options.delete(:border) : true
|
44
|
+
end
|
45
|
+
|
46
|
+
def left?
|
47
|
+
return @left unless @left.nil?
|
48
|
+
@left = (options.delete(:left) || false)
|
49
|
+
end
|
50
|
+
|
51
|
+
def center?
|
52
|
+
return @center unless @center.nil?
|
53
|
+
@center = (options.delete(:center) || false)
|
54
|
+
end
|
55
|
+
|
56
|
+
def right?
|
57
|
+
return @right unless @right.nil?
|
58
|
+
@right = (options.delete(:right) || false)
|
59
|
+
end
|
60
|
+
|
36
61
|
def feedback_options
|
37
62
|
# case layout
|
38
63
|
# when :inline
|
@@ -1,7 +1,7 @@
|
|
1
1
|
%p These icons are a combination of #{link_to 'Feather', 'https://feathericons.com/'} and #{link_to 'FontAwesome', 'https://fontawesome.com/'} icons.
|
2
2
|
|
3
3
|
.row.text-center
|
4
|
-
- Dir["#{__dir__}/../../../assets/icons/*.svg"].map { |path| path.split('/').last.chomp('.svg') }.each do |name|
|
4
|
+
- Dir["#{__dir__}/../../../assets/images/icons/*.svg"].map { |path| path.split('/').last.chomp('.svg') }.each do |name|
|
5
5
|
.col-sm-2.my-3
|
6
6
|
= icon(name)
|
7
7
|
%br
|
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.0.
|
4
|
+
version: 0.0.7
|
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: 2018-
|
11
|
+
date: 2018-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: jquery-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: coffee-rails
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -368,7 +382,10 @@ files:
|
|
368
382
|
- app/assets/javascripts/effective_phone/initialize.js.coffee
|
369
383
|
- app/assets/javascripts/effective_phone/input.js
|
370
384
|
- app/assets/javascripts/effective_phone/jquery.maskedInput.js
|
371
|
-
- app/assets/javascripts/effective_price/
|
385
|
+
- app/assets/javascripts/effective_price/initialize.js.coffee
|
386
|
+
- app/assets/javascripts/effective_price/input.js
|
387
|
+
- app/assets/javascripts/effective_radio/initialize.js.coffee
|
388
|
+
- app/assets/javascripts/effective_radio/input.js
|
372
389
|
- app/assets/javascripts/effective_select/initialize.js.coffee
|
373
390
|
- app/assets/javascripts/effective_select/input.js
|
374
391
|
- app/assets/javascripts/effective_select/overrides.js.coffee
|