custom_inputs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f9096f6fc66e82395b149079a7a2d4ecbcb7cca373642fa1004d70b24faed964
4
+ data.tar.gz: 65566a633b1e6d5081797a66142a6819adf79d6732e10f01c21893b91350574b
5
+ SHA512:
6
+ metadata.gz: c941894463a216ff2d2c256923b114531bff25724eb293b677bee915724c96e00ea5f31b27b5c75ff22cca1c5533bd3a1512f2403d454d731bf0868ce37cb120
7
+ data.tar.gz: a27ffbcd6432b1b1a0b2d1e5767c5e943678c9f69653555fe266019970bf24a9a5023cc84c167a73443e55166b967e1531b0293ab8ffcda02b8a08202c31e5fb
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .idea
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.4
7
+ before_install: gem install bundler -v 1.17.2
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at hasan.a.ayar@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in custom_inputs.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,35 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ custom_inputs (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.3)
10
+ rake (13.0.1)
11
+ rspec (3.9.0)
12
+ rspec-core (~> 3.9.0)
13
+ rspec-expectations (~> 3.9.0)
14
+ rspec-mocks (~> 3.9.0)
15
+ rspec-core (3.9.1)
16
+ rspec-support (~> 3.9.1)
17
+ rspec-expectations (3.9.0)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.9.0)
20
+ rspec-mocks (3.9.1)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.9.0)
23
+ rspec-support (3.9.2)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ bundler (~> 2)
30
+ custom_inputs!
31
+ rake (~> 13.0)
32
+ rspec (~> 3.0)
33
+
34
+ BUNDLED WITH
35
+ 2.1.4
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Hasan Ali Ayar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # Custom Inputs for Formtastic (Active Admin)
2
+
3
+ I've been using [Active Admin](https://github.com/activeadmin/activeadmin) for developing admin panels for my clients.
4
+ There are number of custom input components accumulated along the way.
5
+
6
+ I've decided to make a gem and share all these custom input components that I think may be very useful.
7
+
8
+
9
+ These inputs are optimised for [Arctic Admin](https://github.com/cprodhomme/arctic_admin) theme.
10
+ If you are using any other theme you can ignore the css files of this gem and implement your own css.
11
+
12
+ *Note:* Although these inputs are being actively used in a production app you should use them at your own risk.
13
+
14
+ - [Array input](#array-input)
15
+ - [List input](#list-input)
16
+
17
+
18
+ (More to come...)
19
+ - Code Editor input (...)
20
+ - Query Builder input (...)
21
+ - Remote Image Select input (...)
22
+ - Rich Text input (...)
23
+ - Upload To S3 input (...)
24
+
25
+ ## Installation
26
+
27
+ Add this line to your application's Gemfile:
28
+
29
+ ```ruby
30
+ gem 'custom_inputs'
31
+ ```
32
+
33
+ And then execute:
34
+
35
+ $ bundle
36
+
37
+ Or install it yourself as:
38
+
39
+ $ gem install custom_inputs
40
+
41
+ ## Usage
42
+
43
+ All inputs comes with a javascript and scss file. You need to include these files wherever and whenever you want them to be included.
44
+
45
+ For a quick start you can add them to the base active admin asset files like so:
46
+
47
+
48
+ Append @ app/assets/javascripts/active_admin.js:
49
+ ```javascript
50
+ //= require custom_inputs/array-input
51
+ //= require custom_inputs/list-input
52
+ ```
53
+
54
+ Append @ app/assets/stylesheets/active_admin.scss :
55
+ ```scss
56
+ @import "custom_inputs/array_input";
57
+ @import "custom_inputs/list_input";
58
+ ```
59
+
60
+ ### Array Input
61
+ ![Array input sample](examples/images/array-input-sample.png)
62
+ Array input is specially made for PostgreSQL array fields.
63
+ You can add/remove string items to the input and they'll be packed in an array field.
64
+
65
+ Add the field to the permitted parameter as an array.
66
+ ```ruby
67
+ permit_params colors: []
68
+ ```
69
+
70
+ ```ruby
71
+ form do |f|
72
+ f.inputs do
73
+ f.input :colors, as: :array, undoable: true
74
+ end
75
+ f.actions
76
+ end
77
+ ```
78
+
79
+ #### Available Options
80
+
81
+ - **`undoable`:** deleted array items can be un-deleted if this flag set to true.
82
+
83
+ ### List Input
84
+ ![List input sample](examples/images/list-input-sample.png)
85
+
86
+ List input helps you generate a selection of items (array of hashes) from the supplied `collection_object` option.
87
+
88
+ You can select items from a drop down list, with an optional quantity.
89
+
90
+ The selected items are displayed in an html table.
91
+
92
+ You can determine which fields of the items are to be used by `fields` option.
93
+
94
+ This input will eventually generate an array of hashes with specified fields to be submitted.
95
+
96
+
97
+ ```ruby
98
+ form do |f|
99
+ f.inputs do
100
+ f.input :order_items, as: :list,
101
+ has_quantity: true,
102
+ collection_object: Product.all,
103
+ to_s_method: :name,
104
+ unique_field: :id,
105
+ fields: [
106
+ {'Product number': :id},
107
+ {'Name': :name}
108
+ ]
109
+ end
110
+ f.actions
111
+ end
112
+ ```
113
+ #### Available Options
114
+
115
+ - **`has_quantity`:** append quantity field to the items in the list.
116
+ - **`collection_object*`:** the list of items to choose from.
117
+ - **`to_s_method`:** (default `to_s`) the method to call for each item in the `collection_object` to display it in the dropdown list.
118
+ - **`unique_field`:** (default `id`) the field that uniquely identifies the items in the `collection_object`.
119
+ - **`fields*`:** only the supplied fields of the items will be used in the input.
120
+ The keys are used as table column headers and the values ara the actual field names.
121
+
122
+
123
+ ## Contributing
124
+
125
+ Bug reports and pull requests are very welcome.
126
+
127
+ ## License
128
+
129
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,67 @@
1
+ class ArrayModifier {
2
+ static removeFromArrayInput(e) {
3
+ let button = this;
4
+ let input = button.previousSibling;
5
+ let undoable = button.classList.contains('undoable');
6
+
7
+ if (undoable) {
8
+ if (input.disabled) {
9
+ button.innerHTML = `<i class="fa fa-minus-circle"></i>`;
10
+ input.disabled = false;
11
+ return input.classList.remove('text--strike');
12
+ } else {
13
+ button.innerHTML = `<i class="fa fa-undo"></i>`;
14
+ input.disabled = true;
15
+ input.classList.add('text--strike');
16
+ }
17
+ } else {
18
+ button.parentElement.remove();
19
+ }
20
+ }
21
+
22
+ static createNewInput(value) {
23
+ let template = document.querySelector('.input-group--array__item.template');
24
+ let clone = template.cloneNode(true);
25
+
26
+ clone.classList.remove('template', 'hidden')
27
+
28
+ let input = clone.querySelector('input');
29
+ input.removeAttribute('disabled');
30
+ input.value = value;
31
+ input.focus();
32
+
33
+ return clone;
34
+ }
35
+
36
+ static handleKeyDown(event) {
37
+ if (event.key === 'Enter') {
38
+ ArrayModifier.handleAddClick(event, this.nextSibling);
39
+ return false;
40
+ }
41
+ }
42
+
43
+ static handleAddClick(e, b) {
44
+ e.preventDefault();
45
+
46
+ let button = b || this;
47
+ let newItemField = button.parentNode;
48
+ let input = button.previousSibling;
49
+
50
+ let value = input.value;
51
+ if (value !== '') {
52
+ input.value = '';
53
+ let clone = ArrayModifier.createNewInput(value);
54
+ newItemField.parentNode.insertBefore(clone, newItemField);
55
+ } else {
56
+ return input.focus();
57
+ }
58
+ }
59
+ }
60
+
61
+ $(document).ready(function () {
62
+ $('.js-add-to-array-input').click(ArrayModifier.handleAddClick);
63
+
64
+ let arrayInputContainer = $('.input-group--array');
65
+ $(arrayInputContainer).on('click', '.js-remove-from-array-input', ArrayModifier.removeFromArrayInput);
66
+ $(arrayInputContainer).on('keydown', '.input-group--array__item input', ArrayModifier.handleKeyDown)
67
+ });
@@ -0,0 +1,12 @@
1
+ function clearEmptyValues() {
2
+ console.log('custom_inputs.js : clearEmptyValues');
3
+ document.querySelectorAll('.custom-inputs--input-field').forEach((input) => {
4
+ if (!input.value) {
5
+ input.setAttribute('disabled', 'disabled');
6
+ }
7
+ });
8
+ }
9
+
10
+ $(document).ready(function () {
11
+ $('form').submit(clearEmptyValues);
12
+ });
@@ -0,0 +1,79 @@
1
+ //= require ./custom_inputs
2
+
3
+ class ListModifier {
4
+ static removeFromArrayInput(button) {
5
+ button.parentElement.parentElement.remove();
6
+ }
7
+
8
+ // Add item button click
9
+ static addToItems(e) {
10
+ e.preventDefault;
11
+
12
+ let add_button = this;
13
+ if (add_button.classList.contains('array-action--add')) {
14
+ let container = add_button.parentElement.parentElement;
15
+ let item_table = container.querySelector('.item-table tbody');
16
+ let template_row = container.querySelector('.item-row');
17
+
18
+ //Get selected values
19
+ let quantity_val = container.querySelector('input').value;
20
+ let item_val = JSON.parse(container.querySelector('select').value);
21
+ let row_id = 'row_' + item_val['id'];
22
+
23
+ //If this item is selected before, we'll update it's quantity instead of inserting a new row.
24
+ let row_to_be_updated = item_table.querySelector('#' + row_id);
25
+
26
+ let new_row = false;
27
+ if (!row_to_be_updated) {
28
+ //Clone the item row and show it.
29
+ row_to_be_updated = template_row.cloneNode(true);
30
+ row_to_be_updated.classList.remove('hidden');
31
+ row_to_be_updated.setAttribute('id', row_id);
32
+ new_row = true;
33
+ }
34
+
35
+ // Find elements to manipulate.
36
+ // Html td cells contain field attributes with the key of contained value.
37
+ let fields = row_to_be_updated.querySelectorAll('td[field]');
38
+ fields.forEach(field => {
39
+ let fieldKey = field.getAttribute('field');
40
+ let fieldValue = item_val[fieldKey];
41
+
42
+ // Put value in table cell
43
+ let cell = row_to_be_updated.querySelector('.' + fieldKey + '-cell');
44
+ cell.innerHTML = fieldValue;
45
+
46
+ // put value in hidden input
47
+ let hidden_input = row_to_be_updated.querySelector('.hidden-' + fieldKey);
48
+ hidden_input.value = fieldValue;
49
+ });
50
+
51
+ //Assign selected quantity to hidden inputs#
52
+ let hidden_quantity = row_to_be_updated.querySelector('.hidden-quantity');
53
+ let prev_quantity = hidden_quantity.value || 0;
54
+
55
+ quantity_val = Number(quantity_val) + Number(prev_quantity);
56
+ hidden_quantity.value = quantity_val;
57
+
58
+ //Show selected quantity on table
59
+ let quantity_cell = row_to_be_updated.querySelector('.quantity-cell');
60
+ quantity_cell.innerHTML = quantity_val;
61
+
62
+ if (new_row) {
63
+ //Add cloned row to table
64
+ return item_table.appendChild(row_to_be_updated);
65
+ }
66
+ } else {
67
+ return removeFromArrayInput(this);
68
+ }
69
+ }
70
+ }
71
+
72
+ $(document).ready(function () {
73
+ $('.item-list-container').on('click', '.js-remove-from-items-input', function (e) {
74
+ e.preventDefault();
75
+ ListModifier.removeFromArrayInput(this);
76
+ });
77
+
78
+ $('.js-add-to-items-input').click(ListModifier.addToItems);
79
+ });
@@ -0,0 +1,57 @@
1
+ //array input
2
+
3
+ @mixin reset-aa-button {
4
+ background: none;
5
+ border-radius: 0;
6
+ border: 0;
7
+ box-shadow: none;
8
+ line-height: 1;
9
+ padding: 0;
10
+ text-shadow: none;
11
+ vertical-align: middle;
12
+
13
+ &:not(.disabled) {
14
+ &:hover,
15
+ &:active,
16
+ &:focus {
17
+ background: none;
18
+ border: 0;
19
+ box-shadow: none;
20
+ }
21
+ }
22
+ }
23
+
24
+ .formtastic li.array.input {
25
+ .input-group--array {
26
+ width: 75%;
27
+ margin: 0 0 0 25%;
28
+ .input-group--array__item {
29
+ padding: 4px 0;
30
+ input {
31
+ display: inline-block;
32
+ margin: 0 10px 0 0;
33
+ &.text--strike {
34
+ text-decoration: line-through;
35
+ }
36
+ }
37
+ &.hidden {
38
+ display: none;
39
+ }
40
+ }
41
+
42
+ .array-action--add,
43
+ .array-action--remove {
44
+ @include reset-aa-button;
45
+ display: inline-block;
46
+ font-size: rem(15);
47
+ }
48
+
49
+ .array-action--add {
50
+ color: green;
51
+ }
52
+
53
+ .array-action--remove {
54
+ color: red;
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,41 @@
1
+ //item_with_quantity input
2
+ @import "./variables";
3
+
4
+ .list{
5
+ &.view{
6
+ width: auto;
7
+ }
8
+
9
+ th{
10
+ text-align: left;
11
+ }
12
+ &.error {
13
+ table tr {
14
+ border-color: $danger-color
15
+ }
16
+ }
17
+ .item-list-container {
18
+ width: 100%;
19
+ padding-left: 25%;
20
+ padding-right: 25%;
21
+ }
22
+ .hidden {
23
+ display: none;
24
+ }
25
+ .item-quantity-group:first-child {
26
+ margin-left: 0;
27
+ }
28
+ .item-quantity-group {
29
+ display: flex;
30
+ max-width: 50%;
31
+ margin-left: 25%;
32
+ justify-content: space-between;
33
+ .item-item, .select2 {
34
+ flex: 3;
35
+ }
36
+ .item-quantity {
37
+ flex: 1;
38
+ margin-left: 10px;
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,4 @@
1
+ $danger-color: #EA5E5B;
2
+ $warning-color: #F4D23E;
3
+ $success-color: #B9CF4E;
4
+ $optimal-color: #72BD48;
@@ -0,0 +1,22 @@
1
+ <div class="item_with_quantity view">
2
+
3
+ <table class="item-table">
4
+ <tbody>
5
+ <tr>
6
+ <% items[0].each do |key, value| %>
7
+ <th><%= key %></th>
8
+ <% end %>
9
+ </tr>
10
+ <% items.each do |item| %>
11
+ <tr class="item-row">
12
+ <% item.each do |key, value| %>
13
+
14
+ <td><%= value %></td>
15
+
16
+ <% end %>
17
+ </tr>
18
+ <% end %>
19
+ </tbody>
20
+ </table>
21
+
22
+ </div>
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "custom_inputs"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,53 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ app = File.expand_path("../app", __FILE__)
3
+
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ $LOAD_PATH.unshift(app) unless $LOAD_PATH.include?(app)
6
+ require "custom_inputs/version"
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = "custom_inputs"
10
+ spec.version = CustomInputs::VERSION
11
+ spec.authors = ["Hasan Ali Ayar"]
12
+ spec.email = ["hasan.a.ayar@gmail.com"]
13
+
14
+ spec.summary = "Custom form input fields for active admin + formtastic."
15
+ spec.description = `I've been using Active Admin for developing admin panels for my clients.
16
+ There are number of custom input components accumulated along the way.
17
+
18
+ I've decided to make a gem and share all these custom input components that I think may be very useful.
19
+
20
+ These inputs are optimised for Arctic Admin theme.
21
+ If you are using any other theme you can ignore the css files of this gem and implement your own css.
22
+
23
+ Note: Although these inputs are being actively used in a production app you should use them at your own risk.
24
+ `
25
+ spec.homepage = "https://github.com/hasan-aa/custom_inputs"
26
+ spec.license = "MIT"
27
+
28
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
29
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
30
+ if spec.respond_to?(:metadata)
31
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
32
+
33
+ spec.metadata["homepage_uri"] = spec.homepage
34
+ spec.metadata["source_code_uri"] = "https://github.com/hasan-aa/custom_inputs"
35
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
36
+ else
37
+ raise "RubyGems 2.0 or newer is required to protect against " \
38
+ "public gem pushes."
39
+ end
40
+
41
+ # Specify which files should be added to the gem when it is released.
42
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
43
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
44
+ `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^(test|spec|features|examples)/})}
45
+ end
46
+ spec.bindir = "exe"
47
+ spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f)}
48
+ spec.require_paths = ["lib", "app"]
49
+
50
+ spec.add_development_dependency "bundler", "~> 2"
51
+ spec.add_development_dependency "rake", "~> 13.0"
52
+ spec.add_development_dependency "rspec", "~> 3.0"
53
+ end
@@ -0,0 +1,9 @@
1
+ require "formtastic"
2
+
3
+ module CustomInputs
4
+ class Engine < ::Rails::Engine
5
+ config.autoload_paths += Dir["#{config.root}/lib"]
6
+ # ActiveSupport::Dependencies.explicitly_unloadable_constants << 'Formtastic::Inputs::ArrayInput'
7
+ end
8
+ end
9
+
@@ -0,0 +1,3 @@
1
+ module CustomInputs
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,22 @@
1
+ module Formtastic
2
+ module InputHelpers
3
+ def icon_tag(icon_name)
4
+ template.content_tag(:i, '', class: "fa fa-#{icon_name}")
5
+ end
6
+
7
+ def input_classes(options)
8
+ classes = 'custom-inputs--input-field'
9
+ classes += ' ' + options[:class] if options[:class]
10
+ classes
11
+ end
12
+
13
+ def ci_hidden_field(name, value = nil, options = {})
14
+ template.hidden_field_tag(name, value, options.merge(class: input_classes(options)))
15
+ end
16
+
17
+ def ci_text_field(name, value = nil, options = {})
18
+ template.text_field_tag(name, value, options.merge(class: input_classes(options)))
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ module Formtastic
2
+ module Inputs
3
+ class ArrayInput
4
+ include Base
5
+ include InputHelpers
6
+
7
+ def to_html
8
+ @undoable = options[:undoable]
9
+ input_wrapping do
10
+ inputs = []
11
+
12
+ values = @object.send(method)
13
+ if values
14
+ values.each_with_index do |v, x|
15
+ inputs << array_input_html(v)
16
+ end
17
+ end
18
+
19
+ label_html <<
20
+ template.content_tag(:div, class: 'input-group--array') do
21
+ inputs.join.html_safe <<
22
+ array_input_html('', false) <<
23
+ array_input_html('', true, true) # template for creating new array input field
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def array_input_html(value, remove = true, hidden = false)
31
+ icon_name = remove ? 'minus-circle' : 'plus-circle'
32
+ button_classes = remove ? 'array-action--remove js-remove-from-array-input' : 'array-action--add js-add-to-array-input'
33
+ button_classes += ' undoable' if @undoable
34
+
35
+ button = template.content_tag(:button,
36
+ icon_tag(icon_name),
37
+ class: button_classes,
38
+ type: 'button')
39
+
40
+ wrapper_classes = 'input-group--array__item'
41
+ wrapper_classes += ' hidden template' if hidden
42
+
43
+ template.content_tag(:div, class: wrapper_classes) do
44
+ ci_text_field("#{object_name}[#{method}][]", value, id: nil, disabled: hidden) << button
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,126 @@
1
+ module Formtastic
2
+ module Inputs
3
+ class ListInput
4
+ include Base
5
+ include InputHelpers
6
+
7
+ def has_quantity?
8
+ if @has_quantity.nil?
9
+ @has_quantity = options[:has_quantity]
10
+ @has_quantity = true if @has_quantity.nil?
11
+ end
12
+ @has_quantity
13
+ end
14
+
15
+ def to_html
16
+ fields = options[:fields] # [{'Header Text': :key}... ]
17
+ values = @object ? @object.send(method) : nil
18
+
19
+ input_wrapping do
20
+ label_html <<
21
+ template.content_tag(:div, class: 'item-quantity-group') do
22
+ tags = select_html
23
+ tags << quantity_html if has_quantity?
24
+ tags << buttons(false)
25
+ end <<
26
+ template.content_tag(:div, nil, class: 'item-list-container') do
27
+ # Table
28
+ template.content_tag(:table, class: 'item-table') do
29
+ # Headers
30
+ template.content_tag(:tr) do
31
+ headers = fields.map {|field|
32
+ header = field.first[0]
33
+ template.content_tag(:th, header.to_s)
34
+ }.join.html_safe
35
+ # Quantity header
36
+ headers << template.content_tag(:th, 'Quantity') if has_quantity?
37
+ # Button header
38
+ headers << template.content_tag(:th)
39
+ end <<
40
+ table_row << #hidden template row for js use.
41
+ if values
42
+ values.map {|value| table_row(false, value)}.join.html_safe
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def table_row(hidden = true, item = nil)
52
+ # Generating sample hidden row
53
+ unique_field = options[:unique_field] || 'id'
54
+ unique_value = item
55
+ quantity = nil
56
+ if item and has_quantity?
57
+ unique_value = item[unique_field.to_s]
58
+ quantity = item['quantity']
59
+ end
60
+
61
+ item_data = options[:collection_object].find_by("#{unique_field}": unique_value)
62
+ item_data ||= {}
63
+ fields = options[:fields]
64
+
65
+ css_classes = 'item-row'
66
+ css_classes += ' hidden' if hidden
67
+ template.content_tag(:tr, class: css_classes, id: "row_#{item_data[:id]}") do
68
+ cells = fields.map {|field|
69
+ key = field.first[1]
70
+ cell = template.content_tag(:td, "", class: "#{key}-cell", field: key.to_s) do
71
+ item_data[key].to_s
72
+ end
73
+ cell << hidden_field(key, true, item_data[key]) if has_quantity?
74
+ cell
75
+ }.join.html_safe
76
+
77
+ if has_quantity?
78
+ # Quantity cell
79
+ cells << template.content_tag(:td, class: 'quantity-cell') do
80
+ quantity
81
+ end
82
+ cells << hidden_field('quantity', false, quantity)
83
+ else
84
+ cells << hidden_field(unique_field, false, unique_value)
85
+ end
86
+
87
+ # Button cell
88
+ cells << template.content_tag(:td, width: '26px') do
89
+ buttons(true)
90
+ end
91
+ end
92
+ end
93
+
94
+ def select_html
95
+ template.select_tag('', template.options_from_collection_for_select(options[:collection_object], 'to_json', options[:to_s_method] || 'to_s'), input_html_options.merge({class: 'item-item', name: ''}))
96
+ end
97
+
98
+ def quantity_html
99
+ builder.number_field('', input_html_options.merge({class: 'item-quantity', value: 1, name: ''}))
100
+ end
101
+
102
+ def hidden_field(key, insert_field_attr = true, value = nil)
103
+ options = {class: "hidden-#{key}"}
104
+ options['field'] = key.to_s if insert_field_attr
105
+
106
+ if has_quantity?
107
+ # When with quantity build an array of objects with fields given in options.
108
+ # template.hidden_field_tag("#{object_name}[#{method}][][#{key}]", value = value, options = options)
109
+ ci_hidden_field("#{object_name}[#{method}][][#{key}]", value = value, options = options)
110
+ else
111
+ # When without quantity build an array of IDs
112
+ ci_hidden_field("#{object_name}[#{method}][]", value = value, options = options)
113
+ end
114
+ end
115
+
116
+ def buttons(remove = true)
117
+ if remove
118
+ button = template.content_tag(:button, icon_tag('minus-circle'), class: 'array-action--remove js-remove-from-items-input', type: 'button')
119
+ else
120
+ button = template.content_tag(:button, icon_tag('plus-circle'), class: 'array-action--add js-add-to-items-input', type: 'button')
121
+ end
122
+ button
123
+ end
124
+ end
125
+ end
126
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: custom_inputs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Hasan Ali Ayar
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-03-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: ''
56
+ email:
57
+ - hasan.a.ayar@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - CODE_OF_CONDUCT.md
66
+ - Gemfile
67
+ - Gemfile.lock
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - app/assets/javascripts/custom_inputs/array-input.js
72
+ - app/assets/javascripts/custom_inputs/custom_inputs.js
73
+ - app/assets/javascripts/custom_inputs/list-input.js
74
+ - app/assets/stylesheets/custom_inputs/array_input.scss
75
+ - app/assets/stylesheets/custom_inputs/list_input.scss
76
+ - app/assets/stylesheets/custom_inputs/variables.scss
77
+ - app/views/custom_inputs/_list.html.erb
78
+ - bin/console
79
+ - bin/setup
80
+ - custom_inputs.gemspec
81
+ - lib/custom_inputs.rb
82
+ - lib/custom_inputs/version.rb
83
+ - lib/formtastic/input_helpers.rb
84
+ - lib/formtastic/inputs/array_input.rb
85
+ - lib/formtastic/inputs/list_input.rb
86
+ homepage: https://github.com/hasan-aa/custom_inputs
87
+ licenses:
88
+ - MIT
89
+ metadata:
90
+ allowed_push_host: https://rubygems.org/
91
+ homepage_uri: https://github.com/hasan-aa/custom_inputs
92
+ source_code_uri: https://github.com/hasan-aa/custom_inputs
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ - app
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubygems_version: 3.0.3
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Custom form input fields for active admin + formtastic.
113
+ test_files: []