felt 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +17 -0
- data/LICENSE.txt +21 -0
- data/README.md +154 -0
- data/lib/felt/configuration.rb +11 -0
- data/lib/felt/version.rb +5 -0
- data/lib/felt.rb +22 -0
- data/lib/input_group/base.rb +192 -0
- data/lib/input_group/checkbox_field/checkbox_field.html.erb +15 -0
- data/lib/input_group/checkbox_field.rb +19 -0
- data/lib/input_group/email_field/email_field.html.erb +9 -0
- data/lib/input_group/email_field.rb +15 -0
- data/lib/input_group/password_field/password_field.html.erb +9 -0
- data/lib/input_group/password_field.rb +18 -0
- data/lib/input_group/text_field/text_field.html.erb +9 -0
- data/lib/input_group/text_field.rb +15 -0
- data/lib/label/label.html.erb +1 -0
- data/lib/label.rb +61 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a4d4be91443885d47f5e5aea19104465367551d4e0ef2dc35b110519e84274da
|
4
|
+
data.tar.gz: 2ba284067b6227021c04bb7f40593acb8a57ec2b762e72fad3f5c077a16a2a29
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5a3059fe1adc3287eebdd6812a91b80ce82b5f63fbfc9550cf36cd50bca630b0ca8724d1b4004b5e88f6ccef8c3e5d7098af682309c424c6f3d2f4e537c2bfbe
|
7
|
+
data.tar.gz: 8627ba5a61f4fd64d27c4982d95d0d4860dd6e2d9e74a940110f64fcba804e1c0cd0af426bb7d9daf34a1c8dfaf891f4b0d6ec140a143b544ecfcd6fea2509a2
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- `Felt::InputGroup::CheckboxField` component.
|
13
|
+
- `Felt::InputGroup::PasswordField` component.
|
14
|
+
- `Felt::InputGroup::EmailField` component.
|
15
|
+
- `Felt::InputGroup::TextField` component.
|
16
|
+
- This CHANGELOG file to hopefully serve as an evolving example of a
|
17
|
+
standardized open source project CHANGELOG.
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 Jakob Skjerning
|
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,154 @@
|
|
1
|
+
# Felt
|
2
|
+
|
3
|
+
A set of view components for rendering forms in Rails applications.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'felt'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install felt
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```
|
24
|
+
<% form_for(person) do |form| %>
|
25
|
+
<%= render(TextFieldComponent.new(form, :name)) %>
|
26
|
+
<% end %>
|
27
|
+
```
|
28
|
+
|
29
|
+
## Felt is unstyled by default
|
30
|
+
|
31
|
+
By default Felt only includes a bunch of markup and serverside behavior. If you want your input fields to not be unstyled, you have to configure things a bit.
|
32
|
+
|
33
|
+
To configure the classes you want to apply to your markup, use an initializer. For example, to style your inputs using Flowbite styles:
|
34
|
+
|
35
|
+
```
|
36
|
+
# config/initializers/felt.rb
|
37
|
+
Felt.classes = {
|
38
|
+
:input_group => {
|
39
|
+
:error => "mt-2 text-sm text-red-600 dark:text-red-500",
|
40
|
+
:hint => "mt-2 text-sm text-gray-500 dark:text-gray-400",
|
41
|
+
:label => "block mb-2 text-sm font-medium text-gray-900 dark:text-white",
|
42
|
+
:input => "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
43
|
+
}
|
44
|
+
}
|
45
|
+
```
|
46
|
+
|
47
|
+
If you're using Tailwind 3.0+ remember to add this initializer to your content settings so your classes aren't purged:
|
48
|
+
|
49
|
+
```
|
50
|
+
// tailwind.config.js
|
51
|
+
module.exports = {
|
52
|
+
content: [
|
53
|
+
'./config/initializers/felt.rb',
|
54
|
+
],
|
55
|
+
...
|
56
|
+
}
|
57
|
+
```
|
58
|
+
|
59
|
+
## Goals
|
60
|
+
|
61
|
+
* Form input fields must support the following properties:
|
62
|
+
* Placeholders
|
63
|
+
* Labels
|
64
|
+
* Disabled states
|
65
|
+
* Error states
|
66
|
+
* Hints/helper text
|
67
|
+
* Classes must be customizable so we can change the inputs we don't like. Ie if inputs should have square corners, not rounded.
|
68
|
+
* Markup must be customizable so we can change anything we don't like.
|
69
|
+
|
70
|
+
## Based on existing UI libraries
|
71
|
+
|
72
|
+
Default styles should come from an existing UI library. We cannot design and support a full UI library on top of building the components. Let's stand on the shoulders of giants.
|
73
|
+
|
74
|
+
### Possible choices
|
75
|
+
|
76
|
+
* [Apple Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/components/selection-and-input/text-fields)
|
77
|
+
* [Atlassian Design System](https://atlassian.design/components/form/examples)
|
78
|
+
* [Base Web](https://baseweb.design/components/form-control/)
|
79
|
+
* [Bootstrap](https://getbootstrap.com/docs/5.3/forms/overview/)
|
80
|
+
* [Flowbite](https://flowbite.com/docs/forms/input-field/)
|
81
|
+
* [GOV.UK Design System](https://design-system.service.gov.uk/components/text-input/)
|
82
|
+
* [Material](https://m3.material.io/components/text-fields/specs)
|
83
|
+
* [Polaris](https://polaris.shopify.com/components/form-layout)
|
84
|
+
* [Tailwind UI](https://tailwindui.com/)
|
85
|
+
|
86
|
+
## Anatomy of a form field
|
87
|
+
|
88
|
+
What does a form control or an input group consist of?
|
89
|
+
|
90
|
+
* Input: The actual input field. This can be a text field or a checkbox or a dropdown list or something entirely different.
|
91
|
+
|
92
|
+
* Hint: Use hint text for help that’s relevant to the majority of users, like how their information will be used, or where to find it. This is shown above the input.
|
93
|
+
|
94
|
+
* Help: A short text providing detailed help to the user. This is shown below the input.
|
95
|
+
|
96
|
+
## Architectural decisions
|
97
|
+
|
98
|
+
* Components don't use the `*Component` suffix. This makes naming and usage less verbose. And while it does stray against general ViewComponent recommendations (https://viewcomponent.org/adrs/0002-naming-conventions-for-view-components.html), the pros outweigh the cons in my opinion. Also, if it is good enough for Primer, it is good enough for us (https://viewcomponent.org/adrs/0002-naming-conventions-for-view-components.html).
|
99
|
+
|
100
|
+
* Where applicable component names should match those of their Rails counterparts. Ie a component that replaces `#text_field` should be named `Felt::TextField` etc.
|
101
|
+
|
102
|
+
* We use sidecar folders for all components.
|
103
|
+
|
104
|
+
## To Do
|
105
|
+
|
106
|
+
* [ ] Remove hardcoded classes from CheckboxField...
|
107
|
+
* [ ] Leading icons
|
108
|
+
* [ ] Trailing icons
|
109
|
+
* [ ] Error state for labels
|
110
|
+
* [ ] Error state for the entire input group
|
111
|
+
* [ ] Support all Rails form builder tags
|
112
|
+
* [ ] ARIA!
|
113
|
+
* [ ] Create a standard set of styles that can be distributed with the gem, so it looks proper - perhaps based on Flowbite?
|
114
|
+
* [ ] Should I18n scope be "felt", not "form"?
|
115
|
+
|
116
|
+
## Glossary and terms
|
117
|
+
|
118
|
+
| Felt | Base Web | Bootstrap | Flowbite | GOV UK | Tailwind UI |
|
119
|
+
|-------------|---------------|--------------|-------------|------------|-------------|
|
120
|
+
| Help | Caption | | Helper | | Help |
|
121
|
+
| Hint | | | | Hint | |
|
122
|
+
| Input | Input | | | | |
|
123
|
+
| Input Group | Form Control | Form control | Input field | Form group | Input group |
|
124
|
+
| Label | Label | | | Label | Label |
|
125
|
+
| Placeholder | Placeholder | | | | |
|
126
|
+
| | | Input group | Input group | | Add-on |
|
127
|
+
|
128
|
+
## Stacked vs horizontal input groups
|
129
|
+
|
130
|
+
- Bootstrap stacks fields by default, but also supports horizontal and inline forms (https://getbootstrap.com/docs/5.3/forms/layout/).
|
131
|
+
- Polaris stacks fields by default, but also supports horizontal groups of fields (https://polaris.shopify.com/components/form-layout).
|
132
|
+
|
133
|
+
## Similar projects and inspirations
|
134
|
+
|
135
|
+
* https://github.com/heartcombo/simple_form
|
136
|
+
* https://github.com/pantographe/view_component-form
|
137
|
+
|
138
|
+
## Development
|
139
|
+
|
140
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
141
|
+
|
142
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
143
|
+
|
144
|
+
## Contributing
|
145
|
+
|
146
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/koppen/felt. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/koppen/felt/blob/main/CODE_OF_CONDUCT.md).
|
147
|
+
|
148
|
+
## License
|
149
|
+
|
150
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
151
|
+
|
152
|
+
## Code of Conduct
|
153
|
+
|
154
|
+
Everyone interacting in the Felt project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/koppen/felt/blob/main/CODE_OF_CONDUCT.md).
|
data/lib/felt/version.rb
ADDED
data/lib/felt.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "felt/configuration"
|
4
|
+
require_relative "felt/version"
|
5
|
+
|
6
|
+
require_relative "input_group/checkbox_field"
|
7
|
+
require_relative "input_group/email_field"
|
8
|
+
require_relative "input_group/password_field"
|
9
|
+
require_relative "input_group/text_field"
|
10
|
+
require_relative "label"
|
11
|
+
|
12
|
+
module Felt
|
13
|
+
class Error < StandardError; end
|
14
|
+
|
15
|
+
def self.configuration
|
16
|
+
@configuration ||= Configuration.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.configure
|
20
|
+
yield(configuration)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "view_component"
|
4
|
+
|
5
|
+
module Felt
|
6
|
+
module InputGroup
|
7
|
+
# Renders a stacked input group element. This is the base class for all input
|
8
|
+
# groups and should not be instantiated directly.
|
9
|
+
class Base < ViewComponent::Base
|
10
|
+
attr_reader :attribute, :form, :input_options, :options
|
11
|
+
|
12
|
+
# Returns the classes to use for the root element of the input.
|
13
|
+
def classes
|
14
|
+
classes_from_configuration(:input_group, self.class.config_key)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the error messages to output in the input group. Returns [] if no
|
18
|
+
# errors.
|
19
|
+
#
|
20
|
+
# This returns the full error messages for the attribute, see
|
21
|
+
# ActiveModel::Errors#full_messages for more details.
|
22
|
+
def errors
|
23
|
+
return [] if form.object.nil?
|
24
|
+
|
25
|
+
form.object.errors.full_messages_for(attribute)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns true if the input group has errors.
|
29
|
+
def errors?
|
30
|
+
errors.any?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the classes to use for the help text.
|
34
|
+
def error_classes
|
35
|
+
classes_from_configuration(:error, self.class.config_key, state_key) ||
|
36
|
+
classes_from_configuration(:error, :default, state_key)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the help text for the input group. If no help is configured,
|
40
|
+
# returns nil.
|
41
|
+
#
|
42
|
+
# Help text is looked up in the following order:
|
43
|
+
#
|
44
|
+
# 1. The help argument passed to the component.
|
45
|
+
# 2. The `help` key in the `forms.<object_name>.<attribute>` translation.
|
46
|
+
def help
|
47
|
+
@help ||=
|
48
|
+
translate("help")
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns true if the input group has help configured
|
52
|
+
def help?
|
53
|
+
help.present?
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the classes to use for the help text.
|
57
|
+
def help_classes
|
58
|
+
classes_from_configuration(:help, self.class.config_key, state_key) ||
|
59
|
+
classes_from_configuration(:help, :default, state_key)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the hint for the input group. If no hint is configured, returns nil.
|
63
|
+
#
|
64
|
+
# Hints are looked up in the following order:
|
65
|
+
#
|
66
|
+
# 1. The hint argument passed to the component.
|
67
|
+
# 2. The `hint` key in the `forms.<object_name>.<attribute>` translation.
|
68
|
+
def hint
|
69
|
+
@hint ||=
|
70
|
+
translate("hint")
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns true if the input group has a hint configured
|
74
|
+
def hint?
|
75
|
+
hint.present?
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the classes to use for the hint text.
|
79
|
+
def hint_classes
|
80
|
+
classes_from_configuration(:hint, self.class.config_key, state_key) ||
|
81
|
+
classes_from_configuration(:hint, :default, state_key)
|
82
|
+
end
|
83
|
+
|
84
|
+
# - hint: The hint for the input group. If not provided, the hint will be
|
85
|
+
# looked up in the `forms.<object_name>.<attribute>` translation. See
|
86
|
+
# #hint for more details. To disable the hint, pass an empty string.
|
87
|
+
#
|
88
|
+
# - input_options: The options to pass directly to the input field.
|
89
|
+
#
|
90
|
+
# - label: The label text for the input group. If not provided, the text
|
91
|
+
# will be looked up in the `forms.<object_name>.<attribute>`
|
92
|
+
# translation. See #label for more details. To disable the label, pass
|
93
|
+
# an empty string.
|
94
|
+
#
|
95
|
+
# - placeholder: The placeholder for the input field. If not provided, the
|
96
|
+
# placeholder will be looked up in the `forms.<object_name>.<attribute>`
|
97
|
+
# translation. See #placeholder for more details. To disable the
|
98
|
+
# placeholder, pass an empty string.
|
99
|
+
#
|
100
|
+
# All remaining keyword arguments are passed to the wrapping div element
|
101
|
+
# of the input group. See ActionView::Helpers::TagHelper#content_tag for
|
102
|
+
# details.
|
103
|
+
def initialize(attribute:, form:, help: nil, hint: nil, input_options: {}, label: nil, placeholder: nil, **options)
|
104
|
+
@attribute = attribute
|
105
|
+
@form = form
|
106
|
+
@help = help
|
107
|
+
@hint = hint
|
108
|
+
@input_options = input_options
|
109
|
+
@label = label
|
110
|
+
@options = options
|
111
|
+
@placeholder = placeholder
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns the classes to use for the input field.
|
115
|
+
def input_classes
|
116
|
+
classes_from_configuration(:input, self.class.config_key, state_key) ||
|
117
|
+
classes_from_configuration(:input, :default, state_key)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns the classes to use for the label field.
|
121
|
+
def label_classes
|
122
|
+
classes_from_configuration(:label, self.class.config_key, state_key) ||
|
123
|
+
classes_from_configuration(:label, :default, state_key)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns the label for the input group. If no label is configured, returns
|
127
|
+
# nil.
|
128
|
+
#
|
129
|
+
# Labels are looked up in the following order:
|
130
|
+
#
|
131
|
+
# 1. The label argument passed to the component.
|
132
|
+
# 2. The `label` key in the `forms.<object_name>.<attribute>` translation.
|
133
|
+
# 3. The translation value found under `helpers.label.<modelname>.<attribute>`
|
134
|
+
# (like with ActionView::Helpers::FormBuilder#label).
|
135
|
+
def label
|
136
|
+
@label ||=
|
137
|
+
translate("label")
|
138
|
+
end
|
139
|
+
|
140
|
+
# Returns true if the input group has a label configured
|
141
|
+
def label?
|
142
|
+
label.present?
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns the placeholder for the input group. If no placeholder is
|
146
|
+
# configured, returns nil.
|
147
|
+
#
|
148
|
+
# Placeholders are looked up in the following order:
|
149
|
+
#
|
150
|
+
# 1. The placeholder argument passed to the component.
|
151
|
+
# 2. The `placeholder` key in the `forms.<object_name>.<attribute>`
|
152
|
+
# translation.
|
153
|
+
def placeholder
|
154
|
+
@placeholder ||=
|
155
|
+
translate("placeholder")
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns true if the input group has a placeholder configured
|
159
|
+
def placeholder?
|
160
|
+
placeholder.present?
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
# Returns classes configured at the given path under the classes key in
|
166
|
+
# the configuration.
|
167
|
+
def classes_from_configuration(*path)
|
168
|
+
Felt.configuration.classes.dig(*path)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns the key to use as the state part when looking up classes in
|
172
|
+
# configuration.
|
173
|
+
#
|
174
|
+
# Returns `:invalid` if the input group has errors, `:default` otherwise.
|
175
|
+
def state_key
|
176
|
+
if errors?
|
177
|
+
:invalid
|
178
|
+
else
|
179
|
+
:default
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def translate(key)
|
184
|
+
I18n.translate(key, default: nil, scope: translation_scope)
|
185
|
+
end
|
186
|
+
|
187
|
+
def translation_scope
|
188
|
+
[:forms, form.object_name, attribute].join(".")
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<%= content_tag(:div, :class => classes, **options) do %>
|
2
|
+
<% if hint? %><div class="<%= hint_classes %>"><%= hint %></div><% end %>
|
3
|
+
<div class="flex">
|
4
|
+
<div class="flex items-center h-5">
|
5
|
+
<%= form.check_box(attribute, :class => input_classes, :placeholder => placeholder, **input_options) %>
|
6
|
+
</div>
|
7
|
+
<div class="ml-2 text-sm">
|
8
|
+
<%= render(Felt::Label.new(:form => form, :attribute => attribute, :classes => label_classes, :text => label)) %>
|
9
|
+
<% if help? %><div class="<%= help_classes %>"><%= help %></div><% end %>
|
10
|
+
<% errors.each do |error| %>
|
11
|
+
<p class="<%= error_classes %>"><%= error %></p>
|
12
|
+
<% end %>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "input_group/base"
|
4
|
+
|
5
|
+
module Felt
|
6
|
+
module InputGroup
|
7
|
+
# Checkbox Field renders a checkbox input field with a label and a helper
|
8
|
+
# text.
|
9
|
+
#
|
10
|
+
# The hint text is optional and is rendered before the checkbox.
|
11
|
+
class CheckboxField < InputGroup::Base
|
12
|
+
class << self
|
13
|
+
def config_key
|
14
|
+
:checkbox_field
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%= content_tag(:div, :class => classes, **options) do %>
|
2
|
+
<%= render(Felt::Label.new(:form => form, :attribute => attribute, :classes => label_classes, :text => label)) %>
|
3
|
+
<% if hint? %><div class="<%= hint_classes %>"><%= hint %></div><% end %>
|
4
|
+
<%= form.email_field(attribute, :class => input_classes, :placeholder => placeholder, **input_options) %>
|
5
|
+
<% if help? %><div class="<%= help_classes %>"><%= help %></div><% end %>
|
6
|
+
<% errors.each do |error| %>
|
7
|
+
<p class="<%= error_classes %>"><%= error %></p>
|
8
|
+
<% end %>
|
9
|
+
<% end %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%= content_tag(:div, :class => classes, **options) do %>
|
2
|
+
<%= render(Felt::Label.new(:form => form, :attribute => attribute, :classes => label_classes, :text => label)) %>
|
3
|
+
<% if hint? %><div class="<%= hint_classes %>"><%= hint %></div><% end %>
|
4
|
+
<%= form.password_field(attribute, :class => input_classes, :placeholder => placeholder, **input_options) %>
|
5
|
+
<% if help? %><div class="<%= help_classes %>"><%= help %></div><% end %>
|
6
|
+
<% errors.each do |error| %>
|
7
|
+
<p class="<%= error_classes %>"><%= error %></p>
|
8
|
+
<% end %>
|
9
|
+
<% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "input_group/base"
|
4
|
+
|
5
|
+
module Felt
|
6
|
+
module InputGroup
|
7
|
+
# Renders an input group around a password field. For security reasons this
|
8
|
+
# field is blank by default; pass in a value via options if this is not
|
9
|
+
# desired.
|
10
|
+
class PasswordField < InputGroup::Base
|
11
|
+
class << self
|
12
|
+
def config_key
|
13
|
+
:password_field
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%= content_tag(:div, :class => classes, **options) do %>
|
2
|
+
<%= render(Felt::Label.new(:form => form, :attribute => attribute, :classes => label_classes, :text => label)) %>
|
3
|
+
<% if hint? %><div class="<%= hint_classes %>"><%= hint %></div><% end %>
|
4
|
+
<%= form.text_field(attribute, :class => input_classes, :placeholder => placeholder, **input_options) %>
|
5
|
+
<% if help? %><div class="<%= help_classes %>"><%= help %></div><% end %>
|
6
|
+
<% errors.each do |error| %>
|
7
|
+
<p class="<%= error_classes %>"><%= error %></p>
|
8
|
+
<% end %>
|
9
|
+
<% end %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= form.label(attribute, text, :class => classes) %>
|
data/lib/label.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "view_component"
|
4
|
+
|
5
|
+
module Felt
|
6
|
+
# Renders a label element for a form input.
|
7
|
+
class Label < ViewComponent::Base
|
8
|
+
attr_reader :attribute, :form, :options
|
9
|
+
|
10
|
+
# Returns the classes to use for the label element
|
11
|
+
def classes
|
12
|
+
@classes ||
|
13
|
+
Felt.configuration.classes.dig(:label, :default, :default)
|
14
|
+
end
|
15
|
+
|
16
|
+
# - classes: Classes to add to the label element.
|
17
|
+
#
|
18
|
+
# - text: The label text to show. If not provided, the text will be
|
19
|
+
# looked up in the `forms.<object_name>.<attribute>` translation. See
|
20
|
+
# #label for more details. To disable the label, pass an empty string.
|
21
|
+
#
|
22
|
+
# All remaining keyword arguments are passed to the label element. See
|
23
|
+
# ActionView::Helpers::FormBuilder#label for details.
|
24
|
+
def initialize(attribute:, form:, classes: nil, text: nil, **options)
|
25
|
+
@attribute = attribute
|
26
|
+
@classes = classes
|
27
|
+
@form = form
|
28
|
+
@text = text
|
29
|
+
@options = options
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the text to render in the label. If no text is configured, returns
|
33
|
+
# nil.
|
34
|
+
#
|
35
|
+
# Label texts are looked up in the following order:
|
36
|
+
#
|
37
|
+
# 1. The text argument passed to the component.
|
38
|
+
# 2. The `label` key in the `forms.<object_name>.<attribute>` translation.
|
39
|
+
# 3. The translation value found under
|
40
|
+
# `helpers.label.<modelname>.<attribute>` (like with
|
41
|
+
# ActionView::Helpers::FormBuilder#label).
|
42
|
+
def text
|
43
|
+
@text ||= translate("label")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns true if the input group has a label text configured
|
47
|
+
def text?
|
48
|
+
text.present?
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def translate(key)
|
54
|
+
I18n.translate(key, default: nil, scope: translation_scope)
|
55
|
+
end
|
56
|
+
|
57
|
+
def translation_scope
|
58
|
+
[:forms, form.object_name, attribute].join(".")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: felt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jakob Skjerning
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-01-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: i18n
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: view_component
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- jakob@substancelab.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- CHANGELOG.md
|
49
|
+
- LICENSE.txt
|
50
|
+
- README.md
|
51
|
+
- lib/felt.rb
|
52
|
+
- lib/felt/configuration.rb
|
53
|
+
- lib/felt/version.rb
|
54
|
+
- lib/input_group/base.rb
|
55
|
+
- lib/input_group/checkbox_field.rb
|
56
|
+
- lib/input_group/checkbox_field/checkbox_field.html.erb
|
57
|
+
- lib/input_group/email_field.rb
|
58
|
+
- lib/input_group/email_field/email_field.html.erb
|
59
|
+
- lib/input_group/password_field.rb
|
60
|
+
- lib/input_group/password_field/password_field.html.erb
|
61
|
+
- lib/input_group/text_field.rb
|
62
|
+
- lib/input_group/text_field/text_field.html.erb
|
63
|
+
- lib/label.rb
|
64
|
+
- lib/label/label.html.erb
|
65
|
+
homepage: https://github.com/substancelab/felt
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata:
|
69
|
+
allowed_push_host: https://rubygems.org
|
70
|
+
homepage_uri: https://github.com/substancelab/felt
|
71
|
+
source_code_uri: https://github.com/substancelab/felt
|
72
|
+
changelog_uri: https://github.com/substancelab/felt/blob/main/CHANGELOG.md
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 2.6.0
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubygems_version: 3.2.22
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: View Components for building forms in Rails applications
|
92
|
+
test_files: []
|