bootstrap_form-5.4.0/0000755000004100000410000000000014620142366014614 5ustar www-datawww-databootstrap_form-5.4.0/.editorconfig0000644000004100000410000000031714620142366017272 0ustar www-datawww-data# editorconfig.org root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false bootstrap_form-5.4.0/.gitignore0000644000004100000410000000214014620142366016601 0ustar www-datawww-data.npm/_update-notifier-last-checked .npm/_cacache/ .npm/_logs/ .npm/_npx .idea log/*.log pkg/ demo/db/*.sqlite3 demo/db/*.sqlite3-shm demo/db/*.sqlite3-wal demo/doc/screenshots/**/*.diff.png demo/log/*.log demo/tmp/ *.gem .rbenv-gemsets *.swp /Gemfile.lock test/gemfiles/*.lock gemfiles/*.lock /.ruby-version Vagrantfile .vagrant **/.yarn/**/cache **/.yarn/install-state.gz **/.yarn/unplugged # For the demo app. # Ignore uploaded files in development. demo/storage/* !demo/storage/.keep demo/public/assets **/.byebug_history # Ignore master key for decrypting credentials and more. demo/config/master.key demo/public/packs demo/public/packs-test demo/app/assets/builds/* !demo/app/assets/builds/.keep demo/node_modules demo/yarn-error.log demo/yarn-debug.log* demo/.yarn-integrity demo/vendor/bundle # For stuff that gets created if using the Dockerfile image .bundle/ .cache/ vendor/bundle # or .local/share/pry/pry_history if you need to be more exact .local/ .irb_history .byebug_history # For Debian images with Bash .bash_history # For Alpine images .ash_history .sqlite_history docker-compose.override.yml bootstrap_form-5.4.0/Dockerfile0000644000004100000410000000107414620142366016610 0ustar www-datawww-dataARG RUBY_VERSION=3.0 ARG DISTRO=bullseye FROM ruby:$RUBY_VERSION-$DISTRO ARG NODE_MAJOR=18 RUN mkdir -p /app ENV HOME /app WORKDIR /app ENV GEM_HOME $HOME/vendor/bundle ENV BUNDLE_APP_CONFIG="$GEM_HOME" ENV PATH ./bin:$GEM_HOME/bin:$PATH RUN (echo 'docker'; echo 'docker') | passwd root # Rails wants a newer version of node than we get with the Debian distro. RUN curl -fsSL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash - && apt-get install -y nodejs RUN corepack enable && corepack prepare yarn@stable --activate RUN apt install -y -q sqlite3 EXPOSE 3000 bootstrap_form-5.4.0/CONTRIBUTING.md0000644000004100000410000003311414620142366017047 0ustar www-datawww-data# Contributing Thanks so much for considering a contribution to bootstrap_form. We love pull requests! We want everyone to feel welcome to contribute. We encourage respectful exchanges of ideas. We govern ourselves with the Contributor Covenant [Code of Conduct](/CODE_OF_CONDUCT.md). There are a number of ways you can contribute to `bootstrap_form`: - Fix a bug or add a new feature - Add to the documentation - Review pull requests ## Code Contributions Here's a quick guide for code contributions: ### 1. Check if issue or feature is available to work on Make sure no one else is working on the same issue or feature. Search the issues and pull requests for anything that looks like the issue or feature you want to address. If no one else is working on your issue or feature, carry on with the following steps. ### 2. (Optional) Create an issue, and wait a few days for someone to respond If you create an issue for your feature request or bug, it gives the maintainers a chance to comment on your ideas before you invest a lot of work on a contribution. It may save you some re-work compared to simply submitting a pull request. It's up to you whether you submit an issue. ### 3. Fork the repo Fork the project. Optionally, create a branch you want to work on. ### 4. Get it running locally - Install the required dependencies with `bundle install` - Run tests via: `bundle exec rake` ### 5. Hack away - Try to keep your changes small. Consider making several smaller pull requests if your changes are extensive. - Don't forget to add necessary tests. - Update the README if necessary. - Add a line to the CHANGELOG for your bug fix or feature. - Read the [Coding Guidelines](#coding-guidelines) section and make sure that `rake lint` doesn't find any offences. You may find the [demo application](#the-demo-application) useful for development and debugging. ### 6. Make a pull request - If you've never made a pull request (PR) before, read [this](https://help.github.com/articles/about-pull-requests/). - If your PR fixes an issues, be sure to put "Fixes #nnn" in the description of the PR (where `nnn` is the issue number). Github will automatically close the issue when the PR is merged. - When the PR is submitted, check if GitHub Actions ran all the tests successfully, and didn't raise any issues. ### 7. Done Somebody will shortly review your pull request and if everything is good, it will be merged into the main branch. Eventually the gem will be published with your changes. ### Coding guidelines This project uses [RuboCop](https://github.com/bbatsov/rubocop) to enforce standard Ruby coding guidelines. - Test that your contribution passes with `rake rubocop`. - RuboCop is also run as part of the full test suite with `bundle exec rake`. - Note the Travis build will fail and your PR cannot be merged if RuboCop finds offences. Note that most editors have plugins to run RuboCop as you type, or when you save a file. You may find it well worth your time to install and configure the RuboCop plugin for your editor. Read the [RuboCop documentation](https://rubocop.readthedocs.io/en/latest/integration_with_other_tools/). ### Supported Versions of Ruby and Rails The goal of `bootstrap_form` is to support all versions of Rails currently supported for bug fixes and security issues. We do not test against versions supported for severe security issues. We test against the minimum [version of Ruby required](https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#ruby-versions) for those versions of Rails. The Ruby on Rails support policy is [here](https://guides.rubyonrails.org/maintenance_policy.html). ### Developing with Docker This repository offers experimental support support for a couple of ways to develop using Docker, if you're interested: - Using `docker-compose`. This way is less tested, and is an attempt to make the Docker container a more complete environment where you can conveniently develop and release the gem. - Using just a simple Dockerfile. This way works for simple testing, but doesn't make it easy to release the gem, among other things. Docker is _not_ required to work on this gem. #### Using `docker-compose` The `docker-compose` approach should link to enough of your networking configuration that you can release the gem. However, you have to do some of the configuration yourself, because it's dependent on your host operating system. First, build the image for whatever version of Ruby you need (typically either the earliest supported version or the latest): ```bash RUBY_VERSION=3.2 docker-compose build ``` You can run a shell in a Docker container that pretty much should behave like a Debian distribution with: ```bash docker-compose run --service-ports shell ``` (`--service-ports` exposes port 3000 so you can browse to the demo app on `localhost:3000`. If you just want to run a one-off command, or run the test suite, leave off the `--service-ports`.) The following instructions work for an Ubuntu host, and will probably work for other common Linux distributions. Add a `docker-compose.override.yml` in the local directory, that looks like this: ```docker-compose.yml version: '3.3' # https://blog.giovannidemizio.eu/2021/05/24/how-to-set-user-and-group-in-docker-compose/ services: shell: # You have to set the user and group for this process, because you're going to be # creating all kinds of files from inside the container, that need to persist # outside the container. # Change `1000:1000` to the user and default group of your laptop user. user: 1000:1000 volumes: - /etc/passwd:/etc/passwd:ro - ~/.gem/credentials:/app/.gem/credentials:ro # $HOME here is your host computer's `~`, e.g. `/home/reid`. # `ssh` explicitly looks for its config in the home directory from `/etc/passwd`, # so the target for this has to look like your home directory on the host. - ~/.ssh:${HOME}/.ssh:ro - ${SSH_AUTH_SOCK}:/ssh-agent environment: - SSH_AUTH_SOCK=/ssh-agent ``` You may have to change the `1000:1000` to the user and group IDs of your laptop. You may also have to change the `version` parameter to match the version of the `docker-compose.yml` file. Adapting the above `docker-compose.override.yml` for MacOS should be relatively straight-forward. Windows users, I'm afraid you're on your own. If you figure this out, a PR documenting how to do it would be most welcome. The above doesn't allow you to run the system tests. To keep the image small, it doesn't include Chrome or any other browser. There is an experimental `docker-compose-system-test.yml` file, that runs the `bootstrap_forms` docker container along with an off-the-shelf Selenium container. To start this configuration: ```bash RUBY_VERSION=3.2 docker-compose -f docker-compose-system-test.yml -f docker-compose.override.yml up& RUBY_VERSION=3.2 docker-compose -f docker-compose-system-test.yml -f docker-compose.override.yml exec shell /bin/bash ``` (Sometimes, on shutdown, the Rails server PID file isn't removed, and so the above will fail. `rm demo/tmp/pids/server.pid` will fix it.) Once in the shell: ```bash cd demo bundle exec rails test:system ``` Note that this system test approach is highly experimental and has some rough edges. The docker compose file and/or steps to run system tests may change. The tests currently fail, because the files with which they're being compared were generated on a Mac, but the Docker containers are running Linux. #### Simple Dockerfile This repository includes a `Dockerfile` to build an image with the minimum `bootstrap_form`-supported Ruby environment. To build the image: ```bash docker build --tag bootstrap_form . ``` This builds an image called `bootstrap_form`. You can change that to any tag you wish. Just make sure you use the same tag name in the `docker run` command. If you want to use a different Ruby version, or a smaller Linux distribution (although the distro may be missing tools you need): ```bash docker build --build-arg "RUBY_VERSION=3.0" --build-arg "DISTRO=slim-buster" --tag bootstrap_form . ``` Then run the container you built with the shell, and create the bundle: ```bash docker run --volume "$PWD:/app" --user $UID:`grep ^$USERNAME /etc/passwd | cut -d: -f4` -it bootstrap_form /bin/bash bundle install ``` You can run tests in the container as normal, with `rake test`. (Some of that command line is need for Linux hosts, to run the container as the current user.) One of the disadvantages of this approach is that you can't release the gem from here, because the Docker container doesn't have access to your SSH credentials, or the right user name, or perhaps other things needed to release a gem. But for simple testing, it works. #### Troubleshooting Docker - With the above configuration, the gems are kept in `vendor/bundle` on your hosts, which is `$GEM_HOME` or `/app/vendor/bundle` in the running Docker container. If you're having permission problems when switching versions of Ruby or Rails, you can try `sudo rm -rf vendor/bundle` on the host, then run `BUNDLE_GEMFILES=gemfiles/7.0.gemfile bundle update` in the Docker container to re-install all the gems with the right permissions. ### The Demo Application There is a demo app in this repository. It shows some of the features of `bootstrap_form`, and provides a base on which to build ad-hoc testing, if you need it. Currently, the demo app is only set up to run for Rails 7, due to the variety of ways to include CSS and JavaScript in a modern Rails application. To run the demo app, set up the database and run the server: ```bash cd demo bundle rails db:setup dev ``` To run the demo app in the Docker container: ```bash docker run --volume "$PWD:/app" --user $UID:`grep ^$USERNAME /etc/passwd | cut -d: -f4` -p 3000:3000 -it bootstrap_form /bin/bash cd demo bundle rails db:setup dev ``` You'll see errors in the browser console about duplicate ids. This is expected, since the demo app has many forms with the same fields in them. Something we can fix in the future, perhaps. To use other supported versions of Rails, you will need to create a `Gemfile` for the Rails version. Then, change the `export BUNDLE_GEMFILE...` line to your gem file. Finally, figure out how to include the assets. If you need to run the Rails server separately, for example, to debug the server, you _must_ run it like this: ```sh bundle exec rails s -b 0.0.0.0 ``` If you run just `rails` or even `bin/rails`, the `sprockets-rails` gem won't load and you'll either get error messages, or the assets won't be available to the demo app. At the moment it's a mystery why. PRs to fix this are welcome. Please try to keep the checked-in `.ruby-version` set to the oldest supported version of Ruby. You're welcome and encouraged to try the demo app with other Ruby versions. Just don't check in the `.ruby-version` to GitHub. For the record, the demo app is set up as if the Rails app had been created with: ```sh rails new --skip-hotwire -d sqlite --edge -j esbuild -c bootstrap . ``` This means it's using `esbuild` to pre-process the JavaScript and (S)CSS, and that it's using [`jsbunding-rails`](https://github.com/rails/jsbundling-rails) and [`cssbundling-rails`](https://github.com/rails/cssbundling-rails) to put the assets in `app/assets/builds`, before the Sprockets assets pipeline serves them in development, or pre-compiles them in production. ## Documentation Contributions Contributions to documentation are always welcome. Even fixing one typo improves the quality of `bootstrap_form`. To make a documentation contribution, follow steps 1-3 of Code Contributions, then make the documentation changes, then make the pull request (step 6 of Code Contributions). If you put `[ci skip]` in the commit message of the most recent commit of the PR, you'll be a good citizen by not causing our CI pipeline to run all the tests when it's not necessary. ## Reviewing Pull Requests We are an entirely volunteer project. Sometimes it's hard for people to find the time to review pull requests. You can help! If you see a pull request that's waiting to be merged, it could be because no one has reviewed it yet. Your review could help move the pull request forward to be merged. --- Thanks to all the [great contributors](https://github.com/bootstrap-ruby/bootstrap_form/graphs/contributors) over the years. ## Troubleshooting ### Models and Database Tables `bootstrap_form` needs a few models and tables to support testing. It appears that the necessary tables were created via the `demo/db/schema.rb` file. To support `rich_text_area`, Rails 6 creates some migrations. These migrations had to be run in the existing database (not an empty one) to create a new `schema.rb` that creates the `bootstrap_form` test tables, and the tables needed by Rails 6. The `schema.rb` file was checked in to GitHub, but the migrations were not. In the future, any new Rails functionality that creates tables would likely have to be prepared the same way: ```bash cd demo rails db:setup # create the databases from `schema.rb` rails db:migrate # add the new tables and create a new `schema.rb` ``` ### RuboCop When you push a branch, RuboCop checks may fail, but locally you can't reproduce the failure. This may be because you're using a different version of RuboCop locally. When you push, the RuboCop tests use the currently available version of RuboCop. If you've been working on the branch for a while, it's likely you have a `Gemfile.lock` that specifies an older version of RuboCop. The first thing to try is to update your `Gemfile.lock` locally: ```bash bundle update ``` Or, if you really want to minimize your work: ```bash bundle update --conservative rubocop ``` This should enable you to reproduce the RuboCop failures locally, and then you can fix them. bootstrap_form-5.4.0/docker-compose-system-test.yml0000644000004100000410000000162414620142366022553 0ustar www-datawww-dataversion: '3.3' services: app: &app build: context: . args: NODE_MAJOR: "12" YARN_VERSION: "1.22.4" RUBY_VERSION: ${RUBY_VERSION} image: bootstrap-form:latest-$RUBY_VERSION tmpfs: - /tmp shell: <<: *app stdin_open: true tty: true volumes: - .:/app:cached environment: - SSH_AUTH_SOCK=/ssh-agent - NODE_ENV=development - BOOTSNAP_CACHE_DIR=/usr/local/bundle/_bootsnap - WEB_CONCURRENCY=1 - HISTFILE=/app/.bash_history - SELENIUM_HOST=selenium - SELENIUM_PORT=4444 - TEST_APP_HOST=shell - TEST_APP_PORT=3001 ports: - "3001:3001" command: /bin/bash selenium: image: selenium/standalone-chrome:118.0 logging: driver: none stdin_open: true tty: true environment: - LANG=en_CA.UTF-8 ports: - '4444:4444' - '5900:5900' bootstrap_form-5.4.0/.github/0000755000004100000410000000000014620142366016154 5ustar www-datawww-databootstrap_form-5.4.0/.github/workflows/0000755000004100000410000000000014620142366020211 5ustar www-datawww-databootstrap_form-5.4.0/.github/workflows/ruby.yml0000644000004100000410000000275414620142366021725 0ustar www-datawww-dataname: Ruby on: push: pull_request: jobs: Lint: runs-on: ubuntu-latest env: BUNDLE_GEMFILE: gemfiles/6.1.gemfile steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - uses: ruby/setup-ruby@v1 with: ruby-version: 3.0.6 bundler-cache: true # Disabled since it requires access not granted by GitHub Actions for PRs # - name: Danger # if: ${{ github.event_name == 'pull_request' }} # env: # DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} # run: | # bundle exec danger - name: Rubocop run: bundle exec rubocop --autocorrect Test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: ruby-version: [ '3.2', '3.1', '3.0', 'ruby-head' ] gemfile: [ '7.1', '7.0', '6.1', 'edge' ] env: BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - name: Run tests run: bundle exec rake test Demo: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: working-directory: demo bundler-cache: true - name: Run tests working-directory: demo run: bundle exec rake test:all bootstrap_form-5.4.0/lib/0000755000004100000410000000000014620142366015362 5ustar www-datawww-databootstrap_form-5.4.0/lib/bootstrap_form/0000755000004100000410000000000014620142366020422 5ustar www-datawww-databootstrap_form-5.4.0/lib/bootstrap_form/form_builder.rb0000644000004100000410000001125614620142366023425 0ustar www-datawww-data# require 'bootstrap_form/aliasing' module BootstrapForm class FormBuilder < ActionView::Helpers::FormBuilder attr_reader :layout, :label_col, :control_col, :has_error, :inline_errors, :label_errors, :acts_like_form_tag include BootstrapForm::Helpers::Field include BootstrapForm::Helpers::Bootstrap include BootstrapForm::FormGroupBuilder include BootstrapForm::FormGroup include BootstrapForm::Components include BootstrapForm::Inputs::Base include BootstrapForm::Inputs::CheckBox include BootstrapForm::Inputs::CollectionCheckBoxes include BootstrapForm::Inputs::CollectionRadioButtons include BootstrapForm::Inputs::CollectionSelect include BootstrapForm::Inputs::ColorField include BootstrapForm::Inputs::DateField include BootstrapForm::Inputs::DateSelect include BootstrapForm::Inputs::DatetimeField include BootstrapForm::Inputs::DatetimeLocalField include BootstrapForm::Inputs::DatetimeSelect include BootstrapForm::Inputs::EmailField include BootstrapForm::Inputs::FileField include BootstrapForm::Inputs::GroupedCollectionSelect include BootstrapForm::Inputs::MonthField include BootstrapForm::Inputs::NumberField include BootstrapForm::Inputs::PasswordField include BootstrapForm::Inputs::PhoneField include BootstrapForm::Inputs::RadioButton include BootstrapForm::Inputs::RangeField include BootstrapForm::Inputs::RichTextArea include BootstrapForm::Inputs::SearchField include BootstrapForm::Inputs::Select include BootstrapForm::Inputs::Submit include BootstrapForm::Inputs::TelephoneField include BootstrapForm::Inputs::TextArea include BootstrapForm::Inputs::TextField include BootstrapForm::Inputs::TimeField include BootstrapForm::Inputs::TimeSelect include BootstrapForm::Inputs::TimeZoneSelect include BootstrapForm::Inputs::UrlField include BootstrapForm::Inputs::WeekField include ActionView::Helpers::OutputSafetyHelper delegate :content_tag, :capture, :concat, :tag, to: :@template def initialize(object_name, object, template, options) warn_deprecated_layout_value(options) @layout = options[:layout] || default_layout @label_col = options[:label_col] || default_label_col @control_col = options[:control_col] || default_control_col @label_errors = options[:label_errors] || false @inline_errors = options[:inline_errors].nil? ? @label_errors != true : options[:inline_errors] != false @acts_like_form_tag = options[:acts_like_form_tag] add_default_form_attributes_and_form_inline options super end def add_default_form_attributes_and_form_inline(options) options[:html] ||= {} options[:html].reverse_merge!(BootstrapForm.config.default_form_attributes) return unless options[:layout] == :inline options[:html][:class] = safe_join(([*options[:html][:class]&.split(/\s+/)] + %w[row row-cols-auto g-3 align-items-center]) .compact.uniq, " ") end def fields_for_with_bootstrap(record_name, record_object=nil, fields_options={}, &block) fields_options = fields_for_options(record_object, fields_options) record_object = nil if record_object.is_a?(Hash) && record_object.extractable_options? fields_for_without_bootstrap(record_name, record_object, fields_options, &block) end bootstrap_alias :fields_for # the Rails `fields` method passes its options # to the builder, so there is no need to write a `bootstrap_form` helper # for the `fields` method. private def fields_for_options(record_object, fields_options) field_options = fields_options field_options = record_object if record_object.is_a?(Hash) && record_object.extractable_options? %i[layout control_col inline_errors label_errors].each do |option| field_options[option] ||= options[option] end field_options[:label_col] = field_options[:label_col].present? ? (field_options[:label_col]).to_s : options[:label_col] field_options end def default_layout # :vertical, :horizontal or :inline :vertical end def default_label_col "col-sm-2" end def offset_col(label_col) [*label_col].flat_map { |s| s.split(/\s+/) }.grep(/^col-/).join(" ").gsub(/\bcol-(\w+)-(\d)\b/, 'offset-\1-\2') end def default_control_col "col-sm-10" end def hide_class "visually-hidden" # still accessible for screen readers end def control_class "form-control" end def feedback_class "has-feedback" end def control_specific_class(method) "rails-bootstrap-forms-#{method.to_s.tr('_', '-')}" end end end bootstrap_form-5.4.0/lib/bootstrap_form/configuration.rb0000644000004100000410000000077614620142366023630 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm class Configuration def default_form_attributes=(attributes) case attributes when nil @default_form_attributes = {} when Hash @default_form_attributes = attributes else raise ArgumentError, "Unsupported default_form_attributes #{attributes.inspect}" end end def default_form_attributes return @default_form_attributes if defined? @default_form_attributes {} end end end bootstrap_form-5.4.0/lib/bootstrap_form/engine.rb0000644000004100000410000000034614620142366022217 0ustar www-datawww-data# frozen_string_literal: true require "rails/railtie" module BootstrapForm class Engine < Rails::Engine config.eager_load_namespaces << BootstrapForm config.autoload_paths << File.expand_path("lib", __dir__) end end bootstrap_form-5.4.0/lib/bootstrap_form/helpers.rb0000644000004100000410000000023714620142366022413 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Helpers extend ActiveSupport::Autoload autoload :Bootstrap autoload :Field end end bootstrap_form-5.4.0/lib/bootstrap_form/form_group.rb0000644000004100000410000000550614620142366023134 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module FormGroup extend ActiveSupport::Concern def form_group(*args, &block) options = args.extract_options! name = args.first options[:class] = form_group_classes(options) tag.div(**options.except(:add_control_col_class, :append, :control_col, :floating, :help, :icon, :id, :input_group_class, :label, :label_col, :layout, :prepend)) do label = generate_label(options[:id], name, options[:label], options[:label_col], options[:layout]) form_group_content(label, generate_help(name, options[:help]), options, &block) end end private def form_group_content_tag(name, field_name, without_field_name, options, html_options) html_class = control_specific_class(field_name) html_class = "#{html_class} col-auto g-3" if @layout == :horizontal && options[:skip_inline].blank? tag.div(class: html_class) do input_with_error(name) do send(without_field_name, name, options, html_options) end end end def form_group_content(label, help_text, options, &block) # rubocop:disable Metrics/AbcSize label ||= ActiveSupport::SafeBuffer.new if group_layout_horizontal?(options[:layout]) label + tag.div(capture(&block) + help_text, class: form_group_control_class(options)) else content = ActiveSupport::SafeBuffer.new # Floating labels need to be rendered after the field content << label unless options[:floating] content << capture(&block) content << label if options[:floating] content << help_text if help_text content end end def form_group_control_class(options) classes = [options[:control_col] || control_col] classes << options[:add_control_col_class] if options[:add_control_col_class] classes << offset_col(options[:label_col] || @label_col) unless options[:label] classes.flatten.compact end def form_group_classes(options) classes = options[:class] == false ? [] : (options[:class] || form_group_default_class).split classes << "row" if horizontal_group_with_gutters?(options[:layout], classes) classes << "col-auto g-3" if field_inline_override?(options[:layout]) classes << feedback_class if options[:icon] classes << "form-floating" if options[:floating] classes.presence end def form_group_default_class (layout == :inline ? "col" : "mb-3") end def horizontal_group_with_gutters?(layout, classes) group_layout_horizontal?(layout) && !classes_include_gutters?(classes) end def group_layout_horizontal?(layout) get_group_layout(layout) == :horizontal end def classes_include_gutters?(classes) classes.any? { |c| c =~ /^g-\d+$/ } end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs.rb0000644000004100000410000000174014620142366022273 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs extend ActiveSupport::Autoload autoload :Base autoload :InputsCollection autoload :CheckBox autoload :CollectionCheckBoxes autoload :CollectionRadioButtons autoload :CollectionSelect autoload :ColorField autoload :DateField autoload :DateSelect autoload :DatetimeField autoload :DatetimeLocalField autoload :DatetimeSelect autoload :EmailField autoload :FileField autoload :GroupedCollectionSelect autoload :MonthField autoload :NumberField autoload :PasswordField autoload :PhoneField autoload :RadioButton autoload :RangeField autoload :RichTextArea autoload :SearchField autoload :Select autoload :Submit autoload :TelephoneField autoload :TextArea autoload :TextField autoload :TimeField autoload :TimeSelect autoload :TimeZoneSelect autoload :UrlField autoload :WeekField end end bootstrap_form-5.4.0/lib/bootstrap_form/helpers/0000755000004100000410000000000014620142366022064 5ustar www-datawww-databootstrap_form-5.4.0/lib/bootstrap_form/helpers/field.rb0000644000004100000410000000114114620142366023471 0ustar www-datawww-datamodule BootstrapForm module Helpers module Field def required_field_options(options, method) required = required_field?(options, method) {}.tap do |option| option[:required] = true if required end end private def required_field?(options, method) if options[:skip_required] warn "`:skip_required` is deprecated, use `:required: false` instead" false elsif options.key?(:required) options[:required] else required_attribute?(object, method) end end end end end bootstrap_form-5.4.0/lib/bootstrap_form/helpers/bootstrap.rb0000644000004100000410000000547314620142366024437 0ustar www-datawww-datamodule BootstrapForm module Helpers module Bootstrap include ActionView::Helpers::OutputSafetyHelper def alert_message(title, options={}) css = options[:class] || "alert alert-danger" return unless object.respond_to?(:errors) && object.errors.full_messages.any? tag.div class: css do if options[:error_summary] == false title else tag.p(title) + error_summary end end end def error_summary return unless object.errors.any? tag.ul(class: "rails-bootstrap-forms-error-summary") do object.errors.full_messages.reduce(ActiveSupport::SafeBuffer.new) do |acc, error| acc << tag.li(error) end end end def errors_on(name, options={}) return unless error?(name) hide_attribute_name = options[:hide_attribute_name] || false custom_class = options[:custom_class] || false tag.div class: custom_class || "invalid-feedback" do errors = if hide_attribute_name object.errors[name] else object.errors.full_messages_for(name) end safe_join(errors, ", ") end end def static_control(*args) options = args.extract_options! name = args.first static_options = options.merge( readonly: true, control_class: [options[:control_class], static_class].compact ) static_options[:value] = object.send(name) unless static_options.key?(:value) text_field_with_bootstrap(name, static_options) end def custom_control(*args, &block) options = args.extract_options! name = args.first form_group_builder(name, options, &block) end def prepend_and_append_input(name, options, &block) options = options.extract!(:prepend, :append, :input_group_class).compact input = capture(&block) || ActiveSupport::SafeBuffer.new input = attach_input(options, :prepend) + input + attach_input(options, :append) input << generate_error(name) options.present? && input = tag.div(input, class: ["input-group", options[:input_group_class]].compact) input end def input_with_error(name, &block) input = capture(&block) input << generate_error(name) end def input_group_content(content) return content if content.include?("btn") tag.span(content, class: "input-group-text") end def static_class "form-control-plaintext" end private def attach_input(options, key) tags = [*options[key]].map do |item| input_group_content(item) end safe_join(tags) end end end end bootstrap_form-5.4.0/lib/bootstrap_form/action_view_extensions/0000755000004100000410000000000014620142366025210 5ustar www-datawww-databootstrap_form-5.4.0/lib/bootstrap_form/action_view_extensions/form_helper.rb0000644000004100000410000000351514620142366030043 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module ActionViewExtensions # This module creates BootstrapForm wrappers around the default form_with # and form_for methods # # Example: # # bootstrap_form_for @user do |f| # f.text_field :name # end # # Example: # # bootstrap_form_with model: @user do |f| # f.text_field :name # end module FormHelper def bootstrap_form_for(record, options={}, &block) options.reverse_merge!(builder: BootstrapForm::FormBuilder) with_bootstrap_form_field_error_proc do form_for(record, options, &block) end end def bootstrap_form_with(options={}, &block) options.reverse_merge!(builder: BootstrapForm::FormBuilder) with_bootstrap_form_field_error_proc do form_with(**options, &block) end end def bootstrap_form_tag(options={}, &block) options[:acts_like_form_tag] = true bootstrap_form_for("", options, &block) end def bootstrap_fields_for(record_name, record_object=nil, options={}, &block) options[:builder] = BootstrapForm::FormBuilder fields_for(record_name, record_object, options, &block) end def bootstrap_fields(scope=nil, model: nil, **options, &block) options[:builder] = BootstrapForm::FormBuilder fields(scope, model: model, **options, &block) end private def with_bootstrap_form_field_error_proc original_proc = ActionView::Base.field_error_proc ActionView::Base.field_error_proc = BootstrapForm.field_error_proc yield ensure ActionView::Base.field_error_proc = original_proc end end end end ActiveSupport.on_load(:action_view) do include BootstrapForm::ActionViewExtensions::FormHelper end bootstrap_form-5.4.0/lib/bootstrap_form/version.rb0000644000004100000410000000013714620142366022435 0ustar www-datawww-datamodule BootstrapForm VERSION = "5.4.0".freeze REQUIRED_RAILS_VERSION = ">= 6.1".freeze end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/0000755000004100000410000000000014620142366021744 5ustar www-datawww-databootstrap_form-5.4.0/lib/bootstrap_form/inputs/email_field.rb0000644000004100000410000000034514620142366024525 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module EmailField extend ActiveSupport::Concern include Base included do bootstrap_field :email_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/password_field.rb0000644000004100000410000000035314620142366025277 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module PasswordField extend ActiveSupport::Concern include Base included do bootstrap_field :password_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/range_field.rb0000644000004100000410000000040214620142366024524 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module RangeField extend ActiveSupport::Concern include Base included do bootstrap_field :range_field, control_class: "form-range" end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/file_field.rb0000644000004100000410000000103614620142366024353 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module FileField extend ActiveSupport::Concern include Base included do def file_field_with_bootstrap(name, options={}) options = options.reverse_merge(control_class: "form-control") form_group_builder(name, options) do input_with_error(name) do file_field_without_bootstrap(name, options) end end end bootstrap_alias :file_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/grouped_collection_select.rb0000644000004100000410000000237014620142366027512 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module GroupedCollectionSelect extend ActiveSupport::Concern include Base included do # Disabling Metrics/ParameterLists because the upstream Rails method has the same parameters # rubocop:disable Metrics/ParameterLists def grouped_collection_select_with_bootstrap(method, collection, group_method, group_label_method, option_key_method, option_value_method, options={}, html_options={}) html_options = html_options.reverse_merge(control_class: "form-select") form_group_builder(method, options, html_options) do prepend_and_append_input(method, options) do grouped_collection_select_without_bootstrap(method, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options) end end end # rubocop:enable Metrics/ParameterLists bootstrap_alias :grouped_collection_select end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/collection_select.rb0000644000004100000410000000163514620142366025770 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module CollectionSelect extend ActiveSupport::Concern include Base included do # Disabling Metrics/ParameterLists because the upstream Rails method has the same parameters # rubocop:disable Metrics/ParameterLists def collection_select_with_bootstrap(method, collection, value_method, text_method, options={}, html_options={}) html_options = html_options.reverse_merge(control_class: "form-select") form_group_builder(method, options, html_options) do prepend_and_append_input(method, options) do collection_select_without_bootstrap(method, collection, value_method, text_method, options, html_options) end end end # rubocop:enable Metrics/ParameterLists bootstrap_alias :collection_select end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/number_field.rb0000644000004100000410000000034714620142366024730 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module NumberField extend ActiveSupport::Concern include Base included do bootstrap_field :number_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/search_field.rb0000644000004100000410000000034714620142366024705 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module SearchField extend ActiveSupport::Concern include Base included do bootstrap_field :search_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/time_zone_select.rb0000644000004100000410000000122714620142366025623 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module TimeZoneSelect extend ActiveSupport::Concern include Base included do def time_zone_select_with_bootstrap(method, priority_zones=nil, options={}, html_options={}) html_options = html_options.reverse_merge(control_class: "form-select") form_group_builder(method, options, html_options) do input_with_error(method) do time_zone_select_without_bootstrap(method, priority_zones, options, html_options) end end end bootstrap_alias :time_zone_select end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/text_area.rb0000644000004100000410000000034114620142366024243 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module TextArea extend ActiveSupport::Concern include Base included do bootstrap_field :text_area end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/submit.rb0000644000004100000410000000212114620142366023570 0ustar www-datawww-datamodule BootstrapForm module Inputs module Submit def button(value=nil, options={}, &block) value = setup_css_class "btn btn-secondary", value, options super end def submit(value=nil, options={}) value = setup_css_class "btn btn-secondary", value, options layout == :inline ? form_group { super } : super end def primary(value=nil, options={}, &block) value = setup_css_class "btn btn-primary", value, options if options[:render_as_button] || block options.except! :render_as_button button(value, options, &block) else submit(value, options) end end private def setup_css_class(the_class, value, options) if value.is_a?(Hash) options.merge!(value) value = nil end unless options.key? :class if (extra_class = options.delete(:extra_class)) the_class = "#{the_class} #{extra_class}" end options[:class] = the_class end value end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/time_field.rb0000644000004100000410000000034314620142366024372 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module TimeField extend ActiveSupport::Concern include Base included do bootstrap_field :time_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/datetime_local_field.rb0000644000004100000410000000036614620142366026407 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module DatetimeLocalField extend ActiveSupport::Concern include Base included do bootstrap_field :datetime_local_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/inputs_collection.rb0000644000004100000410000000417114620142366026031 0ustar www-datawww-datamodule BootstrapForm module Inputs module InputsCollection extend ActiveSupport::Concern private def inputs_collection(name, collection, value, text, options={}) options[:label] ||= { class: group_label_class(options[:layout]) } options[:inline] ||= layout_inline?(options[:layout]) form_group_builder(name, options) do inputs = ActiveSupport::SafeBuffer.new collection.each_with_index do |obj, i| input_value = value.respond_to?(:call) ? value.call(obj) : obj.send(value) input_options = form_group_collection_input_options(options, text, obj, i, input_value, collection) inputs << yield(name, input_value, input_options) end inputs end end def group_label_class(field_layout) if layout_horizontal?(field_layout) group_label_class = "col-form-label #{label_col} pt-0" elsif layout_inline?(field_layout) group_label_class = "form-check form-check-inline ps-0" end group_label_class end # FIXME: Find a way to reduce the parameter list size # rubocop:disable Metrics/ParameterLists def form_group_collection_input_options(options, text, obj, index, input_value, collection) input_options = options.merge(label: text.respond_to?(:call) ? text.call(obj) : obj.send(text)) if (checked = input_options[:checked]) input_options[:checked] = form_group_collection_input_checked?(checked, obj, input_value) end # add things like 'data-' attributes to the HTML obj.each { |inner_obj| input_options.merge!(inner_obj) if inner_obj.is_a?(Hash) } if obj.respond_to?(:each) input_options[:error_message] = index == collection.size - 1 input_options.except!(:class) input_options end # rubocop:enable Metrics/ParameterLists def form_group_collection_input_checked?(checked, obj, input_value) checked == input_value || Array(checked).try(:include?, input_value) || checked == obj || Array(checked).try(:include?, obj) end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/collection_check_boxes.rb0000644000004100000410000000203114620142366026755 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module CollectionCheckBoxes extend ActiveSupport::Concern include Base include InputsCollection included do def collection_check_boxes_with_bootstrap(*args) html = inputs_collection(*args) do |name, value, options| options[:multiple] = true check_box(name, options, value, nil) end if args.extract_options!.symbolize_keys!.delete(:include_hidden) { true } html.prepend hidden_field(args.first, value: "", name: field_name(args[0], multiple: true)) end html end bootstrap_alias :collection_check_boxes if Rails::VERSION::MAJOR < 7 def field_name(method, *methods, multiple: false, index: @options[:index]) object_name = @options.fetch(:as) { @object_name } @template.field_name(object_name, method, *methods, index: index, multiple: multiple) end end end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/check_box.rb0000644000004100000410000000776014620142366024230 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module CheckBox extend ActiveSupport::Concern include Base included do def check_box_with_bootstrap(name, options={}, checked_value="1", unchecked_value="0", &block) options = options.symbolize_keys! content = tag.div(class: check_box_wrapper_class(options), **options[:wrapper].to_h.except(:class)) do html = check_box_without_bootstrap(name, check_box_options(name, options), checked_value, unchecked_value) html << check_box_label(name, options, checked_value, &block) unless options[:skip_label] html << generate_error(name) if options[:error_message] html end wrapper(content, options) end bootstrap_alias :check_box end private def wrapper(content, options) if layout == :inline && !options[:multiple] tag.div(class: "col") { content } elsif layout == :horizontal && !options[:multiple] form_group(layout: layout_in_effect(options[:layout]), label_col: options[:label_col]) { content } else content end end def check_box_options(name, options) check_box_options = options.except(:class, :control_col, :error_message, :help, :hide_label, :inline, :label, :label_class, :label_col, :layout, :skip_label, :switch, :wrapper, :wrapper_class) check_box_options[:class] = check_box_classes(name, options) check_box_options.merge!(required_field_options(options, name)) end def check_box_label(name, options, checked_value, &block) label_name = if options[:multiple] check_box_value(name, checked_value) else name end label_options = { class: check_box_label_class(options) } label_options[:for] = options[:id] if options[:id].present? label(label_name, check_box_description(name, options, &block), label_options) end def check_box_description(name, options, &block) content = block ? capture(&block) : options[:label] content || object&.class&.human_attribute_name(name) || name.to_s.humanize end def check_box_value(name, value) # label's `for` attribute needs to match checkbox tag's id, # IE sanitized value, IE # https://github.com/rails/rails/blob/5-0-stable/actionview/lib/action_view/helpers/tags/base.rb#L123-L125 "#{name}_#{value.to_s.gsub(/\s/, '_').gsub(/[^-[[:word:]]]/, '').mb_chars.downcase}" end def check_box_classes(name, options) classes = Array(options[:class]) << "form-check-input" classes << "is-invalid" if error?(name) classes << "position-static" if options[:skip_label] || options[:hide_label] classes.flatten.compact end def check_box_label_class(options) classes = ["form-check-label"] classes << options[:label_class] classes << "required" if options[:required] classes << hide_class if options[:hide_label] classes.flatten.compact end def check_box_wrapper_class(options) classes = ["form-check"] classes << "form-check-inline" if layout_inline?(options[:inline]) classes << "mb-3" unless options[:multiple] || %i[horizontal inline].include?(layout) classes << "form-switch" if options[:switch] classes << options.dig(:wrapper, :class).presence classes << options[:wrapper_class].presence classes.flatten.compact end def checkbox_required(options, method) if options[:skip_required] warn "`:skip_required` is deprecated, use `:required: false` instead" false elsif options.key?(:required) options[:required] else required_attribute?(object, method) end end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/date_field.rb0000644000004100000410000000034314620142366024351 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module DateField extend ActiveSupport::Concern include Base included do bootstrap_field :date_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/radio_button.rb0000644000004100000410000000451514620142366024767 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module RadioButton extend ActiveSupport::Concern include Base included do def radio_button_with_bootstrap(name, value, *args) options = args.extract_options!.symbolize_keys! wrapper_attributes = options[:wrapper] || {} wrapper_attributes[:class] = radio_button_wrapper_class(options) tag.div(**wrapper_attributes) do html = radio_button_without_bootstrap(name, value, radio_button_options(name, options)) html << radio_button_label(name, value, options) unless options[:skip_label] html << generate_error(name) if options[:error_message] html end end bootstrap_alias :radio_button end private def radio_button_options(name, options) radio_button_options = options.except(:class, :label, :label_class, :error_message, :help, :inline, :hide_label, :skip_label, :wrapper, :wrapper_class) radio_button_options[:class] = radio_button_classes(name, options) radio_button_options.merge!(required_field_options(options, name)) end def radio_button_label(name, value, options) label_options = { value: value, class: radio_button_label_class(options) } label_options[:for] = options[:id] if options[:id].present? label(name, options[:label], label_options) end def radio_button_classes(name, options) classes = Array(options[:class]) << "form-check-input" classes << "is-invalid" if error?(name) classes << "position-static" if options[:skip_label] || options[:hide_label] classes.flatten.compact end def radio_button_label_class(options) classes = ["form-check-label"] classes << options[:label_class] classes << hide_class if options[:hide_label] classes.flatten.compact end def radio_button_wrapper_class(options) classes = ["form-check"] classes << "form-check-inline" if layout_inline?(options[:inline]) classes << "disabled" if options[:disabled] classes << options.dig(:wrapper, :class).presence classes << options[:wrapper_class].presence classes.flatten.compact end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/base.rb0000644000004100000410000000411114620142366023200 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module Base extend ActiveSupport::Concern class_methods do def bootstrap_field(field_name, control_class: nil) define_method "#{field_name}_with_bootstrap" do |name, options={ control_class: control_class }.compact| warn_deprecated_layout_value(options) form_group_builder(name, options) do prepend_and_append_input(name, options) do options[:placeholder] ||= name if options[:floating] send("#{field_name}_without_bootstrap".to_sym, name, options.except(:floating)) end end end bootstrap_alias field_name end def bootstrap_select_group(field_name) define_method("#{field_name}_with_bootstrap") do |name, options={}, html_options={}| html_options = html_options.reverse_merge(control_class: "form-select") form_group_builder(name, options, html_options) do form_group_content_tag(name, field_name, "#{field_name}_without_bootstrap", options, html_options) end end bootstrap_alias field_name end # Creates the methods *_without_bootstrap and *_with_bootstrap. # # If your application did not include the rails gem for one of the dsl # methods, then a name error is raised and suppressed. This can happen # if your application does not include the actiontext dependency due to # `rich_text_area` not being defined. def bootstrap_alias(field_name) alias_method "#{field_name}_without_bootstrap".to_sym, field_name alias_method field_name, "#{field_name}_with_bootstrap".to_sym rescue NameError # rubocop:disable Lint/SuppressedException end end private def warn_deprecated_layout_value(options) return unless options[:layout] == :default warn "Layout `:default` is deprecated, use `:vertical` instead." options[:layout] = :vertical end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/select.rb0000644000004100000410000000120414620142366023545 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module Select extend ActiveSupport::Concern include Base included do def select_with_bootstrap(method, choices=nil, options={}, html_options={}, &block) html_options = html_options.reverse_merge(control_class: "form-select") form_group_builder(method, options, html_options) do prepend_and_append_input(method, options) do select_without_bootstrap(method, choices, options, html_options, &block) end end end bootstrap_alias :select end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/color_field.rb0000644000004100000410000000034514620142366024554 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module ColorField extend ActiveSupport::Concern include Base included do bootstrap_field :color_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/collection_radio_buttons.rb0000644000004100000410000000073714620142366027367 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module CollectionRadioButtons extend ActiveSupport::Concern include Base include InputsCollection included do def collection_radio_buttons_with_bootstrap(*args) inputs_collection(*args) do |name, value, options| radio_button(name, value, options) end end bootstrap_alias :collection_radio_buttons end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/week_field.rb0000644000004100000410000000034314620142366024367 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module WeekField extend ActiveSupport::Concern include Base included do bootstrap_field :week_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/phone_field.rb0000644000004100000410000000034514620142366024547 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module PhoneField extend ActiveSupport::Concern include Base included do bootstrap_field :phone_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/rich_text_area.rb0000644000004100000410000000111714620142366025252 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module RichTextArea extend ActiveSupport::Concern include Base included do def rich_text_area_with_bootstrap(name, options={}) form_group_builder(name, options) do prepend_and_append_input(name, options) do options[:class] = safe_join(["trix-content", options[:class]].compact, " ") rich_text_area_without_bootstrap(name, options) end end end bootstrap_alias :rich_text_area end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/datetime_field.rb0000644000004100000410000000035314620142366025231 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module DatetimeField extend ActiveSupport::Concern include Base included do bootstrap_field :datetime_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/datetime_select.rb0000644000004100000410000000036414620142366025427 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module DatetimeSelect extend ActiveSupport::Concern include Base included do bootstrap_select_group :datetime_select end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/month_field.rb0000644000004100000410000000034514620142366024563 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module MonthField extend ActiveSupport::Concern include Base included do bootstrap_field :month_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/text_field.rb0000644000004100000410000000034314620142366024420 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module TextField extend ActiveSupport::Concern include Base included do bootstrap_field :text_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/date_select.rb0000644000004100000410000000035414620142366024547 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module DateSelect extend ActiveSupport::Concern include Base included do bootstrap_select_group :date_select end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/telephone_field.rb0000644000004100000410000000035514620142366025422 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module TelephoneField extend ActiveSupport::Concern include Base included do bootstrap_field :telephone_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/url_field.rb0000644000004100000410000000034114620142366024234 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module UrlField extend ActiveSupport::Concern include Base included do bootstrap_field :url_field end end end end bootstrap_form-5.4.0/lib/bootstrap_form/inputs/time_select.rb0000644000004100000410000000035414620142366024570 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Inputs module TimeSelect extend ActiveSupport::Concern include Base included do bootstrap_select_group :time_select end end end end bootstrap_form-5.4.0/lib/bootstrap_form/form_group_builder.rb0000644000004100000410000000677214620142366024650 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module FormGroupBuilder extend ActiveSupport::Concern private def form_group_builder(method, options, html_options=nil, &block) no_wrapper = options[:wrapper] == false options = form_group_builder_options(options, method) form_group_options = form_group_opts(options, form_group_css_options(method, html_options.try(:symbolize_keys!), options)) options.except!( :help, :icon, :label_col, :control_col, :add_control_col_class, :layout, :skip_label, :label, :label_class, :hide_label, :skip_required, :label_as_placeholder, :wrapper_class, :wrapper ) if no_wrapper yield else form_group(method, form_group_options, &block) end end def form_group_builder_options(options, method) options.symbolize_keys! options = convert_form_tag_options(method, options) if acts_like_form_tag options.merge!(required_field_options(options, method)) end def convert_form_tag_options(method, options={}) unless @options[:skip_default_ids] options[:name] ||= method options[:id] ||= method end options end def form_group_opts(options, css_options) wrapper_options = options[:wrapper] form_group_options = { id: options[:id], help: options[:help], icon: options[:icon], label_col: options[:label_col], control_col: options[:control_col], add_control_col_class: options[:add_control_col_class], layout: get_group_layout(options[:layout]), class: options[:wrapper_class], floating: options[:floating] } form_group_options.merge!(wrapper_options) if wrapper_options.is_a?(Hash) form_group_options[:label] = form_group_label(options, css_options) unless options[:skip_label] form_group_options end def form_group_label(options, css_options) { text: form_group_label_text(options[:label]), class: form_group_label_class(options), required: options[:required] }.merge(css_options[:id].present? ? { for: css_options[:id] } : {}) end def form_group_label_text(label) text = label[:text] if label.is_a?(Hash) text ||= label if label.is_a?(String) text end def form_group_label_class(options) return hide_class if options[:hide_label] || options[:label_as_placeholder] classes = options[:label][:class] if options[:label].is_a?(Hash) classes ||= options[:label_class] classes end def form_group_required(options, method) if options[:skip_required] warn "`:skip_required` is deprecated, use `:required: false` instead" false elsif options.key?(:required) options[:required] else required_attribute?(object, method) end end def form_group_css_options(method, html_options, options) css_options = html_options || options # Add control_class; allow it to be overridden by :control_class option control_classes = css_options.delete(:control_class) { control_class } css_options[:class] = safe_join([control_classes, css_options[:class]].compact, " ") css_options[:class] << " is-invalid" if error?(method) css_options[:placeholder] = form_group_placeholder(options, method) if options[:label_as_placeholder] css_options end def form_group_placeholder(options, method) form_group_label_text(options[:label]) || object.class.human_attribute_name(method) end end end bootstrap_form-5.4.0/lib/bootstrap_form/components/0000755000004100000410000000000014620142366022607 5ustar www-datawww-databootstrap_form-5.4.0/lib/bootstrap_form/components/validation.rb0000644000004100000410000000556414620142366025300 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Components module Validation extend ActiveSupport::Concern private def error?(name) name && object.respond_to?(:errors) && (object.errors[name].any? || association_error?(name)) end def association_error?(name) object.class.try(:reflections)&.any? do |association_name, a| next unless a.is_a?(ActiveRecord::Reflection::BelongsToReflection) next unless a.foreign_key == name.to_s object.errors[association_name].any? end end def required_attribute?(obj, attribute) return false unless obj && attribute target = obj.instance_of?(Class) ? obj : obj.class return false unless target.respond_to? :validators_on presence_validators?(target, obj, attribute) || required_association?(target, obj, attribute) end def required_association?(target, object, attribute) target.try(:reflections)&.any? do |name, a| next unless a.is_a?(ActiveRecord::Reflection::BelongsToReflection) next unless a.foreign_key == attribute.to_s presence_validators?(target, object, name) end end def presence_validators?(target, object, attribute) target.validators_on(attribute).select { |v| presence_validator?(v.class) }.any? do |validator| if_option = validator.options[:if] unless_opt = validator.options[:unless] (!if_option || call_with_self(object, if_option)) && (!unless_opt || !call_with_self(object, unless_opt)) end end def call_with_self(object, proc) proc = object.method(proc) if proc.is_a? Symbol object.instance_exec(*[(object if proc.arity >= 1)].compact, &proc) end def presence_validator?(validator_class) validator_class == ActiveModel::Validations::PresenceValidator || (defined?(ActiveRecord::Validations::PresenceValidator) && validator_class == ActiveRecord::Validations::PresenceValidator) end def inline_error?(name) error?(name) && inline_errors end def generate_error(name) return unless inline_error?(name) help_text = get_error_messages(name) help_klass = "invalid-feedback" help_tag = :div content_tag(help_tag, help_text, class: help_klass) end # rubocop:disable Metrics/AbcSize def get_error_messages(name) object.class.try(:reflections)&.each do |association_name, a| next unless a.is_a?(ActiveRecord::Reflection::BelongsToReflection) next unless a.foreign_key == name.to_s object.errors[association_name].each do |error| object.errors.add(name, error) end end safe_join(object.errors[name], ", ") end # rubocop:enable Metrics/AbcSize end end end bootstrap_form-5.4.0/lib/bootstrap_form/components/hints.rb0000644000004100000410000000411114620142366024256 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Components module Hints extend ActiveSupport::Concern private def generate_help(name, help_text) return if help_text == false help_klass ||= "form-text text-muted" help_text ||= get_help_text_by_i18n_key(name) help_tag ||= :small content_tag(help_tag, help_text, class: help_klass) if help_text.present? end def get_help_text_by_i18n_key(name) return unless object partial_scope = if object_class.respond_to?(:model_name) object_class.model_name.name else object_class.name end # First check for a subkey :html, as it is also accepted by i18n, and the # simple check for name would return an hash instead of a string (both # with .presence returning true!) help_text = nil ["#{name}.html", name, "#{name}_html"].each do |scope| break if help_text help_text = scoped_help_text(scope, partial_scope) end help_text end def object_class if !object.class.is_a?(ActiveModel::Naming) && object.respond_to?(:klass) && object.klass.is_a?(ActiveModel::Naming) object.klass else object.class end end def scoped_help_text(name, partial_scope) underscored_scope = "activerecord.help.#{partial_scope.underscore}" downcased_scope = "activerecord.help.#{partial_scope.downcase}" help_text = translated_help_text(name, underscored_scope).presence help_text ||= if (text = translated_help_text(name, downcased_scope).presence) warn "I18n key '#{downcased_scope}.#{name}' is deprecated, use '#{underscored_scope}.#{name}' instead" text end help_text end def translated_help_text(name, scope) ActiveSupport::SafeBuffer.new I18n.t(name, scope: scope, default: "") end end end end bootstrap_form-5.4.0/lib/bootstrap_form/components/layout.rb0000644000004100000410000000157414620142366024460 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Components module Layout extend ActiveSupport::Concern private def layout_horizontal?(field_layout=nil) layout_in_effect(field_layout) == :horizontal end def layout_inline?(field_layout=nil) layout_in_effect(field_layout) == :inline end def field_inline_override?(field_layout=nil) field_layout == :inline && layout != :inline end # true and false should only come from check_box and radio_button, # and those don't have a :horizontal layout def layout_in_effect(field_layout) field_layout = :inline if field_layout == true field_layout = :vertical if field_layout == false field_layout || layout end def get_group_layout(group_layout) group_layout || layout end end end end bootstrap_form-5.4.0/lib/bootstrap_form/components/labels.rb0000644000004100000410000000357314620142366024406 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Components module Labels extend ActiveSupport::Concern private def generate_label(id, name, options, custom_label_col, group_layout) return if options.blank? # id is the caller's options[:id] at the only place this method is called. # The options argument is a small subset of the options that might have # been passed to generate_label's caller, and definitely doesn't include # :id. options[:for] = id if acts_like_form_tag options[:class] = label_classes(name, options, custom_label_col, group_layout) options.delete(:class) if options[:class].none? label(name, label_text(name, options), options.except(:text)) end def label_classes(name, options, custom_label_col, group_layout) classes = [options[:class] || label_layout_classes(custom_label_col, group_layout)] add_class = options.delete(:add_class) classes.prepend(add_class) if add_class classes << "required" if required_field_options(options, name)[:required] options.delete(:required) classes << "text-danger" if label_errors && error?(name) classes.flatten.compact end def label_layout_classes(custom_label_col, group_layout) if layout_horizontal?(group_layout) ["col-form-label", (custom_label_col || label_col)] elsif layout_inline?(group_layout) %w[form-label me-sm-2] else "form-label" end end def label_text(name, options) label = options[:text] || object&.class&.try(:human_attribute_name, name)&.html_safe # rubocop:disable Rails/OutputSafety if label_errors && error?(name) (" ".html_safe + get_error_messages(name)).prepend(label) else label end end end end end bootstrap_form-5.4.0/lib/bootstrap_form/components.rb0000644000004100000410000000043514620142366023136 0ustar www-datawww-data# frozen_string_literal: true module BootstrapForm module Components extend ActiveSupport::Autoload autoload :Hints autoload :Labels autoload :Layout autoload :Validation include Hints include Labels include Layout include Validation end end bootstrap_form-5.4.0/lib/bootstrap_form.rb0000644000004100000410000000252014620142366020746 0ustar www-datawww-data# NOTE: The rich_text_area and rich_text_area_tag helpers are defined in a file # with a different name and not in the usual autoload-reachable way. # The following line is definitely need to make `bootstrap_form` work. # rubocop:disable Lint/SuppressedException begin require "#{Gem::Specification.find_by_name('actiontext').gem_dir}/app/helpers/action_text/tag_helper" rescue Gem::MissingSpecError end # rubocop:enable Lint/SuppressedException require "action_view" require "action_pack" require "bootstrap_form/action_view_extensions/form_helper" module BootstrapForm extend ActiveSupport::Autoload eager_autoload do autoload :Configuration autoload :FormBuilder autoload :FormGroupBuilder autoload :FormGroup autoload :Components autoload :Inputs autoload :Helpers end class << self def eager_load! super BootstrapForm::Components.eager_load! BootstrapForm::Helpers.eager_load! BootstrapForm::Inputs.eager_load! end def config @config ||= BootstrapForm::Configuration.new end def configure yield config end end mattr_accessor :field_error_proc # rubocop:disable Style/ClassVars @@field_error_proc = proc do |html_tag, _instance_tag| html_tag end # rubocop:enable Style/ClassVars end require "bootstrap_form/engine" if defined?(Rails) bootstrap_form-5.4.0/bootstrap_form.gemspec0000644000004100000410000000214314620142366021221 0ustar www-datawww-datalib = File.expand_path("lib", __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "bootstrap_form/version" Gem::Specification.new do |s| s.name = "bootstrap_form" s.version = BootstrapForm::VERSION s.authors = ["Stephen Potenza", "Carlos Lopes"] s.email = ["potenza@gmail.com", "carlos.el.lopes@gmail.com"] s.homepage = "https://github.com/bootstrap-ruby/bootstrap_form" s.summary = "Rails form builder that makes it easy to style forms using " \ "Bootstrap 5" s.description = "bootstrap_form is a rails form builder that makes it super " \ "easy to create beautiful-looking forms using Bootstrap 5" s.license = "MIT" s.metadata = { "rubygems_mfa_required" => "true" } s.files = `git ls-files -z`.split("\x0").reject do |f| f.match(%r{^(test)/|^(demo)/}) end s.bindir = "exe" s.require_paths = ["lib"] s.required_ruby_version = ">= 3.0" s.add_dependency("actionpack", BootstrapForm::REQUIRED_RAILS_VERSION) s.add_dependency("activemodel", BootstrapForm::REQUIRED_RAILS_VERSION) end bootstrap_form-5.4.0/RELEASING.md0000644000004100000410000000353214620142366016452 0ustar www-datawww-data# Releasing Follow these steps to release a new version of bootstrap_form to rubygems.org. ## Prerequisites * You must have commit rights to the bootstrap_form repository. * You must have push rights for the bootstrap_form gem on rubygems.org. * You must be using a Ruby version that is not end-of-life. ## How to release 1. Determine which would be the correct next version number according to [semver](http://semver.org/). 2. Update the version in `./lib/bootstrap_form/version.rb`. 3. Make sure that you have all the gems necessary for testing and releasing. BUNDLE_GEMFILE=gemfiles/7.1.gemfile bundle update 4. **Ensure the tests are passing by running the tests** (There should be no errors or warnings.) BUNDLE_GEMFILE=gemfiles/7.1.gemfile bundle exec rake test 5. **Ensure the demo tests are passing by running** cd demo bundle update bundle exec rake test:all cd - (You will have failures unless you're running on a Mac configured so the screenshots will be identical. This is something that we're hoping to fix.) 6. Update the GitHub diff links at the beginning of `CHANGELOG.md` (The pattern should be obvious when you look at them). 7. Update the installation instructions in `README.md` to use the new version. 8. 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. 9. Tag, push to GitHub, and publish to rubygems.org: bundle exec rake release 10. Go to https://github.com/bootstrap-ruby/bootstrap_form/releases and create the new release and add release notes by clicking the "Generate release notes" button. Add the link of closed issues from CHANGELOG. Group the commits in sections: * ### New features * ### Bugfixes * ### Performance * ### Documentation * ### Development bootstrap_form-5.4.0/LICENSE.txt0000644000004100000410000000207014620142366016436 0ustar www-datawww-dataCopyright 2012-2018 Stephen Potenza Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bootstrap_form-5.4.0/.yarnrc0000644000004100000410000000016414620142366016114 0ustar www-datawww-data# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 lastUpdateCheck 1698606246341 bootstrap_form-5.4.0/UPGRADE-5.0.md0000644000004100000410000000325014620142366016525 0ustar www-datawww-data# Upgrading to `bootstrap_form` 5.0 We made every effort to make the upgrade from `bootstrap_form` v4 (Bootstrap 4) to `bootstrap_form` 5.0 (Bootstrap 5) as easy as possible. However, Bootstrap 5 is fundamentally different from Bootstrap 4, so some changes may be necessary in your code. ## Bootstrap 5 Changes Upgrading `bootstrap_form` to version 5 means you must upgrade your whole application to Bootstrap 5. Read the [Bootstrap 5 migration guide](https://v5.getbootstrap.com/docs/5.0/migration/) to see what changes you have to make to your views. This will also help you understand changes you might have to make to your `bootstrap_form` code. ## `bootstrap_form` Version 5 Changes ## No `role="form"` Attribute As explained in #560, the `role="form"` attribute generated by `bootstrap_4` caused the W3C validator to output a warning. The `role="form"` attribute was deprecated in the 4.5.0 and is being remove completely in 5.0.0. This has no impact on `bootstrap_form` code itself, but may affect your application if it depended on a form having this attribute set. (Issue #569) ## Different behavior for `errors_on` helper The `errors_on` helper now wraps the error message in a CSS class `invalid-feedback`, instead of `alert` and `alert-danger`, as before. This will display the error as any other [Bootstrap inline form error](https://getbootstrap.com/docs/5.0/forms/validation/#server-side), instead of displaying it as an [Bootstrap alert](https://getbootstrap.com/docs/5.0/components/alerts/). You can use the `custom_class` options for this helper with `alert alert-danger` to restore the old behaviour: ```erb <%= f.errors_on :tasks, custom_class: 'alert alert-danger' %> ``` bootstrap_form-5.4.0/UPGRADE-4.0.md0000644000004100000410000001045014620142366016524 0ustar www-datawww-data# Upgrading to `bootstrap_form` 4.0 We made every effort to make the upgrade from `bootstrap_form` v2.7 (Bootstrap 3) to `bootstrap_form` v4.0 (Bootstrap 4) as easy as possible. However, Bootstrap 4 is fundamentally different from Bootstrap 3, so some changes may be necessary in your code. ## Bootstrap 4 Changes If you made use of Bootstrap classes or Javascript, you should read the [Bootstrap 4 migration guide](https://getbootstrap.com/docs/4.0/migration/). ## Validation Error Messages With Bootstrap 4, in order for validation error messages to display, the message has to be a sibling of the `input` tag, and the `input` tag has to have the `.is-invalid` class. This was different from Bootstrap 3, and forced some changes to `bootstrap_form` that may affect programs that used `bootstrap_form` v2.7. ### Arbitrary Text in `form_group` Blocks In `bootstrap_form` v2.7, it was possible to write something like this: ``` <%= bootstrap_form_for(@user) do |f| %> <%= f.form_group(:email) do %>

