bootstrap_form_extensions 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +34 -0
  4. data/app/assets/javascripts/bootstrap_form_extensions/arrayed_field.js +70 -0
  5. data/app/assets/javascripts/bootstrap_form_extensions/bootstrap-timepicker.min.js +5 -0
  6. data/app/assets/javascripts/bootstrap_form_extensions/index.js +2 -0
  7. data/app/assets/javascripts/bootstrap_form_extensions/jquery.html5data.min.js +7 -0
  8. data/app/assets/javascripts/bootstrap_form_extensions/scheduler.js +226 -0
  9. data/app/assets/javascripts/bootstrap_form_extensions/select_or_new.js +76 -0
  10. data/app/assets/javascripts/bootstrap_form_extensions/time_picker.js +61 -0
  11. data/app/assets/javascripts/bootstrap_form_extensions/timespan.js +60 -0
  12. data/app/assets/stylesheets/bootstrap_form_extensions/bootstrap-timepicker.min.css +10 -0
  13. data/app/assets/stylesheets/bootstrap_form_extensions/common.css +11 -0
  14. data/app/assets/stylesheets/bootstrap_form_extensions/index.css +4 -0
  15. data/app/assets/stylesheets/bootstrap_form_extensions/scheduler.css +31 -0
  16. data/app/assets/stylesheets/bootstrap_form_extensions/submit_bar.css +4 -0
  17. data/app/views/bootstrap_form_extensions/_submit_bar.html.erb +36 -0
  18. data/lib/bootstrap_form_extensions.rb +22 -0
  19. data/lib/bootstrap_form_extensions/arrayed_field.rb +107 -0
  20. data/lib/bootstrap_form_extensions/date_time_pickers.rb +71 -0
  21. data/lib/bootstrap_form_extensions/helpers.rb +18 -0
  22. data/lib/bootstrap_form_extensions/scheduler.rb +72 -0
  23. data/lib/bootstrap_form_extensions/select_or_new.rb +36 -0
  24. data/lib/bootstrap_form_extensions/submit_bar.rb +65 -0
  25. data/lib/bootstrap_form_extensions/timespan.rb +55 -0
  26. data/lib/bootstrap_form_extensions/version.rb +3 -0
  27. data/lib/tasks/rails_bootstrap_form_extensions_tasks.rake +4 -0
  28. data/test/arrayed_field_test.rb +63 -0
  29. data/test/bootstrap_form_extensions_test.rb +7 -0
  30. data/test/date_time_pickers_test.rb +50 -0
  31. data/test/dummy/README.rdoc +28 -0
  32. data/test/dummy/Rakefile +6 -0
  33. data/test/dummy/app/assets/javascripts/application.js +13 -0
  34. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  35. data/test/dummy/app/controllers/application_controller.rb +5 -0
  36. data/test/dummy/app/helpers/application_helper.rb +2 -0
  37. data/test/dummy/app/models/category.rb +7 -0
  38. data/test/dummy/app/models/thing.rb +46 -0
  39. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  40. data/test/dummy/bin/bundle +3 -0
  41. data/test/dummy/bin/rails +4 -0
  42. data/test/dummy/bin/rake +4 -0
  43. data/test/dummy/bin/setup +29 -0
  44. data/test/dummy/config.ru +4 -0
  45. data/test/dummy/config/application.rb +26 -0
  46. data/test/dummy/config/boot.rb +5 -0
  47. data/test/dummy/config/database.yml +25 -0
  48. data/test/dummy/config/environment.rb +5 -0
  49. data/test/dummy/config/environments/development.rb +41 -0
  50. data/test/dummy/config/environments/production.rb +79 -0
  51. data/test/dummy/config/environments/test.rb +42 -0
  52. data/test/dummy/config/initializers/assets.rb +11 -0
  53. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  54. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  55. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  56. data/test/dummy/config/initializers/inflections.rb +16 -0
  57. data/test/dummy/config/initializers/mime_types.rb +4 -0
  58. data/test/dummy/config/initializers/session_store.rb +3 -0
  59. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  60. data/test/dummy/config/locales/en.yml +23 -0
  61. data/test/dummy/config/routes.rb +3 -0
  62. data/test/dummy/config/secrets.yml +22 -0
  63. data/test/dummy/db/migrate/20150918185031_create_things.rb +9 -0
  64. data/test/dummy/db/migrate/20150924203053_add_timespan.rb +7 -0
  65. data/test/dummy/db/migrate/20150929213249_add_arrayed_field.rb +7 -0
  66. data/test/dummy/db/migrate/20151006171627_add_another_arrayed_field.rb +7 -0
  67. data/test/dummy/db/migrate/20151006181943_add_json_field.rb +7 -0
  68. data/test/dummy/db/migrate/20151007213131_add_scheduler_field.rb +7 -0
  69. data/test/dummy/db/migrate/20151030194330_add_date_time.rb +7 -0
  70. data/test/dummy/db/migrate/20151106165522_add_category.rb +10 -0
  71. data/test/dummy/db/schema.rb +31 -0
  72. data/test/dummy/db/test.sqlite3 +0 -0
  73. data/test/dummy/log/development.log +0 -0
  74. data/test/dummy/log/test.log +1997 -0
  75. data/test/dummy/public/404.html +67 -0
  76. data/test/dummy/public/422.html +67 -0
  77. data/test/dummy/public/500.html +66 -0
  78. data/test/dummy/public/favicon.ico +0 -0
  79. data/test/dummy/test/fixtures/things.yml +8 -0
  80. data/test/javascripts/arrayed_field_spec.js +101 -0
  81. data/test/javascripts/helpers/function_bind_polyfill_for_phantomjs.js +32 -0
  82. data/test/javascripts/helpers/jasmine-jquery.js +838 -0
  83. data/test/javascripts/scheduler_spec.js +354 -0
  84. data/test/javascripts/select_or_new_spec.js +113 -0
  85. data/test/javascripts/support/jasmine.yml +127 -0
  86. data/test/javascripts/support/jasmine_helper.rb +19 -0
  87. data/test/javascripts/time_picker_spec.js +42 -0
  88. data/test/javascripts/timespan_spec.js +81 -0
  89. data/test/javascripts/vendor/bootstrap.min.js +7 -0
  90. data/test/javascripts/vendor/jquery-2.1.4.min.js +4 -0
  91. data/test/scheduler_serializer_test.rb +200 -0
  92. data/test/scheduler_test.rb +15 -0
  93. data/test/select_or_new_test.rb +37 -0
  94. data/test/submit_bar_test.rb +112 -0
  95. data/test/test_helper.rb +37 -0
  96. data/test/timespan_test.rb +47 -0
  97. metadata +291 -0
