floating_labels_rails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.idea/.gitignore +10 -0
- data/.idea/floating_labels_rails.iml +95 -0
- data/.idea/modules.xml +8 -0
- data/.idea/vcs.xml +6 -0
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +114 -0
- data/Rakefile +12 -0
- data/WARP.md +38 -0
- data/app/javascript/controllers/floating_label_controller.js +26 -0
- data/app/javascript/controllers/password_toggle_controller.js +36 -0
- data/floating_labels_rails.gemspec +38 -0
- data/lib/floating_labels_rails/engine.rb +20 -0
- data/lib/floating_labels_rails/form_builder.rb +273 -0
- data/lib/floating_labels_rails/version.rb +5 -0
- data/lib/floating_labels_rails.rb +9 -0
- data/lib/generators/floating_labels_rails/install/install_generator.rb +23 -0
- data/lib/generators/floating_labels_rails/install/templates/README +16 -0
- data/lib/generators/floating_labels_rails/install/templates/controllers/floating_label_controller.js +26 -0
- data/lib/generators/floating_labels_rails/install/templates/controllers/password_toggle_controller.js +32 -0
- data/sig/floating_labels_rails.rbs +4 -0
- metadata +108 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0a90add58c0985a975f02b974c1bf7972fecd8d98bf8adc7d611a8d25b2fc68a
|
|
4
|
+
data.tar.gz: 19aae7b876291c9ab10b104fd5343c3cca20d8e152d09437ea901c780cc7d420
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 14ea825fdc9aab047e0b2fd22e486582c69cedd2c8bd4d2f363c31d89ecfaabecd3215276b04b573d5034dc0600bb6fcc7977ad1ab2f96fe79c11262211602bf
|
|
7
|
+
data.tar.gz: d8e28fcb2a0b4d85fb753c7505f20e80d94b09ea546177d25aa985d485925a6a51f8b0ca3bd49356df4e79df9c186666075e617e93dfe069d8d14a0b9be79a06
|
data/.idea/.gitignore
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="RUBY_MODULE" version="4">
|
|
3
|
+
<component name="ModuleRunConfigurationManager">
|
|
4
|
+
<shared />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="NewModuleRootManager">
|
|
7
|
+
<content url="file://$MODULE_DIR$">
|
|
8
|
+
<sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
|
|
9
|
+
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
|
|
10
|
+
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
|
11
|
+
</content>
|
|
12
|
+
<orderEntry type="jdk" jdkName="rbenv: 3.4.5" jdkType="RUBY_SDK" />
|
|
13
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
14
|
+
<orderEntry type="library" scope="PROVIDED" name="action_text-trix (v2.1.15, rbenv: 3.4.5) [gem]" level="application" />
|
|
15
|
+
<orderEntry type="library" scope="PROVIDED" name="actioncable (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
16
|
+
<orderEntry type="library" scope="PROVIDED" name="actionmailbox (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
17
|
+
<orderEntry type="library" scope="PROVIDED" name="actionmailer (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
18
|
+
<orderEntry type="library" scope="PROVIDED" name="actionpack (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
19
|
+
<orderEntry type="library" scope="PROVIDED" name="actiontext (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
20
|
+
<orderEntry type="library" scope="PROVIDED" name="actionview (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
21
|
+
<orderEntry type="library" scope="PROVIDED" name="activejob (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
22
|
+
<orderEntry type="library" scope="PROVIDED" name="activemodel (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
23
|
+
<orderEntry type="library" scope="PROVIDED" name="activerecord (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
24
|
+
<orderEntry type="library" scope="PROVIDED" name="activestorage (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
25
|
+
<orderEntry type="library" scope="PROVIDED" name="activesupport (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
26
|
+
<orderEntry type="library" scope="PROVIDED" name="ast (v2.4.3, rbenv: 3.4.5) [gem]" level="application" />
|
|
27
|
+
<orderEntry type="library" scope="PROVIDED" name="base64 (v0.3.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
28
|
+
<orderEntry type="library" scope="PROVIDED" name="bigdecimal (v3.3.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
29
|
+
<orderEntry type="library" scope="PROVIDED" name="builder (v3.3.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
30
|
+
<orderEntry type="library" scope="PROVIDED" name="bundler (v4.0.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
31
|
+
<orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.3.6, rbenv: 3.4.5) [gem]" level="application" />
|
|
32
|
+
<orderEntry type="library" scope="PROVIDED" name="connection_pool (v3.0.2, rbenv: 3.4.5) [gem]" level="application" />
|
|
33
|
+
<orderEntry type="library" scope="PROVIDED" name="crass (v1.0.6, rbenv: 3.4.5) [gem]" level="application" />
|
|
34
|
+
<orderEntry type="library" scope="PROVIDED" name="date (v3.5.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
35
|
+
<orderEntry type="library" scope="PROVIDED" name="drb (v2.2.3, rbenv: 3.4.5) [gem]" level="application" />
|
|
36
|
+
<orderEntry type="library" scope="PROVIDED" name="erb (v6.0.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
37
|
+
<orderEntry type="library" scope="PROVIDED" name="erubi (v1.13.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
38
|
+
<orderEntry type="library" scope="PROVIDED" name="globalid (v1.3.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
39
|
+
<orderEntry type="library" scope="PROVIDED" name="i18n (v1.14.7, rbenv: 3.4.5) [gem]" level="application" />
|
|
40
|
+
<orderEntry type="library" scope="PROVIDED" name="io-console (v0.8.2, rbenv: 3.4.5) [gem]" level="application" />
|
|
41
|
+
<orderEntry type="library" scope="PROVIDED" name="irb (v1.15.3, rbenv: 3.4.5) [gem]" level="application" />
|
|
42
|
+
<orderEntry type="library" scope="PROVIDED" name="json (v2.18.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
43
|
+
<orderEntry type="library" scope="PROVIDED" name="language_server-protocol (v3.17.0.5, rbenv: 3.4.5) [gem]" level="application" />
|
|
44
|
+
<orderEntry type="library" scope="PROVIDED" name="lint_roller (v1.1.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
45
|
+
<orderEntry type="library" scope="PROVIDED" name="logger (v1.7.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
46
|
+
<orderEntry type="library" scope="PROVIDED" name="loofah (v2.24.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
47
|
+
<orderEntry type="library" scope="PROVIDED" name="mail (v2.9.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
48
|
+
<orderEntry type="library" scope="PROVIDED" name="marcel (v1.1.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
49
|
+
<orderEntry type="library" scope="PROVIDED" name="mini_mime (v1.1.5, rbenv: 3.4.5) [gem]" level="application" />
|
|
50
|
+
<orderEntry type="library" scope="PROVIDED" name="minitest (v5.27.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
51
|
+
<orderEntry type="library" scope="PROVIDED" name="net-imap (v0.5.12, rbenv: 3.4.5) [gem]" level="application" />
|
|
52
|
+
<orderEntry type="library" scope="PROVIDED" name="net-pop (v0.1.2, rbenv: 3.4.5) [gem]" level="application" />
|
|
53
|
+
<orderEntry type="library" scope="PROVIDED" name="net-protocol (v0.2.2, rbenv: 3.4.5) [gem]" level="application" />
|
|
54
|
+
<orderEntry type="library" scope="PROVIDED" name="net-smtp (v0.5.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
55
|
+
<orderEntry type="library" scope="PROVIDED" name="nio4r (v2.7.5, rbenv: 3.4.5) [gem]" level="application" />
|
|
56
|
+
<orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.18.10, rbenv: 3.4.5) [gem]" level="application" />
|
|
57
|
+
<orderEntry type="library" scope="PROVIDED" name="parallel (v1.27.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
58
|
+
<orderEntry type="library" scope="PROVIDED" name="parser (v3.3.10.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
59
|
+
<orderEntry type="library" scope="PROVIDED" name="pp (v0.6.3, rbenv: 3.4.5) [gem]" level="application" />
|
|
60
|
+
<orderEntry type="library" scope="PROVIDED" name="prettyprint (v0.2.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
61
|
+
<orderEntry type="library" scope="PROVIDED" name="prism (v1.6.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
62
|
+
<orderEntry type="library" scope="PROVIDED" name="psych (v5.3.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
63
|
+
<orderEntry type="library" scope="PROVIDED" name="racc (v1.8.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
64
|
+
<orderEntry type="library" scope="PROVIDED" name="rack (v3.2.4, rbenv: 3.4.5) [gem]" level="application" />
|
|
65
|
+
<orderEntry type="library" scope="PROVIDED" name="rack-session (v2.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
66
|
+
<orderEntry type="library" scope="PROVIDED" name="rack-test (v2.2.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
67
|
+
<orderEntry type="library" scope="PROVIDED" name="rackup (v2.3.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
68
|
+
<orderEntry type="library" scope="PROVIDED" name="rails (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
69
|
+
<orderEntry type="library" scope="PROVIDED" name="rails-dom-testing (v2.3.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
70
|
+
<orderEntry type="library" scope="PROVIDED" name="rails-html-sanitizer (v1.6.2, rbenv: 3.4.5) [gem]" level="application" />
|
|
71
|
+
<orderEntry type="library" scope="PROVIDED" name="railties (v8.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
72
|
+
<orderEntry type="library" scope="PROVIDED" name="rainbow (v3.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
73
|
+
<orderEntry type="library" scope="PROVIDED" name="rake (v13.3.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
74
|
+
<orderEntry type="library" scope="PROVIDED" name="rdoc (v6.17.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
75
|
+
<orderEntry type="library" scope="PROVIDED" name="regexp_parser (v2.11.3, rbenv: 3.4.5) [gem]" level="application" />
|
|
76
|
+
<orderEntry type="library" scope="PROVIDED" name="reline (v0.6.3, rbenv: 3.4.5) [gem]" level="application" />
|
|
77
|
+
<orderEntry type="library" scope="PROVIDED" name="rubocop (v1.81.7, rbenv: 3.4.5) [gem]" level="application" />
|
|
78
|
+
<orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.48.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
79
|
+
<orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.13.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
80
|
+
<orderEntry type="library" scope="PROVIDED" name="securerandom (v0.4.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
81
|
+
<orderEntry type="library" scope="PROVIDED" name="stimulus-rails (v1.3.4, rbenv: 3.4.5) [gem]" level="application" />
|
|
82
|
+
<orderEntry type="library" scope="PROVIDED" name="stringio (v3.1.9, rbenv: 3.4.5) [gem]" level="application" />
|
|
83
|
+
<orderEntry type="library" scope="PROVIDED" name="thor (v1.4.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
84
|
+
<orderEntry type="library" scope="PROVIDED" name="timeout (v0.5.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
85
|
+
<orderEntry type="library" scope="PROVIDED" name="tsort (v0.2.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
86
|
+
<orderEntry type="library" scope="PROVIDED" name="tzinfo (v2.0.6, rbenv: 3.4.5) [gem]" level="application" />
|
|
87
|
+
<orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v3.2.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
88
|
+
<orderEntry type="library" scope="PROVIDED" name="unicode-emoji (v4.1.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
89
|
+
<orderEntry type="library" scope="PROVIDED" name="uri (v1.1.1, rbenv: 3.4.5) [gem]" level="application" />
|
|
90
|
+
<orderEntry type="library" scope="PROVIDED" name="useragent (v0.16.11, rbenv: 3.4.5) [gem]" level="application" />
|
|
91
|
+
<orderEntry type="library" scope="PROVIDED" name="websocket-driver (v0.8.0, rbenv: 3.4.5) [gem]" level="application" />
|
|
92
|
+
<orderEntry type="library" scope="PROVIDED" name="websocket-extensions (v0.1.5, rbenv: 3.4.5) [gem]" level="application" />
|
|
93
|
+
<orderEntry type="library" scope="PROVIDED" name="zeitwerk (v2.7.3, rbenv: 3.4.5) [gem]" level="application" />
|
|
94
|
+
</component>
|
|
95
|
+
</module>
|
data/.idea/modules.xml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/floating_labels_rails.iml" filepath="$PROJECT_DIR$/.idea/floating_labels_rails.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
data/.idea/vcs.xml
ADDED
data/.rubocop.yml
ADDED
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 virgostyx
|
|
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,114 @@
|
|
|
1
|
+
# FloatingLabelsRails
|
|
2
|
+
|
|
3
|
+
Floating label form fields pour Rails avec Tailwind CSS et Stimulus.
|
|
4
|
+
|
|
5
|
+
Un gem Rails qui fournit des champs de formulaire avec labels flottants (style Bootstrap) en utilisant Tailwind CSS et Stimulus, sans aucune dépendance à Bootstrap.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Ajoutez cette ligne au Gemfile de votre application :
|
|
10
|
+
```ruby
|
|
11
|
+
gem 'floating_labels_rails'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Puis exécutez :
|
|
15
|
+
```bash
|
|
16
|
+
bundle install
|
|
17
|
+
rails generate floating_labels_rails:install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Prérequis
|
|
21
|
+
|
|
22
|
+
- Rails >= 6.0
|
|
23
|
+
- Tailwind CSS configuré dans votre application
|
|
24
|
+
- Stimulus JS
|
|
25
|
+
|
|
26
|
+
## Utilisation
|
|
27
|
+
|
|
28
|
+
### Utilisation basique
|
|
29
|
+
```erb
|
|
30
|
+
<%= form_with model: @user, builder: FloatingLabelsRails::FormBuilder do |f| %>
|
|
31
|
+
<%= f.floating_text_field :first_name, label: "Prénom", required: true %>
|
|
32
|
+
<%= f.floating_email_field :email, label: "Email", required: true %>
|
|
33
|
+
<%= f.floating_password_field :password, label: "Mot de passe" %>
|
|
34
|
+
<%= f.submit %>
|
|
35
|
+
<% end %>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Configuration globale
|
|
39
|
+
|
|
40
|
+
Pour utiliser le Form Builder par défaut dans toute votre application, ajoutez dans `config/application.rb` :
|
|
41
|
+
```ruby
|
|
42
|
+
config.action_view.default_form_builder = FloatingLabelsRails::FormBuilder
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Après cela, tous vos formulaires utiliseront automatiquement le builder :
|
|
46
|
+
```erb
|
|
47
|
+
<%= form_with model: @user do |f| %>
|
|
48
|
+
<%= f.floating_text_field :name %>
|
|
49
|
+
<% end %>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Types de champs disponibles
|
|
53
|
+
```erb
|
|
54
|
+
# Champs texte
|
|
55
|
+
<%= f.floating_text_field :name %>
|
|
56
|
+
<%= f.floating_email_field :email %>
|
|
57
|
+
<%= f.floating_password_field :password %> # Avec toggle show/hide
|
|
58
|
+
<%= f.floating_telephone_field :phone %>
|
|
59
|
+
<%= f.floating_url_field :website %>
|
|
60
|
+
<%= f.floating_number_field :age %>
|
|
61
|
+
<%= f.floating_date_field :birth_date %>
|
|
62
|
+
|
|
63
|
+
# Select
|
|
64
|
+
<%= f.floating_select :country, [["France", "FR"], ["Belgique", "BE"]] %>
|
|
65
|
+
<%= f.floating_collection_select :category_id, Category.all, :id, :name %>
|
|
66
|
+
|
|
67
|
+
# Textarea
|
|
68
|
+
<%= f.floating_text_area :bio, rows: 5 %>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Options
|
|
72
|
+
|
|
73
|
+
Tous les champs acceptent ces options :
|
|
74
|
+
|
|
75
|
+
- `label` : Texte du label (par défaut : humanize de l'attribut)
|
|
76
|
+
- `required` : Affiche un astérisque rouge (par défaut : false)
|
|
77
|
+
- `class` : Classes CSS additionnelles
|
|
78
|
+
```erb
|
|
79
|
+
<%= f.floating_text_field :name,
|
|
80
|
+
label: "Nom complet",
|
|
81
|
+
required: true,
|
|
82
|
+
class: "my-custom-class" %>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Gestion des erreurs
|
|
86
|
+
|
|
87
|
+
Les erreurs de validation sont automatiquement affichées :
|
|
88
|
+
- Bordure rouge sur le champ
|
|
89
|
+
- Label en rouge
|
|
90
|
+
- Message d'erreur sous le champ
|
|
91
|
+
|
|
92
|
+
## Personnalisation
|
|
93
|
+
|
|
94
|
+
Vous pouvez personnaliser les couleurs Tailwind en modifiant les classes dans le Form Builder.
|
|
95
|
+
|
|
96
|
+
Par défaut, le gem utilise :
|
|
97
|
+
- `blue-500` pour le focus
|
|
98
|
+
- `red-500` pour les erreurs
|
|
99
|
+
- `gray-300` pour les bordures
|
|
100
|
+
|
|
101
|
+
## Développement
|
|
102
|
+
|
|
103
|
+
Après avoir cloné le repo :
|
|
104
|
+
```bash
|
|
105
|
+
bundle install
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Contribution
|
|
109
|
+
|
|
110
|
+
Les contributions sont les bienvenues ! N'hésitez pas à ouvrir une issue ou une pull request.
|
|
111
|
+
|
|
112
|
+
## Licence
|
|
113
|
+
|
|
114
|
+
Le gem est disponible en open source sous les termes de la [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/WARP.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# WARP.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to WARP (warp.dev) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
# Project Overview
|
|
6
|
+
This repository is a Ruby gem (`floating_labels_rails`) that appears to be a Rails engine or integration library. It follows the standard bundler gem structure.
|
|
7
|
+
|
|
8
|
+
# Development
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
- **Install dependencies**: `bin/setup`
|
|
12
|
+
- **Interactive Console**: `bin/console` (loads the gem environment)
|
|
13
|
+
|
|
14
|
+
## Testing
|
|
15
|
+
The project uses `minitest`.
|
|
16
|
+
- **Run all tests**: `bundle exec rake test` (or just `bundle exec rake` as it's the default task)
|
|
17
|
+
- **Run a single test file**: `ruby -Itest test/path/to/test_file.rb`
|
|
18
|
+
- **Run a specific test method**: `ruby -Itest test/path/to/test_file.rb --name test_method_name`
|
|
19
|
+
|
|
20
|
+
## Linting
|
|
21
|
+
The project uses `rubocop`.
|
|
22
|
+
- **Run linter**: `bundle exec rubocop`
|
|
23
|
+
- **Autocorrect**: `bundle exec rubocop -A`
|
|
24
|
+
|
|
25
|
+
# Architecture
|
|
26
|
+
|
|
27
|
+
## Directory Structure
|
|
28
|
+
- `lib/`: Contains the source code.
|
|
29
|
+
- `floating_labels_rails.rb`: Main entry point for the gem.
|
|
30
|
+
- `floating_labels_rails/`: Submodules and classes.
|
|
31
|
+
- `test/`: Contains Minitest tests.
|
|
32
|
+
- `test_helper.rb`: Test configuration and setup.
|
|
33
|
+
- `bin/`: Executables for development (`setup`, `console`).
|
|
34
|
+
- `sig/`: RBS type signatures.
|
|
35
|
+
|
|
36
|
+
## Dependency Management
|
|
37
|
+
- **Gemspec**: Dependencies are defined in `floating_labels_rails.gemspec`.
|
|
38
|
+
- **Gemfile**: Refers to the gemspec for dependencies (`gemspec` directive). Add development-only dependencies (like `rubocop`) here or in the gemspec with `add_development_dependency`.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["input", "label"]
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
this.checkValue()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
checkValue() {
|
|
11
|
+
if (this.inputTarget.value.trim() !== "" ||
|
|
12
|
+
this.inputTarget === document.activeElement) {
|
|
13
|
+
this.labelTarget.classList.add("floating")
|
|
14
|
+
} else {
|
|
15
|
+
this.labelTarget.classList.remove("floating")
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
focus() {
|
|
20
|
+
this.labelTarget.classList.add("floating")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
blur() {
|
|
24
|
+
this.checkValue()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["input", "icon"]
|
|
5
|
+
|
|
6
|
+
toggle() {
|
|
7
|
+
const type = this.inputTarget.type === "password" ? "text" : "password"
|
|
8
|
+
this.inputTarget.type = type
|
|
9
|
+
this.updateIcon(type)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
updateIcon(type) {
|
|
13
|
+
const button = this.iconTarget.closest('button')
|
|
14
|
+
|
|
15
|
+
if (type === "password") {
|
|
16
|
+
button.classList.remove('text-blue-600')
|
|
17
|
+
button.classList.add('text-gray-600')
|
|
18
|
+
|
|
19
|
+
this.iconTarget.innerHTML = `
|
|
20
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
21
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
22
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
|
23
|
+
</svg>
|
|
24
|
+
`
|
|
25
|
+
} else {
|
|
26
|
+
button.classList.remove('text-gray-600')
|
|
27
|
+
button.classList.add('text-blue-600')
|
|
28
|
+
|
|
29
|
+
this.iconTarget.innerHTML = `
|
|
30
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
31
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"/>
|
|
32
|
+
</svg>
|
|
33
|
+
`
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/floating_labels_rails/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "floating_labels_rails"
|
|
7
|
+
spec.version = FloatingLabelsRails::VERSION
|
|
8
|
+
spec.authors = ["Virgo STYX"]
|
|
9
|
+
spec.email = ["virgostyx@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Floating label form fields pour Rails avec Tailwind CSS"
|
|
12
|
+
spec.description = "Un gem Rails qui fournit des champs de formulaire avec labels flottants (style Bootstrap) en utilisant Tailwind CSS et Stimulus"
|
|
13
|
+
spec.homepage = "https://github.com/virgostyx/floating_labels_rails"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 2.7.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/virgostyx/floating_labels_rails"
|
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/virgostyx/floating_labels_rails/blob/main/CHANGELOG.md"
|
|
20
|
+
|
|
21
|
+
# Spécifier les fichiers du gem
|
|
22
|
+
spec.files = Dir.chdir(__dir__) do
|
|
23
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
24
|
+
(File.expand_path(f) == __FILE__) ||
|
|
25
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
spec.bindir = "exe"
|
|
30
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
31
|
+
spec.require_paths = ["lib"]
|
|
32
|
+
|
|
33
|
+
# Dépendances
|
|
34
|
+
spec.add_dependency "rails", ">= 8.0"
|
|
35
|
+
spec.add_dependency "stimulus-rails", ">= 1.0"
|
|
36
|
+
|
|
37
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
38
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
module FloatingLabelsRails
|
|
6
|
+
class Engine < ::Rails::Engine
|
|
7
|
+
isolate_namespace FloatingLabelsRails
|
|
8
|
+
|
|
9
|
+
initializer "floating_labels_rails.assets" do |app|
|
|
10
|
+
app.config.assets.precompile += %w[floating_labels_rails_manifest.js]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
initializer "floating_labels_rails.helpers" do
|
|
14
|
+
ActiveSupport.on_load(:action_view) do
|
|
15
|
+
require "floating_labels_rails/form_builder"
|
|
16
|
+
ActionView::Base.default_form_builder = FloatingLabelsRails::FormBuilder
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FloatingLabelsRails
|
|
4
|
+
class FormBuilder < ActionView::Helpers::FormBuilder
|
|
5
|
+
# Méthode générique pour les champs texte
|
|
6
|
+
def floating_field(attribute, field_type: :text_field, label: nil, required: false, **options)
|
|
7
|
+
label_text = label || attribute.to_s.humanize
|
|
8
|
+
|
|
9
|
+
input_classes = "block w-full px-3 pt-6 pb-2 text-gray-900 bg-transparent border border-gray-300 rounded-lg appearance-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent peer"
|
|
10
|
+
input_classes += " #{options.delete(:class)}" if options[:class]
|
|
11
|
+
|
|
12
|
+
label_classes = "absolute text-gray-500 duration-300 transform -translate-y-3 scale-75 top-4 left-3 z-10 origin-[0] peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-3 peer-focus:text-blue-600"
|
|
13
|
+
|
|
14
|
+
# Ajouter la classe d'erreur si nécessaire
|
|
15
|
+
if @object.errors[attribute].any?
|
|
16
|
+
input_classes += " border-red-500 focus:ring-red-500"
|
|
17
|
+
label_classes += " text-red-600"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@template.content_tag :div, class: "relative mb-6", data: { controller: "floating-label" } do
|
|
21
|
+
field = send(field_type, attribute,
|
|
22
|
+
options.merge(
|
|
23
|
+
data: {
|
|
24
|
+
floating_label_target: "input",
|
|
25
|
+
action: "focus->floating-label#focus blur->floating-label#blur input->floating-label#checkValue"
|
|
26
|
+
},
|
|
27
|
+
class: input_classes,
|
|
28
|
+
placeholder: " "
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
label_tag = @template.label_tag(
|
|
33
|
+
"#{@object_name}_#{attribute}",
|
|
34
|
+
"#{label_text}#{'<span class="text-red-500"> *</span>'.html_safe if required}".html_safe,
|
|
35
|
+
data: { floating_label_target: "label" },
|
|
36
|
+
class: label_classes
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
error_message = if @object.errors[attribute].any?
|
|
40
|
+
@template.content_tag(:p, @object.errors[attribute].first, class: "mt-1 text-sm text-red-600")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
field + label_tag + (error_message || "").html_safe
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Méthodes spécifiques pour chaque type de champ
|
|
48
|
+
def floating_text_field(attribute, **options)
|
|
49
|
+
floating_field(attribute, field_type: :text_field, **options)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def floating_email_field(attribute, **options)
|
|
53
|
+
floating_field(attribute, field_type: :email_field, **options)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def floating_password_field(attribute, **options)
|
|
57
|
+
label_text = options.delete(:label) || attribute.to_s.humanize
|
|
58
|
+
required = options.delete(:required) || false
|
|
59
|
+
|
|
60
|
+
input_classes = "block w-full px-3 pt-6 pb-2 pr-12 text-gray-900 bg-transparent border border-gray-300 rounded-lg appearance-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent peer"
|
|
61
|
+
input_classes += " #{options.delete(:class)}" if options[:class]
|
|
62
|
+
|
|
63
|
+
label_classes = "absolute text-gray-500 duration-300 transform -translate-y-3 scale-75 top-4 left-3 z-10 origin-[0] peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-3 peer-focus:text-blue-600"
|
|
64
|
+
|
|
65
|
+
if @object.errors[attribute].any?
|
|
66
|
+
input_classes += " border-red-500 focus:ring-red-500"
|
|
67
|
+
label_classes += " text-red-600"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
@template.content_tag :div, class: "relative mb-6", data: { controller: "floating-label password-toggle" } do
|
|
71
|
+
field = password_field(attribute,
|
|
72
|
+
options.merge(
|
|
73
|
+
data: {
|
|
74
|
+
floating_label_target: "input",
|
|
75
|
+
password_toggle_target: "input",
|
|
76
|
+
action: "focus->floating-label#focus blur->floating-label#blur input->floating-label#checkValue"
|
|
77
|
+
},
|
|
78
|
+
class: input_classes,
|
|
79
|
+
placeholder: " "
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
label_tag = @template.label_tag(
|
|
84
|
+
"#{@object_name}_#{attribute}",
|
|
85
|
+
"#{label_text}#{'<span class="text-red-500"> *</span>'.html_safe if required}".html_safe,
|
|
86
|
+
data: { floating_label_target: "label" },
|
|
87
|
+
class: label_classes
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
toggle_button = @template.content_tag :button,
|
|
91
|
+
type: "button",
|
|
92
|
+
class: "absolute inset-y-0 right-0 flex items-center pr-3 text-gray-600 hover:text-gray-800 focus:outline-none",
|
|
93
|
+
data: { action: "click->password-toggle#toggle", password_toggle_target: "icon" } do
|
|
94
|
+
'<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
95
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
96
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
|
97
|
+
</svg>'.html_safe
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
error_message = if @object.errors[attribute].any?
|
|
101
|
+
@template.content_tag(:p, @object.errors[attribute].first, class: "mt-1 text-sm text-red-600")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
field + label_tag + toggle_button + (error_message || "").html_safe
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def floating_number_field(attribute, **options)
|
|
109
|
+
floating_field(attribute, field_type: :number_field, **options)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def floating_telephone_field(attribute, **options)
|
|
113
|
+
floating_field(attribute, field_type: :telephone_field, **options)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def floating_url_field(attribute, **options)
|
|
117
|
+
floating_field(attribute, field_type: :url_field, **options)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def floating_date_field(attribute, **options)
|
|
121
|
+
floating_field(attribute, field_type: :date_field, **options)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def floating_datetime_field(attribute, **options)
|
|
125
|
+
floating_field(attribute, field_type: :datetime_field, **options)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Select avec floating label
|
|
129
|
+
def floating_select(attribute, choices = nil, select_options = {}, html_options = {})
|
|
130
|
+
label_text = html_options.delete(:label) || attribute.to_s.humanize
|
|
131
|
+
required = html_options.delete(:required) || false
|
|
132
|
+
include_blank = select_options[:include_blank] || "Sélectionner..."
|
|
133
|
+
|
|
134
|
+
select_classes = "block w-full px-3 pt-6 pb-2 text-gray-900 bg-white border border-gray-300 rounded-lg appearance-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent peer"
|
|
135
|
+
select_classes += " #{html_options.delete(:class)}" if html_options[:class]
|
|
136
|
+
|
|
137
|
+
label_classes = "absolute text-gray-500 duration-300 transform -translate-y-3 scale-75 top-4 left-3 z-10 origin-[0] peer-focus:scale-75 peer-focus:-translate-y-3 peer-focus:text-blue-600 floating"
|
|
138
|
+
|
|
139
|
+
if @object.errors[attribute].any?
|
|
140
|
+
select_classes += " border-red-500 focus:ring-red-500"
|
|
141
|
+
label_classes += " text-red-600"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
@template.content_tag :div, class: "relative mb-6", data: { controller: "floating-label" } do
|
|
145
|
+
select_field = select(
|
|
146
|
+
attribute,
|
|
147
|
+
choices,
|
|
148
|
+
select_options.merge(include_blank: include_blank),
|
|
149
|
+
html_options.merge(
|
|
150
|
+
data: {
|
|
151
|
+
floating_label_target: "input",
|
|
152
|
+
action: "focus->floating-label#focus blur->floating-label#blur change->floating-label#checkValue"
|
|
153
|
+
},
|
|
154
|
+
class: select_classes
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
label_tag = @template.label_tag(
|
|
159
|
+
"#{@object_name}_#{attribute}",
|
|
160
|
+
"#{label_text}#{'<span class="text-red-500"> *</span>'.html_safe if required}".html_safe,
|
|
161
|
+
data: { floating_label_target: "label" },
|
|
162
|
+
class: label_classes
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
dropdown_icon = @template.content_tag :div, class: "pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700" do
|
|
166
|
+
'<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/></svg>'.html_safe
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
error_message = if @object.errors[attribute].any?
|
|
170
|
+
@template.content_tag(:p, @object.errors[attribute].first, class: "mt-1 text-sm text-red-600")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
select_field + label_tag + dropdown_icon + (error_message || "").html_safe
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Collection select avec floating label
|
|
178
|
+
def floating_collection_select(attribute, collection, value_method, text_method, select_options = {}, html_options = {})
|
|
179
|
+
label_text = html_options.delete(:label) || attribute.to_s.humanize
|
|
180
|
+
required = html_options.delete(:required) || false
|
|
181
|
+
include_blank = select_options[:include_blank] || "Sélectionner..."
|
|
182
|
+
|
|
183
|
+
select_classes = "block w-full px-3 pt-6 pb-2 text-gray-900 bg-white border border-gray-300 rounded-lg appearance-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent peer"
|
|
184
|
+
select_classes += " #{html_options.delete(:class)}" if html_options[:class]
|
|
185
|
+
|
|
186
|
+
label_classes = "absolute text-gray-500 duration-300 transform -translate-y-3 scale-75 top-4 left-3 z-10 origin-[0] peer-focus:scale-75 peer-focus:-translate-y-3 peer-focus:text-blue-600 floating"
|
|
187
|
+
|
|
188
|
+
if @object.errors[attribute].any?
|
|
189
|
+
select_classes += " border-red-500 focus:ring-red-500"
|
|
190
|
+
label_classes += " text-red-600"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
@template.content_tag :div, class: "relative mb-6", data: { controller: "floating-label" } do
|
|
194
|
+
select_field = collection_select(
|
|
195
|
+
attribute,
|
|
196
|
+
collection,
|
|
197
|
+
value_method,
|
|
198
|
+
text_method,
|
|
199
|
+
select_options.merge(include_blank: include_blank),
|
|
200
|
+
html_options.merge(
|
|
201
|
+
data: {
|
|
202
|
+
floating_label_target: "input",
|
|
203
|
+
action: "focus->floating-label#focus blur->floating-label#blur change->floating-label#checkValue"
|
|
204
|
+
},
|
|
205
|
+
class: select_classes
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
label_tag = @template.label_tag(
|
|
210
|
+
"#{@object_name}_#{attribute}",
|
|
211
|
+
"#{label_text}#{'<span class="text-red-500"> *</span>'.html_safe if required}".html_safe,
|
|
212
|
+
data: { floating_label_target: "label" },
|
|
213
|
+
class: label_classes
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
dropdown_icon = @template.content_tag :div, class: "pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700" do
|
|
217
|
+
'<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/></svg>'.html_safe
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
error_message = if @object.errors[attribute].any?
|
|
221
|
+
@template.content_tag(:p, @object.errors[attribute].first, class: "mt-1 text-sm text-red-600")
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
select_field + label_tag + dropdown_icon + (error_message || "").html_safe
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Textarea avec floating label
|
|
229
|
+
def floating_text_area(attribute, **options)
|
|
230
|
+
label_text = options.delete(:label) || attribute.to_s.humanize
|
|
231
|
+
required = options.delete(:required) || false
|
|
232
|
+
rows = options.delete(:rows) || 4
|
|
233
|
+
|
|
234
|
+
textarea_classes = "block w-full px-3 pt-6 pb-2 text-gray-900 bg-transparent border border-gray-300 rounded-lg appearance-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent peer resize-none"
|
|
235
|
+
textarea_classes += " #{options.delete(:class)}" if options[:class]
|
|
236
|
+
|
|
237
|
+
label_classes = "absolute text-gray-500 duration-300 transform -translate-y-3 scale-75 top-4 left-3 z-10 origin-[0] peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-3 peer-focus:text-blue-600"
|
|
238
|
+
|
|
239
|
+
if @object.errors[attribute].any?
|
|
240
|
+
textarea_classes += " border-red-500 focus:ring-red-500"
|
|
241
|
+
label_classes += " text-red-600"
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
@template.content_tag :div, class: "relative mb-6", data: { controller: "floating-label" } do
|
|
245
|
+
textarea = text_area(
|
|
246
|
+
attribute,
|
|
247
|
+
options.merge(
|
|
248
|
+
data: {
|
|
249
|
+
floating_label_target: "input",
|
|
250
|
+
action: "focus->floating-label#focus blur->floating-label#blur input->floating-label#checkValue"
|
|
251
|
+
},
|
|
252
|
+
class: textarea_classes,
|
|
253
|
+
rows: rows,
|
|
254
|
+
placeholder: " "
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
label_tag = @template.label_tag(
|
|
259
|
+
"#{@object_name}_#{attribute}",
|
|
260
|
+
"#{label_text}#{'<span class="text-red-500"> *</span>'.html_safe if required}".html_safe,
|
|
261
|
+
data: { floating_label_target: "label" },
|
|
262
|
+
class: label_classes
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
error_message = if @object.errors[attribute].any?
|
|
266
|
+
@template.content_tag(:p, @object.errors[attribute].first, class: "mt-1 text-sm text-red-600")
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
textarea + label_tag + (error_message || "").html_safe
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FloatingLabelsRails
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Installe les contrôleurs Stimulus pour FloatingLabelsRails"
|
|
9
|
+
|
|
10
|
+
def copy_stimulus_controllers
|
|
11
|
+
say "Installation des contrôleurs Stimulus...", :green
|
|
12
|
+
|
|
13
|
+
directory "controllers", "app/javascript/controllers"
|
|
14
|
+
|
|
15
|
+
say "✓ Contrôleurs Stimulus installés", :green
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def show_readme
|
|
19
|
+
readme "README" if behavior == :invoke
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
===============================================================================
|
|
2
|
+
FloatingLabelsRails a été installé avec succès ! 🎉
|
|
3
|
+
Les contrôleurs Stimulus ont été copiés dans :
|
|
4
|
+
app/javascript/controllers/
|
|
5
|
+
Pour utiliser le Form Builder dans vos formulaires :
|
|
6
|
+
<%= form_with model: @user, builder: FloatingLabelsRails::FormBuilder do |f| %>
|
|
7
|
+
<%= f.floating_text_field :name, label: "Nom", required: true %>
|
|
8
|
+
<%= f.floating_email_field :email %>
|
|
9
|
+
<%= f.floating_password_field :password %>
|
|
10
|
+
<%= f.submit %>
|
|
11
|
+
<% end %>
|
|
12
|
+
Pour utiliser le Form Builder par défaut dans toute l'application,
|
|
13
|
+
ajoutez dans config/application.rb :
|
|
14
|
+
config.action_view.default_form_builder = FloatingLabelsRails::FormBuilder
|
|
15
|
+
Documentation complète : https://github.com/votreusername/floating_labels_rails
|
|
16
|
+
===============================================================================
|
data/lib/generators/floating_labels_rails/install/templates/controllers/floating_label_controller.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["input", "label"]
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
this.checkValue()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
checkValue() {
|
|
11
|
+
if (this.inputTarget.value.trim() !== "" ||
|
|
12
|
+
this.inputTarget === document.activeElement) {
|
|
13
|
+
this.labelTarget.classList.add("floating")
|
|
14
|
+
} else {
|
|
15
|
+
this.labelTarget.classList.remove("floating")
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
focus() {
|
|
20
|
+
this.labelTarget.classList.add("floating")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
blur() {
|
|
24
|
+
this.checkValue()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["input", "icon"]
|
|
5
|
+
|
|
6
|
+
toggle() {
|
|
7
|
+
const type = this.inputTarget.type === "password" ? "text" : "password"
|
|
8
|
+
this.inputTarget.type = type
|
|
9
|
+
|
|
10
|
+
// Mettre à jour l'icône
|
|
11
|
+
this.updateIcon(type)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
updateIcon(type) {
|
|
15
|
+
if (type === "password") {
|
|
16
|
+
// Icône œil fermé (mot de passe caché)
|
|
17
|
+
this.iconTarget.innerHTML = `
|
|
18
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
19
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
20
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
|
21
|
+
</svg>
|
|
22
|
+
`
|
|
23
|
+
} else {
|
|
24
|
+
// Icône œil barré (mot de passe visible)
|
|
25
|
+
this.iconTarget.innerHTML = `
|
|
26
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
27
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"/>
|
|
28
|
+
</svg>
|
|
29
|
+
`
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
metadata
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: floating_labels_rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Virgo STYX
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '8.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '8.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: stimulus-rails
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rspec
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.0'
|
|
54
|
+
description: Un gem Rails qui fournit des champs de formulaire avec labels flottants
|
|
55
|
+
(style Bootstrap) en utilisant Tailwind CSS et Stimulus
|
|
56
|
+
email:
|
|
57
|
+
- virgostyx@gmail.com
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- ".idea/.gitignore"
|
|
63
|
+
- ".idea/floating_labels_rails.iml"
|
|
64
|
+
- ".idea/modules.xml"
|
|
65
|
+
- ".idea/vcs.xml"
|
|
66
|
+
- ".rubocop.yml"
|
|
67
|
+
- CHANGELOG.md
|
|
68
|
+
- LICENSE.txt
|
|
69
|
+
- README.md
|
|
70
|
+
- Rakefile
|
|
71
|
+
- WARP.md
|
|
72
|
+
- app/javascript/controllers/floating_label_controller.js
|
|
73
|
+
- app/javascript/controllers/password_toggle_controller.js
|
|
74
|
+
- floating_labels_rails.gemspec
|
|
75
|
+
- lib/floating_labels_rails.rb
|
|
76
|
+
- lib/floating_labels_rails/engine.rb
|
|
77
|
+
- lib/floating_labels_rails/form_builder.rb
|
|
78
|
+
- lib/floating_labels_rails/version.rb
|
|
79
|
+
- lib/generators/floating_labels_rails/install/install_generator.rb
|
|
80
|
+
- lib/generators/floating_labels_rails/install/templates/README
|
|
81
|
+
- lib/generators/floating_labels_rails/install/templates/controllers/floating_label_controller.js
|
|
82
|
+
- lib/generators/floating_labels_rails/install/templates/controllers/password_toggle_controller.js
|
|
83
|
+
- sig/floating_labels_rails.rbs
|
|
84
|
+
homepage: https://github.com/virgostyx/floating_labels_rails
|
|
85
|
+
licenses:
|
|
86
|
+
- MIT
|
|
87
|
+
metadata:
|
|
88
|
+
homepage_uri: https://github.com/virgostyx/floating_labels_rails
|
|
89
|
+
source_code_uri: https://github.com/virgostyx/floating_labels_rails
|
|
90
|
+
changelog_uri: https://github.com/virgostyx/floating_labels_rails/blob/main/CHANGELOG.md
|
|
91
|
+
rdoc_options: []
|
|
92
|
+
require_paths:
|
|
93
|
+
- lib
|
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: 2.7.0
|
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
requirements: []
|
|
105
|
+
rubygems_version: 3.6.9
|
|
106
|
+
specification_version: 4
|
|
107
|
+
summary: Floating label form fields pour Rails avec Tailwind CSS
|
|
108
|
+
test_files: []
|