Bar

<%= end %> <%= end %> ``` and, if `@user.email` had validation errors, it would render: ```

Bar

can't be blank, is too short (minimum is 5 characters)
``` which would show an error message in red. That doesn't work in Bootstrap 4. Outputting error messages had to be moved to accommodate other changes, so `form_group` no longer outputs error messages unless whatever is inside the block is a `bootstrap_form` helper. One way to make the above behave the same in `bootstrap_form` v4.0 is to write it like this: ``` <%= bootstrap_form_for(@user) do |f| %> <%= f.form_group(:email) do %>

Bar

<%= tag.div(@user.errors[:email].join(", "), class: "invalid-feedback", style: "display: block;") unless @user.errors[:email].empty? %> <%= end %> <%= end %> ``` ### Check Boxes and Radio Buttons Bootstrap 4 marks up check boxes and radio buttons differently. In particular, Bootstrap 4 wraps the `input` and `label` tags in a `div.form-check` tag. Because validation error messages have to be siblings of the `input` tag, there is now an `error_message` option to `check_box` and `radio_button` to cause them to put the validation error messages inside the `div.form-check`. This change is mostly invisible to existing programs: - Since the default for `error_message` is false, use of `check_box` and `radio_button` all by themselves behaves the same as in `bootstrap_form` v2.7 - All the `collection*` helpers that output radio buttons and check boxes arrange to produce the validation error message on the last check box or radio button of the group, like `bootstrap_form` v2.7 did There is one situation where an existing program will have to change. When rendering one or more check boxes or radio buttons inside a `form_group` block, the last call to `check_box` or `radio_button` in the block will have to have `error_message: true` added to its parameters, like this: ``` <%= bootstrap_form_for(@user) do |f| %> <%= f.form_group(:education) do %> <%= f.radio_button(:misc, "primary school") %> <%= f.radio_button(:misc, "high school") %> <%= f.radio_button(:misc, "university", error_message: true) %> <%= end %> <%= end %> ``` ## `form-group` and Horizontal Forms In Bootstrap 3, `.form-group` mixed in `.row`. In Bootstrap 4, it doesn't. So `bootstrap_form` automatically adds `.row` to the `div.form-group`s that it creates, if the form group is in a horizontal layout. When migrating forms from the Bootstrap 3 version of `bootstrap_form` to the Bootstrap 4 version, check all horizontal forms to be sure they're being rendered properly. Bootstrap 4 also provides a `.form-row`, which has smaller gutters than `.row`. If you specify ".form-row", `bootstrap_form` will replace `.row` with `.form-row` on the `div.form-group`. When calling `form_group` directly, do something like this: ``` bootstrap_form_for(@user, layout: "horizontal") do |f| f.form_group class: "form-row" do ... end end ``` For the other helpers, do something like this: ``` bootstrap_form_for(@user, layout: "horizontal") do |f| f.form_group class: "form-row" do f.text_field wrapper_class: "form-row" # or f.text_field wrapper: { class: "form-row" } ... end end bootstrap_form-5.4.0/gemfiles/0000755000004100000410000000000014620142366016407 5ustar www-datawww-databootstrap_form-5.4.0/gemfiles/edge.gemfile0000644000004100000410000000034414620142366020646 0ustar www-datawww-datagems = "#{__dir__}/common.gemfile" eval File.read(gems), binding, gems # rubocop: disable Security/Eval gem "rails", git: "https://github.com/rails/rails.git", branch: "main" gem "sprockets-rails", require: "sprockets/railtie" bootstrap_form-5.4.0/gemfiles/6.1.gemfile0000644000004100000410000000020114620142366020236 0ustar www-datawww-datagems = "#{__dir__}/common.gemfile" eval File.read(gems), binding, gems # rubocop: disable Security/Eval gem "rails", "~> 6.1.0" bootstrap_form-5.4.0/gemfiles/7.1.gemfile0000644000004100000410000000026514620142366020251 0ustar www-datawww-datagems = "#{__dir__}/common.gemfile" eval File.read(gems), binding, gems # rubocop: disable Security/Eval gem "rails", "~> 7.1.0" gem "sprockets-rails", require: "sprockets/railtie" bootstrap_form-5.4.0/gemfiles/common.gemfile0000644000004100000410000000072614620142366021236 0ustar www-datawww-datasource "http://rubygems.org" gemspec path: File.dirname(__dir__) # To test with different Rails versions, use the files in `./gemfiles` group :development do gem "htmlbeautifier" gem "puma" gem "rubocop-performance", require: false gem "rubocop-rails", require: false end group :test do gem "diffy" gem "equivalent-xml" gem "mocha" gem "sqlite3" end group :development, :test do gem "debug" gem "pry-byebug" end group :ci do gem "danger" end bootstrap_form-5.4.0/gemfiles/7.0.gemfile0000644000004100000410000000026514620142366020250 0ustar www-datawww-datagems = "#{__dir__}/common.gemfile" eval File.read(gems), binding, gems # rubocop: disable Security/Eval gem "rails", "~> 7.0.2" gem "sprockets-rails", require: "sprockets/railtie" bootstrap_form-5.4.0/Rakefile0000644000004100000410000000127514620142366016266 0ustar www-datawww-databegin require 'bundler/setup' require 'bundler/gem_tasks' require "minitest/test_task" require 'rdoc/task' require 'rubocop/rake_task' rescue LoadError puts 'You must `gem install bundler` and `bundle install` to run rake tasks' end RDoc::Task.new(:rdoc) do |rdoc| rdoc.rdoc_dir = 'rdoc' rdoc.title = 'BootstrapForm' rdoc.options << '--line-numbers' rdoc.rdoc_files.include('README.md') rdoc.rdoc_files.include('lib/**/*.rb') end Minitest::TestTask.create(:test) do |t| t.libs << "test" t.libs << "lib" t.warning = false t.test_globs = ["test/**/*_test.rb"] end desc 'Run RuboCop checks' RuboCop::RakeTask.new(:rubocop) task default: %i[test rubocop:autocorrect] bootstrap_form-5.4.0/CODE_OF_CONDUCT.md0000644000004100000410000000643414620142366017422 0ustar www-datawww-data# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at lcreid@jadesystems.ca. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq bootstrap_form-5.4.0/Gemfile0000644000004100000410000000041214620142366016104 0ustar www-datawww-datagems = "#{__dir__}/gemfiles/common.gemfile" eval File.read(gems), binding, gems # rubocop: disable Security/Eval require "#{__dir__}/lib/bootstrap_form/version" gem "rails", BootstrapForm::REQUIRED_RAILS_VERSION gem "sprockets-rails", require: "sprockets/railtie" bootstrap_form-5.4.0/docker-compose.yml0000644000004100000410000000113614620142366020252 0ustar www-datawww-dataversion: '3.3' services: app: &app build: context: . args: NODE_MAJOR: "18" RUBY_VERSION: ${RUBY_VERSION} image: bootstrap-form:latest-$RUBY_VERSION tmpfs: - /tmp shell: &shell <<: *app stdin_open: true tty: true volumes: - .:/app:cached environment: - SSH_AUTH_SOCK=/ssh-agent - NODE_ENV=development - RAILS_ENV=${RAILS_ENV:-development} - BOOTSNAP_CACHE_DIR=/usr/local/bundle/_bootsnap - WEB_CONCURRENCY=1 - HISTFILE=/app/.bash_history ports: - "3000:3000" command: /bin/bash bootstrap_form-5.4.0/app/0000755000004100000410000000000014620142366015374 5ustar www-datawww-databootstrap_form-5.4.0/app/assets/0000755000004100000410000000000014620142366016676 5ustar www-datawww-databootstrap_form-5.4.0/app/assets/stylesheets/0000755000004100000410000000000014620142366021252 5ustar www-datawww-databootstrap_form-5.4.0/app/assets/stylesheets/rails_bootstrap_forms.css0000644000004100000410000000035614620142366026405 0ustar www-datawww-data.rails-bootstrap-forms-date-select select, .rails-bootstrap-forms-time-select select, .rails-bootstrap-forms-datetime-select select { display: inline-block; width: auto; } .rails-bootstrap-forms-error-summary { margin-top: 10px; } bootstrap_form-5.4.0/README.md0000644000004100000410000021473514620142366016107 0ustar www-datawww-data# bootstrap_form [![Ruby](https://github.com/bootstrap-ruby/bootstrap_form/actions/workflows/ruby.yml/badge.svg)](https://github.com/bootstrap-ruby/bootstrap_form/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/bootstrap_form.svg)](https://rubygems.org/gems/bootstrap_form) `bootstrap_form` is a Rails form builder that makes it super easy to integrate Bootstrap v5-style forms into your Rails application. It provides form helpers that augment the Rails form helpers. `bootstrap_forms`'s form helpers generate the form field and its label and all the Bootstrap mark-up required for proper Bootstrap display. `bootstrap_form` also provides: * [Validation error messages](#validation-and-errors) below the field they correspond to, by default. You can also put the error messages after the label, or turn off `bootstrap_form`'s validation error handling and do it yourself. _Note that this applies to Rails-generated validation messages._ HTML 5 client-side validation and Rails validation out of the box don't really work well together. One discussion of the challenges and some solutions is [here](https://www.jorgemanrubia.com/2019/02/16/form-validations-with-html5-and-modern-rails/) * Automatic [mark-up for the `required` attribute](#required-fields) on required fields. * An easy way to consistently show [help](#help-text) text on fields. * Mark-up for [Bootstrap horizontal forms](#horizontal-forms) (labels to the left of their fields, like a traditional desktop application), if that's what you want. * Many [options](#form-helpers) to modify or augment the generated mark-up. * A way to [escape to the Rails form helpers](#accessing-rails-form-helpers) if you need to do something that `bootstrap_form` can't do. Some other nice things that `bootstrap_form` does for you are: * Reduces the amount of code in your `.erb` files. * Gets you going faster with Bootstrap, because you don't need to learn all the rules of Bootstrap form mark-up to get started. * Reduces errors, because you're doing less typing. * Makes it easier to see the logic of the form, because it's not mixed in with the Bootstrap mark-up. `bootstrap_form` works like the standard Rails form helpers, and this README assumes you know how they work. You start a form with one of [`bootstrap_form_with`](#bootstrap_form_with), [`bootstrap_form_for`](#bootstrap_form_for), or [`bootstrap_form_tag`](#bootstrap_form_tag) in a view file. You get a form builder that calls the [`bootstrap_form` helpers](#form-helpers) instead of the standard Rails helpers. You use that form builder in the view file to render one or more form fields. ## Requirements `bootstrap_form` supports at a minimum the currently supported versions of Ruby and Rails: * Ruby 3.0+ (https://www.ruby-lang.org/en/downloads/branches/) * Rails 6.1+ (https://guides.rubyonrails.org/maintenance_policy.html) * Bootstrap 5.0+ ## Installation Install Bootstrap 5. There are many ways to do this, depending on the asset pipeline you're using in your Rails application. One way is to use the gem that works with Sprockets. To do so, in a brand new Rails 7.0+ application created _without_ the `--webpacker` option, add the `bootstrap` gem to your `Gemfile`: ```ruby gem "bootstrap", "~> 5.0" ``` And follow the remaining instructions in the [official bootstrap installation guide](https://github.com/twbs/bootstrap-rubygem#a-ruby-on-rails) for setting up `application.scss` and `application.js`. Add the `bootstrap_form` gem to your `Gemfile`: ```ruby gem "bootstrap_form", "~> 5.4" ``` Then: `bundle install` Depending on which CSS pre-processor you are using, adding the bootstrap form styles differs slightly. If you use Rails in the default mode without any pre-processor, you'll have to add the following line to your `application.css` file: ```css *= require rails_bootstrap_forms ``` If you followed the [official bootstrap installation guide](https://github.com/twbs/bootstrap-rubygem#a-ruby-on-rails), you'll probably have switched to SCSS. In this case add the following line to your `application.scss`: ```scss @import "rails_bootstrap_forms"; ``` ## Usage ### bootstrap_form_for To get started, use the `bootstrap_form_for` helper in place of the Rails `form_for` helper. Here's an example: ![Example 0](demo/doc/screenshots/bootstrap/readme/00_example.png "Example 0") ```erb <%= bootstrap_form_for(@user) do |f| %> <%= f.email_field :email %> <%= f.password_field :password %> <%= f.check_box :remember_me %> <%= f.submit "Log In" %> <% end %> ``` This generates the following HTML: ```html
``` ### bootstrap_form_tag If your form is not backed by a model, use the `bootstrap_form_tag`. Usage of this helper is the same as `bootstrap_form_for`, except no model object is passed in as the first argument. Here's an example: ![Example 1](demo/doc/screenshots/bootstrap/readme/01_example.png "Example 1") ```erb <%= bootstrap_form_tag url: '/subscribe' do |f| %> <%= f.email_field :email, value: 'name@example.com' %> <%= f.submit %> <% end %> ``` This generates: ```html
``` ### bootstrap_form_with To get started, just use the `bootstrap_form_with` helper in place of `form_with`. Here's an example: ![Example 2](demo/doc/screenshots/bootstrap/readme/02_example.png "Example 2") ```erb <%= bootstrap_form_with(model: @user, local: true) do |f| %> <%= f.email_field :email %> <%= f.password_field :password, help: 'A good password should be at least six characters long' %> <%= f.check_box :remember_me %> <%= f.submit "Log In" %> <% end %> ``` This generates: ```html
A good password should be at least six characters long
``` `bootstrap_form_with` supports both the `model:` and `url:` use cases in `form_with`. `form_with` has some important differences compared to `form_for` and `form_tag`, and these differences apply to `bootstrap_form_with`. A good summary of the differences can be found at: https://m.patrikonrails.com/rails-5-1s-form-with-vs-old-form-helpers-3a5f72a8c78a, or in the [Rails documentation](api.rubyonrails.org). ### bootstrap_fields_for and bootstrap_fields Adding fields for a different object without nesting can be achieved using the `bootstrap_fields_for` and `bootstrap_fields` helpers in the same way it is done in Rails. ![Example 3](demo/doc/screenshots/bootstrap/readme/03_example.png "Example 3") ```erb <%= bootstrap_form_with model: @user do |f| %> <%= f.email_field :email %> <%= bootstrap_fields_for :parent do |pf| %> <%= pf.email_field :email, label: 'Parent email' %> <% end %> <%= bootstrap_fields @user.address do |af| %> <%= af.text_field :street_name %> <% end %> <%= f.primary "Save" %> <% end %> ``` Generated HTML: ```html
``` ## Configuration `bootstrap_form` can be used out-of-the-box without any configuration. However, `bootstrap_form` does have an optional configuration file at `config/initializers/bootstrap_form.rb` for setting options that affect all generated forms in an application. The current configuration options are: | Option | Default value | Description | |---------------------------|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `default_form_attributes` | | `bootstrap_form` versions 3 and 4 added a role="form" attribute to all forms. The W3C validator will raise a **warning** on forms with a role="form" attribute. `bootstrap_form` version 5 drops this attribute by default. Set this option to `{ role: "form" }` to make forms non-compliant with W3C, but generate the `role="form"` attribute like `bootstrap_form` versions 3 and 4. | Example: ```ruby # config/initializers/bootstrap_form.rb BootstrapForm.configure do |c| c.default_form_attributes = { role: "form" } # to make forms non-compliant with W3C. end ``` ## Form Helpers `bootstrap_form` provides its own version of the following Rails form helpers: ``` button email_field search_field check_box file_field select collection_check_boxes grouped_collection_select submit collection_radio_buttons hidden_field (not wrapped, but supported) telephone_field collection_select month_field text_area color_field number_field text_field date_field password_field time_field date_select phone_field time_select datetime_field radio_button time_zone_select datetime_local_field range_field url_field datetime_select rich_text_area week_field ``` By default, the helpers generate a `label` tag, and an `input`, `select`, or `textarea` tag, by calling the Rails `label` helper, and then the Rails helper with the same name as the `bootstrap_form` helper. The `bootstrap_form` helpers accept the same options as the standard Rails form helpers, and pass those options through to the Rails helper. They also accept additional options, described in the following section. ## Form Helper Options Many of the helpers accept the same options. The exceptions are: [button](#submit-buttons), [check_box](#checkboxes-and-radios), [collection_check_boxes](#collections), [collection_radio_buttons](#collections), [collection_select](#selects), [date_select](#date-helpers), [datetime_select](#date-helpers), [file_field](#file-fields), [grouped_collection_select](#selects), [hidden_field](#hidden-fields), [radio_button](#checkboxes-and-radios), [rich_text_area](#rich-text-areas-aka-trix-editor), [select](#selects), [submit](#submit-buttons), [time_select](#date-helpers), [time_zone_select](#selects) The options for the form helpers that aren't in the exceptions list are described in the following sub-sections: ### Labels Use the `label` option if you want to specify the field's label text: ![Example 4](demo/doc/screenshots/bootstrap/readme/04_example.png "Example 4") ```erb <%= f.password_field :password_confirmation, label: "Confirm Password" %> ``` This generates: ```html
``` To hide a label, use the `hide_label: true` option. This adds the `visually-hidden` class, which keeps your labels accessible to those using screen readers. ![Example 5](demo/doc/screenshots/bootstrap/readme/05_example.png "Example 5") ```erb <%= f.text_area :comment, hide_label: true, placeholder: "Leave a comment..." %> ``` This generates: ```html
``` To add custom classes to the field's label: ![Example 6](demo/doc/screenshots/bootstrap/readme/06_example.png "Example 6") ```erb <%= f.email_field :email, label_class: "custom-class" %> ``` This generates: ```html
``` Or you can add the label as input placeholder instead (this automatically hides the label): ![Example 7](demo/doc/screenshots/bootstrap/readme/07_example.png "Example 7") ```erb <%= f.email_field :email, value: '', label_as_placeholder: true %> ``` This generates: ```html
``` ### Input Elements / Controls To specify the class of the generated input tag, use the `control_class` option: ![Example 8](demo/doc/screenshots/bootstrap/readme/08_example.png "Example 8") ```erb <%= f.text_field :email, control_class: "custom-class" %> ``` This generates: ```html
``` ### Help Text To add help text, use the `help` option: ![Example 9](demo/doc/screenshots/bootstrap/readme/09_example.png "Example 9") ```erb <%= f.password_field :password, help: "Must be at least 6 characters long" %> ``` This generates: ```html
Must be at least 6 characters long
``` This gem is also aware of help messages in locale translation files (i18n): ```yml en: activerecord: help: user: password: "A good password should be at least six characters long" ``` Help translations containing HTML should follow the convention of appending `_html` to the name: ```yml en: activerecord: help: user: password_html: "A good password should be at least six characters long" ``` If your model name has multiple words (like `SuperUser`), the key on the translation file should be underscored (`super_user`). You can override help translations for a particular field by passing the `help` option or turn them off completely by passing `help: false`. ### Prepending and Appending Inputs You can pass `prepend` and/or `append` options to input fields: ![Example 10](demo/doc/screenshots/bootstrap/readme/10_example.png "Example 10") ```erb <%= f.text_field :price, prepend: "$", append: ".00" %> ``` This generates: ```html
$ .00
``` If you want to attach multiple items to the input, pass them as an array: ![Example 11](demo/doc/screenshots/bootstrap/readme/11_example.png "Example 11") ```erb <% icon = capture do %><% end %> <%= f.text_field :price, prepend: ['Net', icon], append: ['.00', 'per day'] %> ``` This generates: ```html
Net .00 per day
``` You can also prepend and append buttons. Note: The buttons must contain the `btn` class to generate the correct markup. ![Example 12](demo/doc/screenshots/bootstrap/readme/12_example.png "Example 12") ```erb <%= f.text_field :search, append: link_to("Go", "#", class: "btn btn-secondary") %> ``` This generates: ```html
Go
``` To add a class to the input group wrapper, use the `:input_group_class` option. ![Example 13](demo/doc/screenshots/bootstrap/readme/13_example.png "Example 13") ```erb <%= f.email_field :email, append: f.primary('Subscribe'), input_group_class: 'input-group-lg' %> ``` This generates: ```html
``` ### Additional Form Group Attributes Bootstrap mark-up dictates that most input field types have the label and input wrapped in a `div.mb-3`. If you want to change the CSS class or any other attribute to the form group div, you can use the `wrapper: { class: 'mb-3 additional-class', data: { foo: 'bar' } }` option. ![Example 14](demo/doc/screenshots/bootstrap/readme/14_example.png "Example 14") ```erb <%= f.text_field :name, wrapper: { class: 'mb-3 has-warning', data: { foo: 'bar' } } %> ``` This generates: ```html
``` Which produces the following output: ![Example 15](demo/doc/screenshots/bootstrap/readme/15_example.png "Example 15") ```erb
``` This generates: ```html
``` If you only want to set the class on the form group div, you can use the `wrapper_class` option: `wrapper_class: 'mb-3 additional-class'`. It's just a short form of `wrapper: { class: 'mb-3 additional-class' }`. If you don't want any class on the form group div, you can set it to `false`: `wrapper_class: false`. ### Suppressing the Form Group Altogether You may want to define your own form group div around a field. To do so, add the option `wrapper: false` to the input field. For example: ![Example 16](demo/doc/screenshots/bootstrap/readme/16_example.png "Example 16") ```erb <%= f.form_group :user do %> <%= f.email_field :email, wrapper: false %> <% end %> ``` Generated HTML: ```html
``` Note that Bootstrap relies on the form group div to correctly format most fields, so if you use the `wrapper: false` option, you should provide your own form group div around the input field. You can write your own HTML, or use the `form_group` helper. ## Selects Our select helper accepts the same arguments as the [default Rails helper](http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-select). Here's an example of how you pass both options and html_options hashes: ![Example 17](demo/doc/screenshots/bootstrap/readme/17_example.png "Example 17") ```erb <%= f.select :product, [["Apple", 1], ["Grape", 2]], { label: "Choose your favorite fruit:", wrapper: { class: 'has-warning', data: { foo: 'bar' } } }, { class: "selectpicker" } %> ``` This generates: ```html
``` ## Checkboxes and Radios Checkboxes and radios should be placed inside of a `form_group` to render properly. The following example ensures that the entire form group will display an error if an associated validations fails: ![Example 18](demo/doc/screenshots/bootstrap/readme/18_example.png "Example 18") ```erb <%= f.form_group :skill_level, label: { text: "Skill" }, help: "Optional Help Text" do %> <%= f.radio_button :skill_level, 0, label: "Novice", checked: true %> <%= f.radio_button :skill_level, 1, label: "Intermediate" %> <%= f.radio_button :skill_level, 2, label: "Advanced" %> <% end %> <%= f.form_group :terms do %> <%= f.check_box :terms, label: "I agree to the Terms of Service" %> <% end %> ``` This generates: ```html
Optional Help Text
``` You can also create a checkbox using a block: ![Example 19](demo/doc/screenshots/bootstrap/readme/19_example.png "Example 19") ```erb <%= f.form_group :terms, label: { text: "Optional Label" } do %> <%= f.check_box :terms do %> You need to check this box to accept our terms of service and privacy policy <% end %> <% end %> ``` This generates: ```html
``` To display checkboxes and radios inline, pass the `inline: true` option: ![Example 20](demo/doc/screenshots/bootstrap/readme/20_example.png "Example 20") ```erb <%= f.form_group :skill_level, label: { text: "Skill" } do %> <%= f.radio_button :skill_level, 0, label: "Novice", inline: true %> <%= f.radio_button :skill_level, 1, label: "Intermediate", inline: true %> <%= f.radio_button :skill_level, 2, label: "Advanced", inline: true %> <% end %> ``` This generates: ```html
``` Check boxes and radio buttons are wrapped in a `div.form-check`. You can add classes to this `div` with the `:wrapper_class` option: ![Example 21](demo/doc/screenshots/bootstrap/readme/21_example.png "Example 21") ```erb <%= f.radio_button :skill_level, 0, label: "Novice", inline: true, wrapper_class: "w-auto" %> ``` This generates: ```html
``` You can also add a style to the tag using the `wrapper` option: ![Example 22](demo/doc/screenshots/bootstrap/readme/22_example.png "Example 22") ```erb <%= f.check_box :skilled, inline: true, wrapper: {style: "color: green"} %> <%= f.radio_button :skill_level, 0, label: "Novice", inline: true, wrapper: {class: 'w-auto', style: "color: red"} %> ``` This generates: ```html
``` ### Switches To render checkboxes as switches with Bootstrap 4.2+, use `switch: true`: ![Example 23](demo/doc/screenshots/bootstrap/readme/23_example.png "Example 23") ```erb <%= f.check_box :remember_me, switch: true %> ``` This generates: ```html
``` ### Collections `bootstrap_form` also provides helpers that automatically create the `form_group` and the `radio_button`s or `check_box`es for you: ![Example 24](demo/doc/screenshots/bootstrap/readme/24_example.png "Example 24") ```erb <%= f.collection_radio_buttons :skill_level, Skill.all, :id, :name %> <%= f.collection_check_boxes :skills, Skill.all, :id, :name %> ``` This generates: ```html
``` NOTE: These helpers do not currently support a block, unlike their equivalent Rails helpers. See issue [#477](https://github.com/bootstrap-ruby/bootstrap_form/issues/477). If you need to use the block syntax, use `collection_check_boxes_without_bootstrap` or `collection_radio_buttons_without_bootstrap` for now. Collection methods accept these options: * `:label`: Customize the `form_group`'s label * `:hide_label`: Pass true to hide the `form_group`'s label * `:help`: Add a help span to the `form_group` * Other options will be forwarded to the `radio_button`/`check_box` method To add `data-` attributes to a collection of radio buttons, map your models to an array and add a hash: ![Example 25](demo/doc/screenshots/bootstrap/readme/25_example.png "Example 25") ```erb <%# Use the :first and :second elements of the array to be the value and label respectively %> <%- choices = @collection.map { |addr| [ addr.id, addr.street, { 'data-zip-code': addr.zip_code } ] } -%> <%= f.collection_radio_buttons :misc, choices, :first, :second %> ``` This generates: ```html
``` ## Range Controls You can create a range control like this: ![Example 26](demo/doc/screenshots/bootstrap/readme/26_example.png "Example 26") ```erb <%= f.range_field :excellence %> ``` This generates: ```html
``` ## Static Controls You can create a static control like this: ![Example 27](demo/doc/screenshots/bootstrap/readme/27_example.png "Example 27") ```erb <%= f.static_control :email %> ``` This generates: ```html
``` Here's the output for a horizontal layout: ![Example 28](demo/doc/screenshots/bootstrap/readme/28_example.png "Example 28") ```erb <%= bootstrap_form_for(@user, layout: :horizontal) do |f| %> <%= f.static_control :email %> <% end %> ``` This generates: ```html
``` You can also create a static control that isn't based on a model attribute: ![Example 29](demo/doc/screenshots/bootstrap/readme/29_example.png "Example 29") ```erb <%= f.static_control :field_name, label: "Custom Static Control", value: "Content Here" %> ``` This generates: ```html
``` `field_name` may be any name that isn't already used in the form. Note that you may get "unpermitted parameter" messages in your log file with this approach. You can also create the static control the following way, if you don't need to get the value of the static control as a parameter when the form is submitted: ![Example 30](demo/doc/screenshots/bootstrap/readme/30_example.png "Example 30") ```erb <%= f.static_control label: "Custom Static Control", value: "Content Here", name: nil %> ``` This generates: ```html
``` (If you neither provide a field name nor `name: nil`, the Rails code that submits the form will give a JavaScript error.) Prior to version 4 of `bootstrap_form`, you could pass a block to the `static_control` method. The value of the block would be used for the content of the static "control". Bootstrap 4 actually creates and styles a disabled input field for static controls, so the value of the control has to be specified by the `value:` option. Passing a block to `static_control` no longer has any effect. ## Date Helpers The multiple selects that the date and time helpers (`date_select`, `time_select`, `datetime_select`) generate are wrapped inside a `div.rails-bootstrap-forms-[date|time|datetime]-select` tag. This is because Bootstrap automatically styles our controls as `block`s. This wrapper fixes this by defining these selects as `inline-block` and a width of `auto`. ## Submit Buttons The `btn btn-secondary` CSS classes are automatically added to your submit buttons. ![Example 31](demo/doc/screenshots/bootstrap/readme/31_example.png "Example 31") ```erb <%= f.submit %> ``` This generates: ```html ``` You can also use the `primary` helper, which adds `btn btn-primary` to your submit button: ![Example 32](demo/doc/screenshots/bootstrap/readme/32_example.png "Example 32") ```erb <%= f.primary "Optional Label" %> ``` This generates: ```html ``` You can specify your own classes like this: ![Example 33](demo/doc/screenshots/bootstrap/readme/33_example.png "Example 33") ```erb <%= f.submit "Log In", class: "btn btn-success" %> ``` This generates: ```html ``` If the `primary` helper receives a `render_as_button: true` option or a block, it will be rendered as an HTML button, instead of an input tag. This allows you to specify HTML content and styling for your buttons (such as adding illustrative icons to them). For example, the following statements ![Example 34](demo/doc/screenshots/bootstrap/readme/34_example.png "Example 34") ```erb <%= f.primary "Save changes ".html_safe, render_as_button: true %> <%= f.primary do concat 'Save changes ' concat content_tag(:span, nil, class: 'bi bi-save') end %> ``` This generates: ```html ``` are equivalent, and each of them both be rendered as: ```html ``` If you wish to add additional CSS classes to your button, while keeping the default ones, you can use the `extra_class` option. This is particularly useful for adding extra details to buttons (without forcing you to repeat the Bootstrap classes), or for element targeting via CSS classes. Be aware, however, that using the `class` option will discard any extra classes you add. As an example, the following button declarations ![Example 35](demo/doc/screenshots/bootstrap/readme/35_example.png "Example 35") ```erb <%= f.primary "My Nice Button", extra_class: 'my-button' %> <%= f.primary "My Button", class: 'my-button' %> ``` will be rendered as ```html ``` (some unimportant HTML attributes have been removed for simplicity) ## Rich Text Areas AKA Trix Editor ![Example 36](demo/doc/screenshots/bootstrap/readme/36_example.png "Example 36") ```erb <%= f.rich_text_area(:life_story) %> ``` will be rendered as: ```html
``` ## File Fields The `file_field` helper generates mark-up for a Bootstrap 4 custom file field entry. It takes the [options for `text_field`](#form-helper-options), minus `append` and `prepend`. ## Hidden Fields The `hidden_field` helper in `bootstrap_form` calls the Rails helper directly, and does no additional mark-up. ## Accessing Rails Form Helpers If you want to use the original Rails form helpers for a particular field, append `_without_bootstrap` to the helper: ![Example 37](demo/doc/screenshots/bootstrap/readme/37_example.png "Example 37") ```erb <%= f.text_field_without_bootstrap :email %> ``` This generates: ```html ``` ## Form Styles By default, your forms will stack labels on top of controls and your controls will grow to 100 percent of the available width. This is consistent with Bootstrap's "mobile first" approach. ### Inline Forms To use an inline-layout form, use the `layout: :inline` option. To hide labels, use the `hide_label: true` option, which keeps your labels accessible to those using screen readers. ![Example 38](demo/doc/screenshots/bootstrap/readme/38_example.png "Example 38") ```erb <%= bootstrap_form_for(@user, layout: :inline) do |f| %> <%= f.email_field :email, hide_label: true %> <%= f.password_field :password, hide_label: true %> <%= f.check_box :remember_me %> <%= f.submit %> <% end %> ``` This generates: ```html
``` To skip label rendering at all, use `skip_label: true` option. ![Example 39](demo/doc/screenshots/bootstrap/readme/39_example.png "Example 39") ```erb <%= f.password_field :password, skip_label: true %> ``` This generates: ```html
``` ### Horizontal Forms To use a horizontal-layout form with labels to the left of the control, use the `layout: :horizontal` option. You should specify both `label_col` and `control_col` css classes as well (they default to `col-sm-2` and `col-sm-10`). In the example below, the submit button has been wrapped in a `form_group` to keep it properly aligned. ![Example 40](demo/doc/screenshots/bootstrap/readme/40_example.png "Example 40") ```erb <%= bootstrap_form_for(@user, layout: :horizontal, label_col: "col-sm-2", control_col: "col-sm-10") do |f| %> <%= f.email_field :email %> <%= f.password_field :password %> <%= f.check_box :remember_me %> <%= f.form_group do %> <%= f.submit %> <% end %> <% end %> ``` This generates: ```html
``` The `label_col` and `control_col` css classes can also be changed per control: ![Example 41](demo/doc/screenshots/bootstrap/readme/41_example.png "Example 41") ```erb <%= bootstrap_form_for(@user, layout: :horizontal) do |f| %> <%= f.email_field :email %> <%= f.text_field :age, label_col: %w[col-sm-3 text-bg-info], control_col: %w[col-sm-3 text-bg-success] %> <%= f.check_box :terms, label_col: "", control_col: "col-sm-11" %> <%= f.form_group do %> <%= f.submit %> <% end %> <% end %> ``` This generates: ```html
``` or default value can be changed in initializer: ```ruby # config/initializers/bootstrap_form.rb module BootstrapForm class FormBuilder def default_label_col 'col-sm-4' end def default_control_col 'col-sm-8' end def default_layout # :vertical, :horizontal or :inline :horizontal end end end ``` Control col wrapper class can be modified with `add_control_col_class`. This option will preserve column definition: ![Example 42](demo/doc/screenshots/bootstrap/readme/42_example.png "Example 42") ```erb <%= bootstrap_form_for(@user, layout: :horizontal) do |f| %> <%= f.email_field :email %> <%= f.text_field :age, add_control_col_class: "additional-control-col-class" %> <%= f.form_group do %> <%= f.submit %> <% end %> <% end %> ``` This generates: ```html
``` ### Custom Field Layout The form-level `layout` can be overridden per field, unless the form-level layout was `inline`: ![Example 43](demo/doc/screenshots/bootstrap/readme/43_example.png "Example 43") ```erb <%= bootstrap_form_for(@user, layout: :horizontal) do |f| %> <%= f.email_field :email %>
<%= f.text_field :feet, layout: :vertical %>
<%= f.text_field :inches, layout: :vertical %>
<%= f.check_box :terms, layout: :vertical %> <%= f.submit %> <% end %> ``` This generates: ```html
``` A form-level `layout: :inline` can't be overridden because of the way Bootstrap 4 implements in-line layouts. One possible work-around is to leave the form-level layout as default, and specify the individual fields as `layout: :inline`, except for the fields(s) that should be other than in-line. ### Floating Labels The `floating` option can be used to enable Bootstrap 5's floating labels. This option is supported on text fields and dropdowns. Here's an example: ![Example 44](demo/doc/screenshots/bootstrap/readme/44_example.png "Example 44") ```erb <%= bootstrap_form_for(@user) do |f| %> <%= f.email_field :email, floating: true %> <%= f.password_field :password, floating: true %> <%= f.password_field :password, floating: true %> <%= f.select :status, [["Active", 1], ["Inactive", 2]], include_blank: "Select a value", floating: true %> <%= f.submit "Log In" %> <% end %> ``` This generates: ```html
``` ## Validation and Errors Rails normally wraps fields with validation errors in a `div.field_with_errors`, but this behaviour isn't consistent with Bootstrap 4 styling. By default, `bootstrap_form` generations in-line errors which appear below the field. But it can also generate errors on the label, or not display any errors, leaving it up to you. ### Inline Errors By default, fields that have validation errors will be outlined in red and the error will be displayed below the field. Here's an example: ![Example 45](demo/doc/screenshots/bootstrap/readme/45_example.png "Example 45") ```erb <%= bootstrap_form_for(@user_with_error) do |f| %> <%= f.email_field :email %> <%= f.collection_radio_buttons :misc, Skill.all, :id, :name %> <%= f.collection_check_boxes :preferences, [[1, 'Good'], [2, 'Bad']], :first, :second %> <%= f.fields_for :address do |af| %> <%= af.text_field :street %> <% end %> <% end %> ``` Generated HTML: ```html
is invalid
is invalid
is invalid
is invalid
``` You can turn off inline errors for the entire form like this: ```erb <%= bootstrap_form_for(@user_with_error, inline_errors: false) do |f| %> ... <% end %> ``` ### Label Errors You can also display validation errors in the field's label; just turn on the `:label_errors` option. Here's an example: ![Example 46](demo/doc/screenshots/bootstrap/readme/46_example.png "Example 46") ```erb <%= bootstrap_form_for(@user_with_error, label_errors: true) do |f| %> <%= f.email_field :email %> <% end %> ``` Generated HTML: ```html
``` By default, turning on `:label_errors` will also turn off `:inline_errors`. If you want both turned on, you can do that too: ```erb <%= bootstrap_form_for(@user, label_errors: true, inline_errors: true) do |f| %> ... <% end %> ``` ### Alert Messages To display an error message with an error summary, you can use the `alert_message` helper. This won't output anything unless a model validation has failed. ![Example 47](demo/doc/screenshots/bootstrap/readme/47_example.png "Example 47") ```erb <%= bootstrap_form_for @user_with_error do |f| %> <%= f.alert_message "Please fix the errors below." %> <% end %> ``` Which outputs: ```html