@@ -0,0 +1,61 @@
1
+ +function ($) {
2
+ 'use strict';
3
+
4
+
5
+ // TIME PICKER CLASS DEFINITION
6
+ // ============================
7
+
8
+ var TimePicker = function (element, options) {
9
+ var defaults = {
10
+ minuteStep: 1,
11
+ showSeconds: true,
12
+ secondStep: 1,
13
+ showMeridian: false,
14
+ defaultTime: false
15
+ }
16
+ $(element).timepicker($.extend(defaults, options))
17
+ }
18
+
19
+ TimePicker.VERSION = '1.0.0'
20
+
21
+
22
+ // TIME PICKER PLUGIN DEFINITION
23
+ // =============================
24
+
25
+ function Plugin(options) {
26
+ return $(this).each(function () {
27
+ var $this = $(this)
28
+ var data = $this.data('bsfe.timepicker')
29
+
30
+ if (!data) $this.data('bsfe.timepicker', (data = new TimePicker(this, options)))
31
+ })
32
+ }
33
+
34
+ var old = $.fn.timePicker
35
+
36
+ $.fn.timePicker = Plugin
37
+ $.fn.timePicker.Constructor = TimePicker
38
+
39
+
40
+ // TIME PICKER NO CONFLICT
41
+ // =======================
42
+
43
+ $.fn.timePicker.noConflict = function () {
44
+ $.fn.timePicker = old
45
+ return this
46
+ }
47
+
48
+
49
+ // TIME PICKER DATA-API
50
+ // ====================
51
+
52
+ $(window).on('load page:load page:restore', function () {
53
+ $('[data-provide="timepicker"]').each(function () {
54
+ var $picker = $(this)
55
+ var options = $picker.html5data('time')
56
+
57
+ Plugin.call($picker, options)
58
+ })
59
+ })
60
+
61
+ }(jQuery);
@@ -0,0 +1,60 @@
1
+ +function ($) {
2
+ 'use strict';
3
+
4
+ // TIMESPAN CLASS DEFINITION
5
+ // =========================
6
+
7
+ var Timespan = function (element) {
8
+ this.$container = $(element)
9
+ this.$container.on('change.bsfe.timespan.data-api', '.timespan-quantity, .timespan-unit', this.updateSeconds.bind(this))
10
+ }
11
+
12
+ Timespan.VERSION = '1.0.0'
13
+
14
+ Timespan.prototype.updateSeconds = function (e) {
15
+ var hidden = this.$container.find('.timespan-seconds')
16
+ var quantity = this.$container.find('.timespan-quantity')
17
+ var unit = this.$container.find('.timespan-unit')
18
+ var seconds = +quantity.val() * +unit.val()
19
+
20
+ hidden.val(seconds)
21
+ }
22
+
23
+
24
+ // TIMESPAN PLUGIN DEFINITION
25
+ // ==========================
26
+
27
+ function Plugin() {
28
+ return $(this).each(function () {
29
+ var $this = $(this)
30
+ var data = $this.data('bsfe.timespan')
31
+
32
+ if (!data) $this.data('bsfe.timespan', (data = new Timespan(this)))
33
+ })
34
+ }
35
+
36
+ var old = $.fn.timespan
37
+
38
+ $.fn.timespan = Plugin
39
+ $.fn.timespan.Constructor = Timespan
40
+
41
+
42
+ // TIMESPAN NO CONFLICT
43
+ // ====================
44
+
45
+ $.fn.timespan.noConflict = function () {
46
+ $.fn.timespan = old
47
+ return this
48
+ }
49
+
50
+
51
+ // TIMESPAN DATA-API
52
+ // =================
53
+
54
+ $(window).on('load page:load page:restore', function () {
55
+ $('[data-timespan]').each(function () {
56
+ Plugin.call(this)
57
+ })
58
+ })
59
+
60
+ }(jQuery);
@@ -0,0 +1,10 @@
1
+ /*!
2
+ * Timepicker Component for Twitter Bootstrap
3
+ *
4
+ * Copyright 2013 Joris de Wit
5
+ *
6
+ * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */.bootstrap-timepicker{position:relative}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu{left:auto;right:0}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:before{left:auto;right:12px}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:after{left:auto;right:13px}.bootstrap-timepicker .input-group-addon{cursor:pointer}.bootstrap-timepicker .input-group-addon i{display:inline-block;width:16px;height:16px}.bootstrap-timepicker-widget.dropdown-menu{padding:4px}.bootstrap-timepicker-widget.dropdown-menu.open{display:inline-block}.bootstrap-timepicker-widget.dropdown-menu:before{border-bottom:7px solid rgba(0,0,0,0.2);border-left:7px solid transparent;border-right:7px solid transparent;content:"";display:inline-block;position:absolute}.bootstrap-timepicker-widget.dropdown-menu:after{border-bottom:6px solid #fff;border-left:6px solid transparent;border-right:6px solid transparent;content:"";display:inline-block;position:absolute}.bootstrap-timepicker-widget.timepicker-orient-left:before{left:6px}.bootstrap-timepicker-widget.timepicker-orient-left:after{left:7px}.bootstrap-timepicker-widget.timepicker-orient-right:before{right:6px}.bootstrap-timepicker-widget.timepicker-orient-right:after{right:7px}.bootstrap-timepicker-widget.timepicker-orient-top:before{top:-7px}.bootstrap-timepicker-widget.timepicker-orient-top:after{top:-6px}.bootstrap-timepicker-widget.timepicker-orient-bottom:before{bottom:-7px;border-bottom:0;border-top:7px solid #999}.bootstrap-timepicker-widget.timepicker-orient-bottom:after{bottom:-6px;border-bottom:0;border-top:6px solid #fff}.bootstrap-timepicker-widget a.btn,.bootstrap-timepicker-widget input{border-radius:4px}.bootstrap-timepicker-widget table{width:100%;margin:0}.bootstrap-timepicker-widget table td{text-align:center;height:30px;margin:0;padding:2px}.bootstrap-timepicker-widget table td:not(.separator){min-width:30px}.bootstrap-timepicker-widget table td span{width:100%}.bootstrap-timepicker-widget table td a{border:1px transparent solid;width:100%;display:inline-block;margin:0;padding:8px 0;outline:0;color:#333}.bootstrap-timepicker-widget table td a:hover{text-decoration:none;background-color:#eee;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border-color:#ddd}.bootstrap-timepicker-widget table td a i{margin-top:2px;font-size:18px}.bootstrap-timepicker-widget table td input{width:25px;margin:0;text-align:center}.bootstrap-timepicker-widget .modal-content{padding:4px}@media(min-width:767px){.bootstrap-timepicker-widget.modal{width:200px;margin-left:-100px}}@media(max-width:767px){.bootstrap-timepicker{width:100%}.bootstrap-timepicker .dropdown-menu{width:100%}}
@@ -0,0 +1,11 @@
1
+ // Vertical space
2
+ .voffset { margin-top: 2px; }
3
+ .voffset1 { margin-top: 5px; }
4
+ .voffset2 { margin-top: 10px; }
5
+ .voffset3 { margin-top: 15px; }
6
+ .voffset4 { margin-top: 30px; }
7
+ .voffset5 { margin-top: 40px; }
8
+ .voffset6 { margin-top: 60px; }
9
+ .voffset7 { margin-top: 80px; }
10
+ .voffset8 { margin-top: 100px; }
11
+ .voffset9 { margin-top: 150px; }
@@ -0,0 +1,4 @@
1
+ /*
2
+ *= require bootstrap-datepicker3
3
+ *= require_tree .
4
+ */
@@ -0,0 +1,31 @@
1
+ .scheduler-badge {
2
+ }
3
+ .scheduler-badge td {
4
+ cursor: pointer;
5
+ width: 13px;
6
+ line-height: 0.5em;
7
+ border: 1px solid grey;
8
+ }
9
+ .scheduler-badge td.on {
10
+ background-color: #dff0d8;
11
+ }
12
+ .scheduler-editor a {
13
+ color: #777777;
14
+ }
15
+ .scheduler-editor th {
16
+ width: 38px;
17
+ text-align: center;
18
+ }
19
+ .scheduler-editor th a {
20
+ font-weight: normal;
21
+ }
22
+ .scheduler-editor td.clickable {
23
+ border: 1px solid lightgrey;
24
+ cursor: pointer;
25
+ }
26
+ .scheduler-editor td.clickable.on {
27
+ background-color: #dff0d8;
28
+ }
29
+ #scheduler-container a {
30
+ cursor: pointer;
31
+ }
@@ -0,0 +1,4 @@
1
+ .submitbar-next-action-menu > li > button.btn-link {
2
+ width: 100%;
3
+ text-align: left;
4
+ }
@@ -0,0 +1,36 @@
1
+ <div class="form-group">
2
+ <div class='pull-left submitbar-left'>
3
+ <% if show_submit_button %>
4
+ <div class="btn-group dropup submitbar-submit-group">
5
+ <button type="submit" class="btn btn-primary submitbar-save"><%= submit_button_text %></button>
6
+ <% if show_submit_menu %>
7
+ <button type="button" class="btn btn-primary dropdown-toggle submitbar-next-action-toggle" data-toggle="dropdown">
8
+ <span class="caret"></span>
9
+ <span class="sr-only">Toggle Dropdown</span>
10
+ </button>
11
+ <ul class="dropdown-menu submitbar-next-action-menu" role="menu">
12
+ <% if show_submit_and_dup_button %>
13
+ <li><button type="submit" name="next_action" class="btn-link submitbar-duplicate" value="duplicate"><%= submit_button_text %> and duplicate</button></li>
14
+ <% end %>
15
+ <% if show_submit_and_new_button %>
16
+ <li><button type="submit" name="next_action" class="btn-link submitbar-new" value="new"><%= submit_button_text %> and new</button></li>
17
+ <% end %>
18
+ </ul>
19
+ <% end %>
20
+ </div>
21
+ <% end %>
22
+ <% extra_buttons.each do |text:, url:, options:| %>
23
+ <%= link_to text, url, options %>
24
+ <% end %>
25
+ <% if show_cancel_button %>
26
+ <%= link_to cancel_button_text, cancel_button_url, class: "btn btn-default submitbar-cancel", rel: "nofollow" %>
27
+ <% end %>
28
+ </div>
29
+ <% if right_buttons.any? %>
30
+ <div class='pull-right submitbar-right'>
31
+ <% right_buttons.each do |text:, url:, options:| %>
32
+ <%= link_to text, url, options %>
33
+ <% end %>
34
+ </div>
35
+ <% end %>
36
+ </div>
@@ -0,0 +1,22 @@
1
+ require 'bootstrap_form/form_builder'
2
+ require 'bootstrap_form_extensions/submit_bar'
3
+ require 'bootstrap_form_extensions/timespan'
4
+ require 'bootstrap_form_extensions/arrayed_field'
5
+ require 'bootstrap_form_extensions/scheduler'
6
+ require 'bootstrap_form_extensions/date_time_pickers'
7
+ require 'bootstrap-datepicker-rails'
8
+ require 'bootstrap_form_extensions/select_or_new'
9
+
10
+ module BootstrapFormExtensions
11
+ module Rails
12
+ class Engine < ::Rails::Engine
13
+ end
14
+ end
15
+ end
16
+
17
+ BootstrapForm::FormBuilder.send :include, BootstrapFormExtensions::SubmitBar,
18
+ BootstrapFormExtensions::Timespan,
19
+ BootstrapFormExtensions::ArrayedField,
20
+ BootstrapFormExtensions::Scheduler,
21
+ BootstrapFormExtensions::DateTimePickers,
22
+ BootstrapFormExtensions::SelectOrNew
@@ -0,0 +1,107 @@
1
+ require 'bootstrap_form_extensions/helpers'
2
+
3
+ module BootstrapFormExtensions
4
+
5
+ module ArrayedField
6
+
7
+ include BootstrapFormExtensions::Helpers
8
+
9
+ ARRAYED_HELPERS = %w[ url_field text_field ]
10
+ ARRAYED_HELPERS.each do |method_name|
11
+ define_method "arrayed_#{method_name}" do |field, **options|
12
+ arrayed_field method_name, field, options
13
+ end
14
+ end
15
+
16
+ def arrayed_json_field method, fields, **options
17
+ fields = parse_fields_for_arrayed_json fields
18
+ col_class = options.fetch :col_class, 'col-sm-2'
19
+
20
+ blueprint = fields.map do |field|
21
+ case field[:type]
22
+ when :select
23
+ @template.select_tag nil, @template.options_for_select(field[:options]), class: 'form-control', data: { name: "#{object_name}[#{method}][][#{field[:name]}]" }
24
+ else
25
+ @template.text_field_tag nil, nil, class: 'form-control', placeholder: field[:name], data: { name: "#{object_name}[#{method}][][#{field[:name]}]" }
26
+ end
27
+ end
28
+ blueprint = arrayed_field_row_builder blueprint, col_class: col_class
29
+ blueprint = arrayed_field_blueprint_builder blueprint
30
+
31
+ form_group_builder_for_arrayed_field method, blueprint, options do |values|
32
+ inputs = fields.map do |field|
33
+ case field[:type]
34
+ when :select
35
+ @template.select_tag nil, @template.options_for_select(field[:options], values[field[:name].to_s]), class: 'form-control'
36
+ else
37
+ @template.text_field_tag "#{object_name}[#{method}][][#{field[:name]}]", values[field[:name].to_s], class: 'form-control', placeholder: field[:name]
38
+ end
39
+ end
40
+ arrayed_field_row_builder inputs, col_class: col_class
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def arrayed_field field_type, field, **options
47
+ field_tag = "#{field_type}_tag".to_sym
48
+
49
+ blueprint = @template.send field_tag, nil, nil, class: 'form-control', data: { name: "#{object_name}[#{field}][]" }
50
+ blueprint = arrayed_field_row_builder blueprint
51
+ blueprint = arrayed_field_blueprint_builder blueprint
52
+
53
+ form_group_builder_for_arrayed_field field, blueprint, options do |value|
54
+ input = @template.send field_tag, "#{object_name}[#{field}][]", value, class: 'form-control'
55
+ arrayed_field_row_builder input
56
+ end
57
+ end
58
+
59
+ def parse_fields_for_arrayed_json fields
60
+ fields.map do |field|
61
+ if field.is_a? Hash
62
+ name = field.keys.first
63
+ values = field.values.first
64
+ type = values.fetch :type, :text
65
+ select_options = values.fetch :options, []
66
+ { name: name, type: type, options: select_options }
67
+ else
68
+ { name: field, type: :text }
69
+ end
70
+ end
71
+ end
72
+
73
+ def arrayed_field_row_builder args, col_class: 'col-sm-11'
74
+ args = [ args ].flatten
75
+
76
+ inputs = args.inject(''.html_safe) do |content, input|
77
+ content << content_tag(:div, input, class: col_class)
78
+ end
79
+
80
+ remove_button = @template.link_to glyphicon_tag('trash'), 'javascript:void(0);', class: 'btn btn-default remove-arrayed-field-row'
81
+ remove_button = content_tag :div, remove_button, class: 'col-sm-1'
82
+
83
+ content_tag :div, inputs + remove_button, class: 'row voffset1'
84
+ end
85
+
86
+ def arrayed_field_blueprint_builder row
87
+ content_tag :div, row, class: 'blueprint-for-arrayed-field', style: 'display:none;'
88
+ end
89
+
90
+ def form_group_builder_for_arrayed_field method, blueprint, options, &row_builder_block
91
+ add_button = @template.link_to glyphicon_tag('plus'), 'javascript:void(0);', class: 'btn btn-default add-arrayed-field-row'
92
+ add_button = content_tag :div, add_button, class: 'col-sm-12'
93
+ add_button = content_tag :div, add_button, class: 'row voffset1'
94
+
95
+ rows = object.send(method) rescue []
96
+ rows = [] unless rows.is_a? Array
97
+ rows = rows.inject(''.html_safe) { |content, value| content << row_builder_block.call(value) }
98
+ rows = content_tag :div, rows, class: 'arrayed-field-rows'
99
+
100
+ form_group_builder method, options do
101
+ content_tag :div, blueprint + rows + add_button, data: { arrayed_field: true }
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ end
@@ -0,0 +1,71 @@
1
+ module BootstrapFormExtensions
2
+
3
+ module DateTimePickers
4
+
5
+ include BootstrapFormExtensions::Helpers
6
+
7
+ def date_time_picker attribute, **options
8
+ name = attribute.to_s.sub /_at$/, ''
9
+ date_method = options.delete(:date_method) { "#{name}_date".to_sym }
10
+ time_method = options.delete(:time_method) { "#{name}_time".to_sym }
11
+
12
+ date_options = options.delete(:date_options) { {} }
13
+ time_options = options.delete(:time_options) { {} }
14
+ date_options[:class] = [ 'form-control', date_options[:class] ].compact.uniq.join(' ')
15
+ time_options[:class] = [ 'form-control', time_options[:class] ].compact.uniq.join(' ')
16
+
17
+ # TODO: Replace for these commented out lines, once this pull request is merged: https://github.com/bootstrap-ruby/rails-bootstrap-forms/pull/238
18
+ # options[:wrapper] ||= {}
19
+ # options[:wrapper].merge! inline: true
20
+ options[:control_col] = 'col-sm-10 form-inline'
21
+
22
+ date_picker_html = content_tag :div, date_picker_builder(date_method, date_options), class: 'form-group'
23
+ time_picker_html = content_tag :div, time_picker_builder(time_method, time_options), class: 'form-group'
24
+ form_group_builder(attribute, options) { date_picker_html + '&nbsp;&nbsp;'.html_safe + time_picker_html }
25
+ end
26
+
27
+ def date_picker method, **options
28
+ form_group_builder(method, options) { date_picker_builder method, options }
29
+ end
30
+
31
+ def time_picker method, **options
32
+ form_group_builder(method, options) { time_picker_builder method, options }
33
+ end
34
+
35
+ private
36
+
37
+ def date_picker_builder method, **options
38
+ widget_options = {
39
+ provide: 'datepicker',
40
+ date_today_highlight: true,
41
+ date_format: 'yyyy-mm-dd',
42
+ date_today_btn: 'linked',
43
+ date_autoclose: true,
44
+ date_disable_touch_keyboard: true,
45
+ date_enable_on_readonly: false,
46
+ date_show_on_focus: false
47
+ }
48
+ widget = options.delete(:widget) { {} }
49
+ widget.each { |option, value| widget_options["date_#{option.to_s.underscore}".to_sym] = value }
50
+
51
+ text = self.text_field_without_bootstrap method, { size: 10 }.merge(options)
52
+ icon = content_tag :span, glyphicon_tag('calendar'), class: 'input-group-addon'
53
+ content_tag :div, text + icon, class: 'input-group date', data: widget_options
54
+ end
55
+
56
+ def time_picker_builder method, **options
57
+ widget_options = { provide: 'timepicker' }
58
+ widget = options.delete(:widget) { {} }
59
+ widget.each { |option, value| widget_options["time_#{option.to_s.underscore}".to_sym] = value }
60
+
61
+ options.reverse_merge! size: 8, data: {}
62
+ options[:data].merge! widget_options
63
+
64
+ text = self.text_field_without_bootstrap method, options
65
+ icon = content_tag :span, glyphicon_tag('time'), class: 'input-group-addon'
66
+ content_tag :div, text + icon, class: 'input-group bootstrap-timepicker'
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,18 @@
1
+ module BootstrapFormExtensions
2
+
3
+ module Helpers
4
+
5
+ def glyphicon_tag *icons
6
+ options = icons.extract_options!
7
+ options[:class] = options.fetch :class, ''
8
+ options[:class] << " glyphicon " << icons.flatten.map{ |icon| "glyphicon-#{icon}"}.join(' ')
9
+ content_tag :i, nil, options
10
+ end
11
+
12
+ def true? value
13
+ value.to_s.match(/(true|t|yes|y|1)$/i).present?
14
+ end
15
+
16
+ end
17
+
18
+ end