bootstrap_form 5.0.0 → 5.2.0
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/.github/workflows/ruby.yml +31 -16
- data/.gitignore +15 -3
- data/.rubocop.yml +3 -1
- data/.yarnrc +5 -0
- data/CHANGELOG.md +53 -2
- data/CONTRIBUTING.md +110 -22
- data/Dangerfile +5 -5
- data/Dockerfile +6 -11
- data/Gemfile +5 -23
- data/README.md +805 -54
- data/RELEASING.md +16 -10
- data/Rakefile +7 -6
- data/bootstrap_form.gemspec +7 -10
- data/docker-compose.yml +9 -48
- data/gemfiles/6.0.gemfile +1 -1
- data/gemfiles/6.1.gemfile +1 -1
- data/gemfiles/7.0.gemfile +5 -0
- data/gemfiles/common.gemfile +28 -0
- data/gemfiles/edge.gemfile +3 -2
- data/lib/bootstrap_form/components/hints.rb +13 -4
- data/lib/bootstrap_form/components/labels.rb +4 -10
- data/lib/bootstrap_form/components/validation.rb +36 -19
- data/lib/bootstrap_form/form_builder.rb +9 -6
- data/lib/bootstrap_form/form_group.rb +15 -8
- data/lib/bootstrap_form/form_group_builder.rb +10 -7
- data/lib/bootstrap_form/helpers/bootstrap.rb +12 -9
- data/lib/bootstrap_form/helpers/field.rb +26 -0
- data/lib/bootstrap_form/helpers.rb +1 -0
- data/lib/bootstrap_form/inputs/check_box.rb +26 -8
- data/lib/bootstrap_form/inputs/collection_check_boxes.rb +5 -1
- data/lib/bootstrap_form/inputs/collection_select.rb +1 -1
- data/lib/bootstrap_form/inputs/grouped_collection_select.rb +1 -1
- data/lib/bootstrap_form/inputs/radio_button.rb +15 -10
- data/lib/bootstrap_form/version.rb +2 -1
- data/lib/bootstrap_form.rb +1 -1
- metadata +17 -88
- data/demo/.postcssrc.yml +0 -3
- data/demo/README.md +0 -17
- data/demo/Rakefile +0 -6
- data/demo/app/assets/config/manifest.js +0 -1
- data/demo/app/assets/stylesheets/actiontext.scss +0 -38
- data/demo/app/assets/stylesheets/application.scss +0 -1
- data/demo/app/controllers/application_controller.rb +0 -2
- data/demo/app/controllers/bootstrap_controller.rb +0 -14
- data/demo/app/helpers/bootstrap_helper.rb +0 -27
- data/demo/app/javascript/channels/consumer.js +0 -6
- data/demo/app/javascript/channels/index.js +0 -5
- data/demo/app/javascript/packs/application.js +0 -11
- data/demo/app/models/address.rb +0 -3
- data/demo/app/models/application_record.rb +0 -3
- data/demo/app/models/faux_user.rb +0 -9
- data/demo/app/models/super_user.rb +0 -2
- data/demo/app/models/user.rb +0 -11
- data/demo/app/views/active_storage/blobs/_blob.html.erb +0 -14
- data/demo/app/views/bootstrap/form.html.erb +0 -67
- data/demo/app/views/layouts/application.html.erb +0 -68
- data/demo/bin/bundle +0 -3
- data/demo/bin/rails +0 -4
- data/demo/bin/rake +0 -4
- data/demo/bin/setup +0 -36
- data/demo/bin/update +0 -31
- data/demo/bin/webpack +0 -15
- data/demo/bin/webpack-dev-server +0 -15
- data/demo/bin/yarn +0 -11
- data/demo/config/application.rb +0 -21
- data/demo/config/boot.rb +0 -5
- data/demo/config/database.yml +0 -21
- data/demo/config/environment.rb +0 -5
- data/demo/config/environments/development.rb +0 -60
- data/demo/config/environments/production.rb +0 -48
- data/demo/config/environments/test.rb +0 -46
- data/demo/config/initializers/application_controller_renderer.rb +0 -8
- data/demo/config/initializers/assets.rb +0 -14
- data/demo/config/initializers/backtrace_silencers.rb +0 -7
- data/demo/config/initializers/cookies_serializer.rb +0 -5
- data/demo/config/initializers/filter_parameter_logging.rb +0 -4
- data/demo/config/initializers/inflections.rb +0 -16
- data/demo/config/initializers/mime_types.rb +0 -4
- data/demo/config/initializers/wrap_parameters.rb +0 -14
- data/demo/config/locales/en.yml +0 -33
- data/demo/config/puma.rb +0 -56
- data/demo/config/routes.rb +0 -5
- data/demo/config/spring.rb +0 -6
- data/demo/config/storage.yml +0 -35
- data/demo/config/webpack/development.js +0 -5
- data/demo/config/webpack/environment.js +0 -3
- data/demo/config/webpack/production.js +0 -5
- data/demo/config/webpack/test.js +0 -5
- data/demo/config/webpacker.yml +0 -92
- data/demo/config.ru +0 -5
- data/demo/db/schema.rb +0 -69
- data/demo/log/.keep +0 -0
- data/demo/package.json +0 -17
- data/demo/public/favicon.ico +0 -0
- data/demo/test/fixtures/action_text/rich_texts.yml +0 -4
- data/demo/yarn.lock +0 -6257
- data/gemfiles/5.2.gemfile +0 -4
data/RELEASING.md
CHANGED
@@ -6,20 +6,26 @@ Follow these steps to release a new version of bootstrap_form to rubygems.org.
|
|
6
6
|
|
7
7
|
* You must have commit rights to the bootstrap_form repository.
|
8
8
|
* You must have push rights for the bootstrap_form gem on rubygems.org.
|
9
|
-
* You must be using Ruby
|
10
|
-
* Your GitHub credentials must be available to Chandler via `~/.netrc` or an environment variable, [as explained here](https://github.com/mattbrictson/chandler#2-configure-credentials).
|
9
|
+
* You must be using a Ruby version that is not end-of-life.
|
11
10
|
|
12
11
|
## How to release
|
13
12
|
|
14
|
-
1. Run `bundle
|
15
|
-
2. **Ensure the tests are passing by running
|
13
|
+
1. Run `BUNDLE_GEMFILE=gemfiles/7.0.gemfile bundle update` to make sure that you have all the gems necessary for testing and releasing.
|
14
|
+
2. **Ensure the tests are passing by running the tests**
|
15
|
+
|
16
|
+
(There should be no errors or warnings.)
|
17
|
+
|
18
|
+
BUNDLE_GEMFILE=gemfiles/7.0.gemfile bundle exec rake test
|
19
|
+
|
20
|
+
2. **Ensure the demo tests are passing by running**
|
21
|
+
|
22
|
+
cd demo
|
23
|
+
bundle update
|
24
|
+
bundle exec rake test:all
|
25
|
+
|
16
26
|
3. Determine which would be the correct next version number according to [semver](http://semver.org/).
|
17
27
|
4. Update the version in `./lib/bootstrap_form/version.rb`.
|
18
|
-
5. Update the `CHANGELOG.md` (
|
19
|
-
* Rename the Pending Release section to `[version][] (date)` with appropriate values `version` and `date`
|
20
|
-
* Remove the "Your contribution here!" bullets from the release notes
|
21
|
-
* Add a new Pending Release section at the top of the file with a template for contributors to fill in, including "Your contribution here!" bullets
|
22
|
-
* Add the appropriate GitHub diff links to the footer of the document
|
28
|
+
5. Update the GitHub diff links at the beginning of `CHANGELOG.md` (The pattern should be obvious when you look at them).
|
23
29
|
6. Update the installation instructions in `README.md` to use the new version.
|
24
30
|
7. Commit the CHANGELOG and version changes in a single commit; the message should be "Preparing vX.Y.Z" where `X.Y.Z` is the version being released.
|
25
|
-
8. Run `bundle exec rake release`; this will tag, push to GitHub, publish to rubygems.org
|
31
|
+
8. Run `bundle exec rake release`; this will tag, push to GitHub, and publish to rubygems.org.
|
data/Rakefile
CHANGED
@@ -17,12 +17,13 @@ end
|
|
17
17
|
|
18
18
|
require 'bundler/gem_tasks'
|
19
19
|
|
20
|
-
require
|
20
|
+
require "minitest/test_task"
|
21
21
|
|
22
|
-
|
23
|
-
t.libs <<
|
24
|
-
t.
|
25
|
-
t.
|
22
|
+
Minitest::TestTask.create(:test) do |t|
|
23
|
+
t.libs << "test"
|
24
|
+
t.libs << "lib"
|
25
|
+
t.warning = false
|
26
|
+
t.test_globs = ["test/**/*_test.rb"]
|
26
27
|
end
|
27
28
|
|
28
29
|
# This automatically updates GitHub Releases whenever we `rake release` the gem
|
@@ -34,4 +35,4 @@ end
|
|
34
35
|
desc 'Run RuboCop checks'
|
35
36
|
RuboCop::RakeTask.new(:rubocop)
|
36
37
|
|
37
|
-
task default: %i[test rubocop]
|
38
|
+
task default: %i[test rubocop:autocorrect]
|
data/bootstrap_form.gemspec
CHANGED
@@ -3,31 +3,28 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
3
|
|
4
4
|
require "bootstrap_form/version"
|
5
5
|
|
6
|
-
REQUIRED_RAILS_VERSION = ">= 5.2".freeze
|
7
|
-
|
8
6
|
Gem::Specification.new do |s|
|
9
7
|
s.name = "bootstrap_form"
|
10
8
|
s.version = BootstrapForm::VERSION
|
11
9
|
s.authors = ["Stephen Potenza", "Carlos Lopes"]
|
12
10
|
s.email = ["potenza@gmail.com", "carlos.el.lopes@gmail.com"]
|
13
11
|
s.homepage = "https://github.com/bootstrap-ruby/bootstrap_form"
|
14
|
-
s.summary = "Rails form builder that makes it easy to style forms using "\
|
12
|
+
s.summary = "Rails form builder that makes it easy to style forms using " \
|
15
13
|
"Bootstrap 5"
|
16
|
-
s.description = "bootstrap_form is a rails form builder that makes it super "\
|
14
|
+
s.description = "bootstrap_form is a rails form builder that makes it super " \
|
17
15
|
"easy to create beautiful-looking forms using Bootstrap 5"
|
18
16
|
s.license = "MIT"
|
17
|
+
s.metadata = { "rubygems_mfa_required" => "true" }
|
19
18
|
|
20
19
|
s.files = `git ls-files -z`.split("\x0").reject do |f|
|
21
|
-
f.match(%r{^(test)/})
|
20
|
+
f.match(%r{^(test)/|^(demo)/})
|
22
21
|
end
|
23
22
|
|
24
23
|
s.bindir = "exe"
|
25
24
|
s.require_paths = ["lib"]
|
26
25
|
|
27
|
-
s.required_ruby_version = ">=
|
28
|
-
|
29
|
-
s.add_dependency("actionpack", REQUIRED_RAILS_VERSION)
|
30
|
-
s.add_dependency("activemodel", REQUIRED_RAILS_VERSION)
|
26
|
+
s.required_ruby_version = ">= 3.0"
|
31
27
|
|
32
|
-
s.
|
28
|
+
s.add_dependency("actionpack", BootstrapForm::REQUIRED_RAILS_VERSION)
|
29
|
+
s.add_dependency("activemodel", BootstrapForm::REQUIRED_RAILS_VERSION)
|
33
30
|
end
|
data/docker-compose.yml
CHANGED
@@ -1,70 +1,31 @@
|
|
1
|
-
version: '
|
1
|
+
version: '3.3'
|
2
2
|
|
3
3
|
services:
|
4
4
|
app: &app
|
5
5
|
build:
|
6
6
|
context: .
|
7
7
|
args:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
image: bootstrap-form:0.0.1
|
8
|
+
NODE_MAJOR: "12"
|
9
|
+
YARN_VERSION: "1.22.4"
|
10
|
+
RUBY_VERSION: ${RUBY_VERSION}
|
11
|
+
image: bootstrap-form:latest-$RUBY_VERSION
|
13
12
|
tmpfs:
|
14
13
|
- /tmp
|
15
14
|
|
16
|
-
|
15
|
+
shell: &shell
|
17
16
|
<<: *app
|
18
17
|
stdin_open: true
|
19
18
|
tty: true
|
20
19
|
volumes:
|
21
20
|
- .:/app:cached
|
22
|
-
- rails_cache:/app/tmp/cache
|
23
|
-
- bundle:/usr/local/bundle
|
24
|
-
- node_modules:/app/node_modules
|
25
|
-
- packs:/app/public/packs
|
26
21
|
environment:
|
22
|
+
- SSH_AUTH_SOCK=/ssh-agent
|
27
23
|
- NODE_ENV=development
|
28
24
|
- RAILS_ENV=${RAILS_ENV:-development}
|
29
25
|
- BOOTSNAP_CACHE_DIR=/usr/local/bundle/_bootsnap
|
30
26
|
- WEBPACKER_DEV_SERVER_HOST=webpacker
|
31
27
|
- WEB_CONCURRENCY=1
|
32
28
|
- HISTFILE=/app/.bash_history
|
33
|
-
- EDITOR=vi
|
34
|
-
|
35
|
-
shell:
|
36
|
-
<<: *backend
|
37
|
-
command: /bin/bash
|
38
|
-
ports:
|
39
|
-
- '3000:3000'
|
40
|
-
|
41
|
-
server:
|
42
|
-
<<: *backend
|
43
|
-
command: sh -c "cd demo/app && bundle exec rails server -b 0.0.0.0"
|
44
|
-
ports:
|
45
|
-
- '3000:3000'
|
46
|
-
|
47
|
-
test:
|
48
|
-
<<: *backend
|
49
|
-
command: rake test
|
50
|
-
|
51
|
-
webpacker:
|
52
|
-
<<: *app
|
53
|
-
command: ./bin/webpack-dev-server
|
54
29
|
ports:
|
55
|
-
-
|
56
|
-
|
57
|
-
- .:/app:cached
|
58
|
-
- bundle:/usr/local/bundle
|
59
|
-
- node_modules:/app/node_modules
|
60
|
-
- packs:/app/public/packs
|
61
|
-
environment:
|
62
|
-
- NODE_ENV=${NODE_ENV:-development}
|
63
|
-
- RAILS_ENV=${RAILS_ENV:-development}
|
64
|
-
- WEBPACKER_DEV_SERVER_HOST=0.0.0.0
|
65
|
-
|
66
|
-
volumes:
|
67
|
-
bundle:
|
68
|
-
node_modules:
|
69
|
-
rails_cache:
|
70
|
-
packs:
|
30
|
+
- "3000:3000"
|
31
|
+
command: /bin/bash
|
data/gemfiles/6.0.gemfile
CHANGED
data/gemfiles/6.1.gemfile
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gemspec path: File.dirname(__dir__)
|
4
|
+
|
5
|
+
# To test with different Rails versions, use the files in `./gemfiles`
|
6
|
+
|
7
|
+
group :development do
|
8
|
+
gem "htmlbeautifier"
|
9
|
+
gem "puma"
|
10
|
+
gem "rubocop-performance", require: false
|
11
|
+
gem "rubocop-rails", require: false
|
12
|
+
end
|
13
|
+
|
14
|
+
group :test do
|
15
|
+
gem "diffy"
|
16
|
+
gem "equivalent-xml"
|
17
|
+
gem "mocha"
|
18
|
+
gem "sqlite3"
|
19
|
+
end
|
20
|
+
|
21
|
+
group :development, :test do
|
22
|
+
gem "debug"
|
23
|
+
gem "pry-byebug"
|
24
|
+
end
|
25
|
+
|
26
|
+
group :ci do
|
27
|
+
gem "danger"
|
28
|
+
end
|
data/gemfiles/edge.gemfile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
gems = "#{
|
1
|
+
gems = "#{__dir__}/common.gemfile"
|
2
2
|
eval File.read(gems), binding, gems # rubocop: disable Security/Eval
|
3
3
|
|
4
|
-
gem "rails", git: "https://github.com/rails/rails.git"
|
4
|
+
gem "rails", git: "https://github.com/rails/rails.git", branch: "main"
|
5
|
+
gem "sprockets-rails", require: "sprockets/railtie"
|
@@ -8,7 +8,7 @@ module BootstrapForm
|
|
8
8
|
private
|
9
9
|
|
10
10
|
def generate_help(name, help_text)
|
11
|
-
return if help_text == false
|
11
|
+
return if help_text == false
|
12
12
|
|
13
13
|
help_klass ||= "form-text text-muted"
|
14
14
|
help_text ||= get_help_text_by_i18n_key(name)
|
@@ -20,10 +20,10 @@ module BootstrapForm
|
|
20
20
|
def get_help_text_by_i18n_key(name)
|
21
21
|
return unless object
|
22
22
|
|
23
|
-
partial_scope = if
|
24
|
-
|
23
|
+
partial_scope = if object_class.respond_to?(:model_name)
|
24
|
+
object_class.model_name.name
|
25
25
|
else
|
26
|
-
|
26
|
+
object_class.name
|
27
27
|
end
|
28
28
|
|
29
29
|
# First check for a subkey :html, as it is also accepted by i18n, and the
|
@@ -38,6 +38,15 @@ module BootstrapForm
|
|
38
38
|
help_text
|
39
39
|
end
|
40
40
|
|
41
|
+
def object_class
|
42
|
+
if !object.class.is_a?(ActiveModel::Naming) &&
|
43
|
+
object.respond_to?(:klass) && object.klass.is_a?(ActiveModel::Naming)
|
44
|
+
object.klass
|
45
|
+
else
|
46
|
+
object.class
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
41
50
|
def scoped_help_text(name, partial_scope)
|
42
51
|
underscored_scope = "activerecord.help.#{partial_scope.underscore}"
|
43
52
|
downcased_scope = "activerecord.help.#{partial_scope.downcase}"
|
@@ -24,14 +24,8 @@ module BootstrapForm
|
|
24
24
|
|
25
25
|
def label_classes(name, options, custom_label_col, group_layout)
|
26
26
|
classes = ["form-label", options[:class], label_layout_classes(custom_label_col, group_layout)]
|
27
|
-
|
28
|
-
|
29
|
-
when true
|
30
|
-
classes << "required"
|
31
|
-
when nil, :default
|
32
|
-
classes << "required" if required_attribute?(object, name)
|
33
|
-
end
|
34
|
-
|
27
|
+
classes << "required" if required_field_options(options, name)[:required]
|
28
|
+
options.delete(:required)
|
35
29
|
classes << "text-danger" if label_errors && error?(name)
|
36
30
|
classes.flatten.compact
|
37
31
|
end
|
@@ -46,9 +40,9 @@ module BootstrapForm
|
|
46
40
|
|
47
41
|
def label_text(name, options)
|
48
42
|
if label_errors && error?(name)
|
49
|
-
(options[:text] || object.class.human_attribute_name(name)).to_s
|
43
|
+
(options[:text] || object.class.human_attribute_name(name)).to_s + " #{get_error_messages(name)}"
|
50
44
|
else
|
51
|
-
options[:text]
|
45
|
+
options[:text] || object&.class.try(:human_attribute_name, name)
|
52
46
|
end
|
53
47
|
end
|
54
48
|
end
|
@@ -8,35 +8,45 @@ module BootstrapForm
|
|
8
8
|
private
|
9
9
|
|
10
10
|
def error?(name)
|
11
|
-
object.respond_to?(:errors) &&
|
11
|
+
name && object.respond_to?(:errors) && (object.errors[name].any? || association_error?(name))
|
12
|
+
end
|
13
|
+
|
14
|
+
def association_error?(name)
|
15
|
+
object.class.reflections.any? do |association_name, a|
|
16
|
+
next unless a.is_a?(ActiveRecord::Reflection::BelongsToReflection)
|
17
|
+
next unless a.foreign_key == name.to_s
|
18
|
+
|
19
|
+
object.errors[association_name].any?
|
20
|
+
end
|
12
21
|
end
|
13
22
|
|
14
23
|
def required_attribute?(obj, attribute)
|
15
24
|
return false unless obj && attribute
|
16
25
|
|
17
26
|
target = obj.instance_of?(Class) ? obj : obj.class
|
27
|
+
return false unless target.respond_to? :validators_on
|
18
28
|
|
19
|
-
target_validators
|
20
|
-
|
21
|
-
else
|
22
|
-
[]
|
23
|
-
end
|
24
|
-
|
25
|
-
presence_validator?(target_validators)
|
29
|
+
presence_validator?(target_validators(target, attribute)) ||
|
30
|
+
required_association?(target, attribute)
|
26
31
|
end
|
27
32
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
has_presence_validator |= target_validators.include?(
|
35
|
-
ActiveRecord::Validations::PresenceValidator
|
36
|
-
)
|
33
|
+
def required_association?(target, attribute)
|
34
|
+
target.reflections.find do |name, a|
|
35
|
+
next unless a.is_a?(ActiveRecord::Reflection::BelongsToReflection)
|
36
|
+
next unless a.foreign_key == attribute.to_s
|
37
|
+
|
38
|
+
presence_validator?(target_validators(target, name))
|
37
39
|
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def target_validators(target, attribute)
|
43
|
+
target.validators_on(attribute).map(&:class)
|
44
|
+
end
|
38
45
|
|
39
|
-
|
46
|
+
def presence_validator?(target_validators)
|
47
|
+
target_validators.include?(ActiveModel::Validations::PresenceValidator) ||
|
48
|
+
(defined?(ActiveRecord::Validations::PresenceValidator) &&
|
49
|
+
target_validators.include?(ActiveRecord::Validations::PresenceValidator))
|
40
50
|
end
|
41
51
|
|
42
52
|
def inline_error?(name)
|
@@ -54,7 +64,14 @@ module BootstrapForm
|
|
54
64
|
end
|
55
65
|
|
56
66
|
def get_error_messages(name)
|
57
|
-
object.errors[name]
|
67
|
+
messages = object.errors[name]
|
68
|
+
object.class.reflections.each do |association_name, a|
|
69
|
+
next unless a.is_a?(ActiveRecord::Reflection::BelongsToReflection)
|
70
|
+
next unless a.foreign_key == name.to_s
|
71
|
+
|
72
|
+
messages << object.errors[association_name]
|
73
|
+
end
|
74
|
+
messages.join(", ")
|
58
75
|
end
|
59
76
|
end
|
60
77
|
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# require 'bootstrap_form/aliasing'
|
2
2
|
|
3
|
+
# rubocop:disable Metrics/ClassLength
|
3
4
|
module BootstrapForm
|
4
5
|
class FormBuilder < ActionView::Helpers::FormBuilder
|
5
6
|
attr_reader :layout, :label_col, :control_col, :has_error, :inline_errors,
|
6
7
|
:label_errors, :acts_like_form_tag
|
7
8
|
|
9
|
+
include BootstrapForm::Helpers::Field
|
8
10
|
include BootstrapForm::Helpers::Bootstrap
|
9
11
|
|
10
12
|
include BootstrapForm::FormGroupBuilder
|
@@ -67,13 +69,14 @@ module BootstrapForm
|
|
67
69
|
|
68
70
|
return unless options[:layout] == :inline
|
69
71
|
|
70
|
-
options[:html][:class] =
|
72
|
+
options[:html][:class] =
|
73
|
+
([*options[:html][:class]&.split(/\s+/)] + %w[row row-cols-auto g-3 align-items-center])
|
74
|
+
.compact.uniq.join(" ")
|
71
75
|
end
|
72
76
|
|
73
77
|
def fields_for_with_bootstrap(record_name, record_object=nil, fields_options={}, &block)
|
74
78
|
fields_options = fields_for_options(record_object, fields_options)
|
75
|
-
record_object.is_a?(Hash) && record_object.extractable_options?
|
76
|
-
record_object = nil
|
79
|
+
record_object = nil if record_object.is_a?(Hash) && record_object.extractable_options?
|
77
80
|
fields_for_without_bootstrap(record_name, record_object, fields_options, &block)
|
78
81
|
end
|
79
82
|
|
@@ -87,8 +90,7 @@ module BootstrapForm
|
|
87
90
|
|
88
91
|
def fields_for_options(record_object, fields_options)
|
89
92
|
field_options = fields_options
|
90
|
-
record_object.is_a?(Hash) && record_object.extractable_options?
|
91
|
-
field_options = record_object
|
93
|
+
field_options = record_object if record_object.is_a?(Hash) && record_object.extractable_options?
|
92
94
|
%i[layout control_col inline_errors label_errors].each do |option|
|
93
95
|
field_options[option] ||= options[option]
|
94
96
|
end
|
@@ -114,7 +116,7 @@ module BootstrapForm
|
|
114
116
|
end
|
115
117
|
|
116
118
|
def hide_class
|
117
|
-
"
|
119
|
+
"visually-hidden" # still accessible for screen readers
|
118
120
|
end
|
119
121
|
|
120
122
|
def control_class
|
@@ -130,3 +132,4 @@ module BootstrapForm
|
|
130
132
|
end
|
131
133
|
end
|
132
134
|
end
|
135
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -32,15 +32,18 @@ module BootstrapForm
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
def form_group_content(label, help_text, options, &block)
|
35
|
+
def form_group_content(label, help_text, options, &block) # rubocop:disable Metrics/AbcSize
|
36
|
+
label ||= ActiveSupport::SafeBuffer.new
|
36
37
|
if group_layout_horizontal?(options[:layout])
|
37
|
-
|
38
|
+
label + tag.div(capture(&block) + help_text, class: form_group_control_class(options))
|
38
39
|
else
|
40
|
+
content = ActiveSupport::SafeBuffer.new
|
39
41
|
# Floating labels need to be rendered after the field
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
42
|
+
content << label unless options[:floating]
|
43
|
+
content << capture(&block)
|
44
|
+
content << label if options[:floating]
|
45
|
+
content << help_text if help_text
|
46
|
+
content
|
44
47
|
end
|
45
48
|
end
|
46
49
|
|
@@ -52,12 +55,16 @@ module BootstrapForm
|
|
52
55
|
end
|
53
56
|
|
54
57
|
def form_group_classes(options)
|
55
|
-
classes = [
|
58
|
+
classes = options[:class] == false ? [] : (options[:class] || form_group_default_class).split
|
56
59
|
classes << "row" if horizontal_group_with_gutters?(options[:layout], classes)
|
57
60
|
classes << "col-auto g-3" if field_inline_override?(options[:layout])
|
58
61
|
classes << feedback_class if options[:icon]
|
59
62
|
classes << "form-floating" if options[:floating]
|
60
|
-
classes
|
63
|
+
classes.presence
|
64
|
+
end
|
65
|
+
|
66
|
+
def form_group_default_class
|
67
|
+
(layout == :inline ? "col" : "mb-3")
|
61
68
|
end
|
62
69
|
|
63
70
|
def horizontal_group_with_gutters?(layout, classes)
|
@@ -28,8 +28,7 @@ module BootstrapForm
|
|
28
28
|
def form_group_builder_options(options, method)
|
29
29
|
options.symbolize_keys!
|
30
30
|
options = convert_form_tag_options(method, options) if acts_like_form_tag
|
31
|
-
options
|
32
|
-
options
|
31
|
+
options.merge!(required_field_options(options, method))
|
33
32
|
end
|
34
33
|
|
35
34
|
def convert_form_tag_options(method, options={})
|
@@ -77,11 +76,15 @@ module BootstrapForm
|
|
77
76
|
classes
|
78
77
|
end
|
79
78
|
|
80
|
-
def form_group_required(options)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
options
|
79
|
+
def form_group_required(options, method)
|
80
|
+
if options[:skip_required]
|
81
|
+
warn "`:skip_required` is deprecated, use `:required: false` instead"
|
82
|
+
false
|
83
|
+
elsif options.key?(:required)
|
84
|
+
options[:required]
|
85
|
+
else
|
86
|
+
required_attribute?(object, method)
|
87
|
+
end
|
85
88
|
end
|
86
89
|
|
87
90
|
def form_group_css_options(method, html_options, options)
|
@@ -8,7 +8,7 @@ module BootstrapForm
|
|
8
8
|
|
9
9
|
def submit(name=nil, options={})
|
10
10
|
setup_css_class "btn btn-secondary", options
|
11
|
-
super
|
11
|
+
layout == :inline ? form_group { super } : super
|
12
12
|
end
|
13
13
|
|
14
14
|
def primary(name=nil, options={}, &block)
|
@@ -27,17 +27,20 @@ module BootstrapForm
|
|
27
27
|
return unless object.respond_to?(:errors) && object.errors.full_messages.any?
|
28
28
|
|
29
29
|
tag.div class: css do
|
30
|
-
|
31
|
-
|
30
|
+
if options[:error_summary] == false
|
31
|
+
title
|
32
|
+
else
|
33
|
+
tag.p(title) + error_summary
|
34
|
+
end
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
35
38
|
def error_summary
|
36
39
|
return unless object.errors.any?
|
37
40
|
|
38
|
-
tag.ul
|
39
|
-
object.errors.full_messages.
|
40
|
-
|
41
|
+
tag.ul(class: "rails-bootstrap-forms-error-summary") do
|
42
|
+
object.errors.full_messages.reduce(ActiveSupport::SafeBuffer.new) do |acc, error|
|
43
|
+
acc << tag.li(error)
|
41
44
|
end
|
42
45
|
end
|
43
46
|
end
|
@@ -79,12 +82,12 @@ module BootstrapForm
|
|
79
82
|
end
|
80
83
|
|
81
84
|
def prepend_and_append_input(name, options, &block)
|
82
|
-
options = options.extract!(:prepend, :append, :input_group_class)
|
85
|
+
options = options.extract!(:prepend, :append, :input_group_class).compact
|
83
86
|
|
84
87
|
input = capture(&block) || ActiveSupport::SafeBuffer.new
|
85
88
|
|
86
89
|
input = attach_input(options, :prepend) + input + attach_input(options, :append)
|
87
|
-
input
|
90
|
+
input << generate_error(name)
|
88
91
|
options.present? &&
|
89
92
|
input = tag.div(input, class: ["input-group", options[:input_group_class]].compact)
|
90
93
|
input
|
@@ -96,7 +99,7 @@ module BootstrapForm
|
|
96
99
|
end
|
97
100
|
|
98
101
|
def input_group_content(content)
|
99
|
-
return content if
|
102
|
+
return content if content.include?("btn")
|
100
103
|
|
101
104
|
tag.span(content, class: "input-group-text")
|
102
105
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module BootstrapForm
|
2
|
+
module Helpers
|
3
|
+
module Field
|
4
|
+
def required_field_options(options, method)
|
5
|
+
required = required_field?(options, method)
|
6
|
+
{}.tap do |option|
|
7
|
+
option[:required] = required
|
8
|
+
option[:aria] = { required: true } if required
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def required_field?(options, method)
|
15
|
+
if options[:skip_required]
|
16
|
+
warn "`:skip_required` is deprecated, use `:required: false` instead"
|
17
|
+
false
|
18
|
+
elsif options.key?(:required)
|
19
|
+
options[:required]
|
20
|
+
else
|
21
|
+
required_attribute?(object, method)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|