Please fix the errors below.

  • Email is invalid
  • Misc is invalid
  • Preferences is invalid
``` You can turn off the error summary like this: ![Example 48](demo/doc/screenshots/bootstrap/readme/48_example.png "Example 48") ```erb <%= bootstrap_form_for @user_with_error do |f| %> <%= f.alert_message "Please fix the errors below.", error_summary: false %> <% end %> ``` This generates: ```html
Please fix the errors below.
``` To output a simple unordered list of errors, use the `error_summary` helper. ![Example 49](demo/doc/screenshots/bootstrap/readme/49_example.png "Example 49") ```erb <%= bootstrap_form_for @user_with_error do |f| %> <%= f.error_summary %> <% end %> ``` Which outputs: ```html
``` ### Errors On If you want to display a custom inline error for a specific attribute not represented by a form field, use the `errors_on` helper. ![Example 50](demo/doc/screenshots/bootstrap/readme/50_example.png "Example 50") ```erb <%= bootstrap_form_for @user_with_error do |f| %> <%= f.errors_on :email %> <% end %> ``` Which outputs: ```html
Email is invalid
``` You can hide the attribute name like this: ![Example 51](demo/doc/screenshots/bootstrap/readme/51_example.png "Example 51") ```erb <%= bootstrap_form_for @user_with_error do |f| %> <%= f.errors_on :email, hide_attribute_name: true %> <% end %> ``` Which outputs: ```html
is invalid
``` You can also use a custom class for the wrapping div, like this: ![Example 52](demo/doc/screenshots/bootstrap/readme/52_example.png "Example 52") ```erb <%= bootstrap_form_for @user_with_error do |f| %> <%= f.errors_on :email, custom_class: 'custom-error' %> <% end %> ``` Which outputs: ```html
Email is invalid
``` ## Required Fields A label that is associated with a required field is automatically annotated with a `required` CSS class. `bootstrap_form` doesn't provide any styling for required fields. You're free to add any appropriate CSS to style required fields as desired. One example would be to automatically add an asterisk to the end of the label: ```css label.required:after { content:" *"; } ``` The label `required` class is determined based on the definition of a presence validator with the associated model attribute. Presently this is one of: ActiveRecord::Validations::PresenceValidator or ActiveModel::Validations::PresenceValidator. In cases where this behaviour is undesirable, use the `required` option to force the class to be present or absent: ![Example 53](demo/doc/screenshots/bootstrap/readme/53_example.png "Example 53") ```erb <%= f.password_field :login, label: "New Username", required: true %> <%= f.password_field :password, label: "New Password", required: false %> ``` This generates: ```html
``` ### Required belongs_to associations Adding a form control for a `belongs_to` field will automatically pick up the associated presence validator. ![Example 54](demo/doc/screenshots/bootstrap/readme/54_example.png "Example 54") ```erb <%= bootstrap_form_for(@address, url: '/address') do |f| %> <%= f.collection_select :user_id, @users, :id, :email, include_blank: "Select a value" %> <%= f.text_field :street %> <%= f.text_field :city %> <%= f.text_field :state %> <%= f.text_field :zip_code %> <%= f.submit "Save" %> <% end %> ``` Generated HTML: ```html
``` ## Disabled fields Fields can be disabled using the standard Rails form helper option. ![Example 55](demo/doc/screenshots/bootstrap/readme/55_example.png "Example 55") ```erb <%= bootstrap_form_for @user do |f| %>
<%= f.email_field :email, disabled: true, size: 18 %>
<%= f.password_field :password, disabled: true, size: 18 %>
<%= f.text_area :comments, disabled: true, rows: 2, cols: 18 %>
<%= f.text_field :status, disabled: true, size: 18 %>
<%= f.number_field :misc, label: "Number", disabled: true %>
<%= f.radio_button :preferences, 1, disabled: true %>
<%= f.check_box :terms, disabled: true %>
<%= f.select :type, [1,2,3], {}, disabled: true %>
<%= f.datetime_field :created_at, disabled: true %>
<%= f.primary disabled: true %> <% end %> ``` Generated HTML: ```html
``` ## Internationalization bootstrap_form follows standard rails conventions so it's i18n-ready. See more here: http://guides.rubyonrails.org/i18n.html#translations-for-active-record-models ## Future Compatibility The Rails team has [suggested](https://github.com/rails/rails/issues/25197) that `form_for` and `form_tag` may be deprecated and then removed in future versions of Rails. `bootstrap_form` will continue to support `bootstrap_form_for` and `bootstrap_form_tag` as long as Rails supports `form_for` and `form_tag`. ## Other Tips and Edge Cases By their very nature, forms are extremely diverse. It would be extremely difficult to provide a gem that could handle every need. Here are some tips for handling edge cases. ### Empty But Visible Labels Some third party plug-ins require an empty but visible label on an input control. The `hide_label` option generates a label that won't appear on the screen, but it's considered invisible and therefore doesn't work with such a plug-in. An empty label (e.g. `""`) causes the underlying Rails helper to generate a label based on the field's attribute's name. The solution is to use a zero-width character for the label, or some other "empty" HTML. For example: ```ruby label: "​".html_safe ``` or ```ruby label: "".html_safe ``` ## Contributing We welcome contributions. If you're considering contributing to bootstrap_form, please review the [Contributing](/CONTRIBUTING.md) document first. ## Previous Version If you're looking for `bootstrap_form` for Bootstrap 4, go [here](https://github.com/bootstrap-ruby/bootstrap_form/tree/bootstrap-4). ## License MIT License. Copyright 2012-2021 Stephen Potenza (https://github.com/potenza) and others bootstrap_form-5.4.0/.rubocop.yml0000644000004100000410000000352414620142366017072 0ustar www-datawww-datarequire: - rubocop-performance - rubocop-rails AllCops: DisplayCopNames: true DisplayStyleGuide: true TargetRubyVersion: 3.0 TargetRailsVersion: 6.1 NewCops: enable Exclude: - bin/* - Capfile - demo/bin/* - demo/bower_components/**/* - demo/config/boot.rb - demo/config/environment.rb - demo/config/initializers/version.rb - demo/db/schema.rb - demo/node_modules/**/* - demo/Rakefile - demo/tmp/**/* - demo/vendor/**/* - Gemfile - gemfiles/vendor/bundle/**/* - vendor/bundle/**/* - Guardfile - Rakefile - vendor/**/* Layout/LineLength: Max: 132 Exclude: - "demo/config/**/*" - "demo/db/**/*" Layout/SpaceAroundEqualsInParameterDefault: EnforcedStyle: no_space Metrics/AbcSize: Max: 18 Exclude: - "demo/test/**/*" - "test/**/*" Metrics/BlockLength: Exclude: - "lib/bootstrap_form/inputs/base.rb" - "demo/config/**/*" - "demo/test/**/*" - "test/**/*" Metrics/ClassLength: Exclude: - "demo/test/**/*" - "test/**/*" Metrics/MethodLength: Max: 12 Exclude: - "demo/db/migrate/*" - "demo/test/**/*" - "test/**/*" Naming/MemoizedInstanceVariableName: EnforcedStyleForLeadingUnderscores: optional Naming/VariableNumber: Enabled: false Rails: Enabled: true Rails/ApplicationRecord: Exclude: - "demo/db/migrate/**" Rails/RefuteMethods: Enabled: false Rails/Validation: Enabled: false Style/BarePercentLiterals: EnforcedStyle: percent_q Style/ClassAndModuleChildren: Enabled: false Style/Documentation: Enabled: false Style/DoubleNegation: Enabled: false Style/EmptyMethod: Enabled: false Style/FrozenStringLiteralComment: Enabled: false Style/NumericPredicate: Enabled: false Style/StringLiterals: EnforcedStyle: double_quotes Style/TrivialAccessors: AllowPredicates: true bootstrap_form-5.4.0/CHANGELOG.md0000644000004100000410000005336014620142366016434 0ustar www-datawww-data# Change Log You can find recent releases with docs in GitHub: https://github.com/bootstrap-ruby/bootstrap_form/releases ## Diffs - [5.4.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v5.3.2...v5.4.0) [Closed issues](https://github.com/bootstrap-ruby/bootstrap_form/issues?q=closed%3A2023-09-15T21%3A00..2023-10-30T00) - [5.3.2](https://github.com/bootstrap-ruby/bootstrap_form/compare/v5.3.1...v5.3.2) [Closed issues](https://github.com/bootstrap-ruby/bootstrap_form/issues?q=closed%3A2023-09-15T15%3A00..2023-09-15T21%3A00) - [5.3.1](https://github.com/bootstrap-ruby/bootstrap_form/compare/v5.3.0...v5.3.1) [Closed issues](https://github.com/bootstrap-ruby/bootstrap_form/issues?q=closed%3A2023-09-15T13%3A00..2023-09-15T15%3A00) - [5.3.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v5.2.3...v5.3.0) [Closed issues](https://github.com/bootstrap-ruby/bootstrap_form/issues?q=closed%3A2023-06-18T07%3A00..2023-09-15T13%3A00) - [5.2.2](https://github.com/bootstrap-ruby/bootstrap_form/compare/v5.2.2...v5.2.3) [Closed issues](https://github.com/bootstrap-ruby/bootstrap_form/issues?q=closed%3A2023-05-28T16%3A00..2023-06-18T07%3A00) - [5.2.2](https://github.com/bootstrap-ruby/bootstrap_form/compare/v5.2.1...v5.2.2) [Closed issues](https://github.com/bootstrap-ruby/bootstrap_form/issues?q=closed%3A2023-05-23T10%3A00..2023-05-28T16%3A00) - [5.2.1](https://github.com/bootstrap-ruby/bootstrap_form/compare/v5.2.0...v5.2.1) [Closed issues](https://github.com/bootstrap-ruby/bootstrap_form/issues?q=closed%3A2023-05-20T23%3A59..2023-05-23T10%3A00) - [5.2.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v5.1.0...v5.2.0) [Closed issues](https://github.com/bootstrap-ruby/bootstrap_form/issues?q=closed%3A2022-06-25T17%3A22..2023-05-20T23%3A59) - [5.1.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v5.0.0...v5.1.0) [Closed issues](https://github.com/bootstrap-ruby/bootstrap_form/issues?q=closed%3A2021-11-12T05%3A58..2022-06-25T17%3A22) - [5.0.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v4.5.0...v5.0.0) - [4.5.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v4.4.0...v4.5.0) - [4.4.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v4.3.0...v4.4.0) - [4.3.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v4.2.0...v4.3.0) - [4.2.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v4.1.0...v4.2.0) - [4.1.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v4.0.0...v4.1.0) - [4.0.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v4.0.0.alpha1...v4.0.0) - [4.0.0.alpha1](https://github.com/bootstrap-ruby/bootstrap_form/compare/v2.7.0...v4.0.0.alpha1) - [2.7.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v2.6.0...v2.7.0) - [2.6.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v2.5.3...v2.6.0) - [2.5.3](https://github.com/bootstrap-ruby/bootstrap_form/compare/v2.5.2...v2.5.3) - [2.5.2](https://github.com/bootstrap-ruby/bootstrap_form/compare/v2.5.1...v2.5.2) - [2.5.1](https://github.com/bootstrap-ruby/bootstrap_form/compare/v2.5.0...v2.5.1) - [2.5.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v2.4.0...v2.5.0) - [2.4.0](https://github.com/bootstrap-ruby/bootstrap_form/compare/v2.3.0...v2.4.0) ## Older releases The tooling to support our old way of doing the change log doesn't work anymore, and we don't have a better solution, so this change log has become unreliable since version 4.5.0. Please refer to the commit history if you need to debug changes. ## [Pending Release][] ### Breaking changes * [#618](https://github.com/bootstrap-ruby/bootstrap_form/pull/618): Allow overriding the wrapper class - [@donv](https://github.com/donv). * Your contribution here! ### New features * [#572](https://github.com/bootstrap-ruby/bootstrap_form/issues/572): Simplify the formatting of the file upload control to follow the new Bootstrap 5 styles * [#573](https://github.com/bootstrap-ruby/bootstrap_form/issues/573): Add support for Bootstrap 5's floating labels * [#215](https://github.com/bootstrap-ruby/bootstrap_form/issues/215): Add `include_hidden` option to `check_box` ### Bugfixes * [#582](https://github.com/bootstrap-ruby/bootstrap_form/issues/582): Fix tests in bootstrap-5 branch, removes Rubocop offenses, and adds testing with Rails 6.1. ## [5.0.0.alpha1][] ### Breaking changes * [#569] Remove `role="form"` from the default generated form HTML so forms pass W3C validation. (Only a breaking change if you depended on the `form` attribute. `bootstrap_form` doesn't depend on it.) ### New features * Your contribution here! ### Bugfixes * [#284] [#300] Field's help message is displayed, even while inline error message is displayed. - [@antpaw](https://github.com/antpaw) ## [5.0.0][] (2021-11-11) ### Breaking changes * Generates markup for Bootstrap 5. ### New features * [#572](https://github.com/bootstrap-ruby/bootstrap_form/issues/572): Simplify the formatting of the file upload control to follow the new Bootstrap 5 styles * [#573](https://github.com/bootstrap-ruby/bootstrap_form/issues/573): Add support for Bootstrap 5's floating labels ### Bugfixes * [#582](https://github.com/bootstrap-ruby/bootstrap_form/issues/582): Fix tests in bootstrap-5 branch, removes Rubocop offenses, and adds testing with Rails 6.1. ## [5.0.0.alpha1][] ### Breaking changes * [#569] Remove `role="form"` from the default generated form HTML so forms pass W3C validation. (Only a breaking change if you depended on the `form` attribute. `bootstrap_form` doesn't depend on it.) ### New features * Your contribution here! ### Bugfixes * [#586](https://github.com/bootstrap-ruby/bootstrap_form/pull/586): Fix Rails 6.1 tests on master - [@thimo](https://github.com/thimo). * [#587](https://github.com/bootstrap-ruby/bootstrap_form/pull/587): Replace `strip_heredoc` with `<<~` - [@thimo](https://github.com/thimo). ## [4.5.0][] (2020-04-29) ### New features * [#562](https://github.com/bootstrap-ruby/bootstrap_form/pull/562): Allow to configure default form attributes - [@sharshenov](https://github.com/sharshenov). ## [4.4.0][] (2020-03-08) ### New features * [#557](https://github.com/bootstrap-ruby/bootstrap_form/pull/557): Allow prepending and appending multiple items to an input by passing an array to `prepend` and `append` options - [@donv](https://github.com/donv). * [#550](https://github.com/bootstrap-ruby/bootstrap_form/pull/550): Add `default_layout` so we can use it for all forms - [@duleorlovic](https://github.com/duleorlovic). ### Bugfixes * Your contribution here! ## [4.3.0][] (2019-09-22) ### New features * [#503] Support Rails 6.0.0. * Small documentation changes. ## [4.2.0][] (2019-03-08) ### New features * [#508] Support `rich_text_area` AKA the Trix editor on Rails 6+. * [#518] Move all inputs to separate, more maintainable files. * [#514](https://github.com/bootstrap-ruby/bootstrap_form/pull/514): Add support for BS 4.2 switches - [@simmerz](https://github.com/simmerz) ### Bugfixes * [#522](https://github.com/bootstrap-ruby/bootstrap_form/pull/522): Clean up rubocop offences - [@simmerz](https://github.com/simmerz) * [#524](https://github.com/bootstrap-ruby/bootstrap_form/pull/524): Fix non-inline layout rendering without help text - [@simmerz](https://github.com/simmerz) ## [4.1.0][] (2019-01-19) ### New features - [#259] Allow to render input without wrapper [@yevhene]. ### Bugfixes * [#496] Ensure required attribute is passed through to input tag. ## [4.0.0][] (2018-10-27) 🚨 **This release adds support for Bootstrap v4 and drops support for Bootstrap v3.** 🚨 If your app uses Bootstrap v3, you should continue using bootstrap_form 2.7.x instead. Bootstrap v3 and v4 are very different, and thus bootstrap_form now produces different markup in order to target v4. The changes are too many to list here; you can refer to Bootstrap's [Migrating to v4](https://getbootstrap.com/docs/4.0/migration/) page for a detailed explanation. In addition to these necessary markup changes, the bootstrap_form API itself has the following important changes in this release. ### Breaking changes * See [Migrating to v4](https://getbootstrap.com/docs/4.0/migration/). ### New features * [#476] Give a way to pass classes to the `div.form-check` wrapper for check boxes and radio buttons - [@lcreid](https://github.com/lcreid). * [461](https://github.com/bootstrap-ruby/bootstrap_form/pull/461): default form-inline class applied to parent content div on date select helpers. Can override with a :skip_inline option on the field helper - [@lancecarlson](https://github.com/lancecarlson). * The `button`, `submit`, and `primary` helpers can now receive an additional option, `extra_class`. This option allows us to specify additional CSS classes to be added to the corresponding button/input, _while_ maintaining the original default ones. E.g., a primary button with an `extra_class` 'test-button' will have its final CSS classes declaration as 'btn btn-primary test-button'. * [#488](https://github.com/bootstrap-ruby/bootstrap_form/pull/488): add required option on form_group_builder - [@ThomasSevestre](https://github.com/ThomasSevestre). ### Bugfixes * [#347](https://github.com/bootstrap-ruby/bootstrap_form/issues/347) Fix `wrapper_class` and `wrapper` options for helpers that have `html_options`. * [#472](https://github.com/bootstrap-ruby/bootstrap_form/pull/472) Use `id` option value as `for` attribute of label for custom checkboxes and radio buttons. * [#478](https://github.com/bootstrap-ruby/bootstrap_form/issues/478) Fix offset for form group without label when multiple label widths are specified. ## [4.0.0.alpha1][] (2018-06-16) 🚨 **This release adds support for Bootstrap v4 and drops support for Bootstrap v3.** 🚨 If your app uses Bootstrap v3, you should continue using bootstrap_form 2.7.x instead. Bootstrap v3 and v4 are very different, and thus bootstrap_form now produces different markup in order to target v4. The changes are too many to list here; you can refer to Bootstrap's [Migrating to v4](https://getbootstrap.com/docs/4.0/migration/) page for a detailed explanation. In addition to these necessary markup changes, the bootstrap_form API itself has the following important changes in this release. ### Breaking changes * Rails 4.x is no longer supported * Ruby 2.2 or newer is required * Built-in support for the `nested_form` gem has been completely removed * The `icon` option is no longer supported (Bootstrap v4 does not include icons) * The deprecated Rails methods `check_boxes_collection` and `radio_buttons_collection` have been removed * `hide_label: true` and `skip_label: true` on individual check boxes and radio buttons apply Bootstrap 4 markup. This means the appearance of a page may change if you're upgrading from the Bootstrap 3 version of `bootstrap_form`, and you used `check_box` or `radio_button` with either of those options * `static_control` will no longer properly show error messages. This is the result of bootstrap changes. * `static_control` will also no longer accept a block, use the `value` option instead. * `form_group` with a block that produces arbitrary text needs to be modified to produce validation error messages (see the UPGRADE-4.0 document). [@lcreid](https://github.com/lcreid). * `form_group` with a block that contains more than one `check_box` or `radio_button` needs to be modified to produce validation error messages (see the UPGRADE-4.0 document). [@lcreid](https://github.com/lcreid). * [#456](https://github.com/bootstrap-ruby/bootstrap_form/pull/456): Fix label `for` attribute when passing non-english characters using `collection_check_boxes` - [@ug0](https://github.com/ug0). * [#449](https://github.com/bootstrap-ruby/bootstrap_form/pull/449): Bootstrap 4 no longer mixes in `.row` in `.form-group`. `bootstrap_form` adds `.row` to `div.form-group` when layout is horizontal. ### New features * Support for Rails 5.1 `form_with` - [@lcreid](https://github.com/lcreid). * Support Bootstrap v4's [Custom Checkboxes and Radios](https://getbootstrap.com/docs/4.0/components/forms/#checkboxes-and-radios-1) with a new `custom: true` option * Allow HTML in help translations by using the `_html` suffix on the key - [@unikitty37](https://github.com/unikitty37) * [#408](https://github.com/bootstrap-ruby/bootstrap_form/pull/408): Add option[:id] on static control #245 - [@duleorlovic](https://github.com/duleorlovic). * [#455](https://github.com/bootstrap-ruby/bootstrap_form/pull/455): Support for i18n `:html` subkeys in help text - [@jsaraiva](https://github.com/jsaraiva). * Adds support for `label_as_placeholder` option, which will set the label text as an input fields placeholder (and hiding the label for sr_only). * [#449](https://github.com/bootstrap-ruby/bootstrap_form/pull/449): Passing `.form-row` overrides default `.form-group.row` in horizontal layouts. * Added an option to the `submit` (and `primary`, by transitivity) form tag helper, `render_as_button`, which when truthy makes the submit button render as a button instead of an input. This allows you to easily provide further styling to your form submission buttons, without requiring you to reinvent the wheel and use the `button` helper (and having to manually insert the typical Bootstrap classes). - [@jsaraiva](https://github.com/jsaraiva). * Add `:error_message` option to `check_box` and `radio_button`, so they can output validation error messages if needed. [@lcreid](https://github.com/lcreid). * [#487](https://github.com/bootstrap-ruby/bootstrap_form/pull/487): Add add_control_col_class option on form_group. - [@ThomasSevestre](https://github.com/ThomasSevestre). * Your contribution here! ### Bugfixes * [#357](https://github.com/bootstrap-ruby/bootstrap_form/pull/357) if provided, use html option `id` to specify `for` attribute on label [@duleorlovic](https://github.com/duleorlovic) ## [2.7.0][] (2017-04-21) Features: * [#325](https://github.com/bootstrap-ruby/bootstrap_form/pull/325): Support :prepend and :append for the `select` helper - [@donv](https://github.com/donv). ## [2.6.0][] (2017-02-03) Bugfixes: - Fix ambiguous first argument warning (#311, @mikenicklas) Features: - Add a FormBuilder#custom_control helper [#289](https://github.com/bootstrap-ruby/bootstrap_form/pull/289) ## [2.5.3][] (2016-12-23) There are no user-facing changes with this release. Behind the scenes, the tests have been greatly improved. The project is now tested against and compatible with the following Rails versions 4.0, 4.1, 4.2 and 5.0 (#278). ## [2.5.2][] (2016-10-08) Bugfixes: - Allow objects without `model_name`s to act as form objects (#295, @laserlemon) - Fix offset for submit for horizontal forms when using non-sm column breakers for label column (#293, @oteyatosys) ## [2.5.1][] (2016-09-23) Bugfixes: - Fix getting help text for elements when using anonymous models (see [issue 282](https://github.com/bootstrap-ruby/bootstrap_form/issues/282)) ## [2.5.0][] (2016-08-12) Bugfixes: - Sanitize