moderntw_confirms 1.0.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/.rspec +3 -0
- data/CHANGELOG.md +24 -0
- data/LICENSE.txt +21 -0
- data/README.md +173 -0
- data/Rakefile +12 -0
- data/lib/generators/moderntw_confirms/install/install_generator.rb +125 -0
- data/lib/generators/moderntw_confirms/install/templates/_moderntw_confirms_modal.html.erb +104 -0
- data/lib/generators/moderntw_confirms/install/templates/moderntw_confirms.rb +20 -0
- data/lib/moderntw_confirms/engine.rb +16 -0
- data/lib/moderntw_confirms/version.rb +5 -0
- data/lib/moderntw_confirms.rb +20 -0
- data/vendor/assets/javascripts/moderntw_confirms.js +404 -0
- metadata +101 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 699d66807d16cb4cc6fefb0cc5ce9e0fc9b2cebeb1f4675d0199fac4ce7d63b6
|
|
4
|
+
data.tar.gz: dc52fe74b4c4af10f0615a559c3b24ccdb03570f14bc5e486f83c02de65924e3
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ea3e7d14c539f7e6bad0dad77228dc856c5d93ce1174129b3707eb0d7afe2324f6ca122737185345cecaed96f9dcd5599de4452f77eb9efe57d7c83552613f38
|
|
7
|
+
data.tar.gz: 5726f6f98b8962720b67b13a81a26dc8c04f77b362dbc75768b959c0fa85a9d4f65de960fb468eec45106c9556f3197a3f063ec7c3ec2f60873896edc764dad8
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
## [0.1.0] - 2025-10-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial release
|
|
14
|
+
- Automatic interception of Rails `data-confirm` attributes
|
|
15
|
+
- Support for Turbo `data-turbo-confirm` attributes
|
|
16
|
+
- Beautiful Tailwind CSS modal design
|
|
17
|
+
- Smooth fade and scale animations
|
|
18
|
+
- Keyboard navigation support (ESC to cancel, Enter to confirm)
|
|
19
|
+
- Rails generator for easy installation
|
|
20
|
+
- Support for importmap, Webpacker, and Asset Pipeline
|
|
21
|
+
- Optional Stimulus controller for advanced customization
|
|
22
|
+
- Configurable Tailwind classes via initializer
|
|
23
|
+
- Dynamic content support (works with AJAX/Turbo loaded content)
|
|
24
|
+
- Focus management for accessibility
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Robin Ciubotaru
|
|
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,173 @@
|
|
|
1
|
+
# ModernTW Confirms
|
|
2
|
+
|
|
3
|
+
Beautiful Tailwind CSS confirmation modals for Rails applications. Drop-in replacement for browser confirmation dialogs.
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/rb/moderntw_confirms)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Zero Configuration** - Works with your existing Rails code immediately
|
|
11
|
+
- **Beautiful Design** - Tailwind CSS powered modals with smooth animations
|
|
12
|
+
- **Smart Detection** - Automatically styles destructive actions in red
|
|
13
|
+
- **Fully Responsive** - Works perfectly on mobile, tablet, and desktop
|
|
14
|
+
- **Turbo Ready** - Full support for Rails 7+ Turbo and Turbo Streams
|
|
15
|
+
- **Accessible** - Keyboard navigation, focus management, and ARIA labels
|
|
16
|
+
- **Production Ready** - Error boundaries with automatic fallback to native confirms
|
|
17
|
+
- **Customizable** - Easy to modify styles to match your design system
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Add this line to your application's Gemfile:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
gem 'moderntw_confirms'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
And then execute:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bundle install
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Run the installation generator:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
rails generate moderntw_confirms:install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This will:
|
|
40
|
+
- Add the modal partial to your layouts
|
|
41
|
+
- Configure JavaScript based on your setup (Importmap, Webpacker, or Asset Pipeline)
|
|
42
|
+
- Create an optional configuration initializer
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
After installation, all your existing confirmation dialogs will automatically use the new modals. No code changes required!
|
|
47
|
+
|
|
48
|
+
### Existing code that just works:
|
|
49
|
+
|
|
50
|
+
```erb
|
|
51
|
+
<!-- Links with confirmations -->
|
|
52
|
+
<%= link_to "Delete", item_path(@item),
|
|
53
|
+
method: :delete,
|
|
54
|
+
data: { confirm: "Are you sure?" } %>
|
|
55
|
+
|
|
56
|
+
<!-- Buttons with confirmations -->
|
|
57
|
+
<%= button_to "Remove", item_path(@item),
|
|
58
|
+
method: :delete,
|
|
59
|
+
data: { turbo_confirm: "Delete this item?" } %>
|
|
60
|
+
|
|
61
|
+
<!-- Forms with confirmations -->
|
|
62
|
+
<%= form_with model: @user,
|
|
63
|
+
data: { turbo_confirm: "Save changes?" } do |form| %>
|
|
64
|
+
<!-- form fields -->
|
|
65
|
+
<% end %>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Smart Styling
|
|
69
|
+
|
|
70
|
+
The gem automatically detects destructive actions and styles them appropriately:
|
|
71
|
+
|
|
72
|
+
- **Red modals** for: delete, remove, destroy, reset, clear, cancel
|
|
73
|
+
- **Blue modals** for: save, update, submit, confirm, proceed
|
|
74
|
+
|
|
75
|
+
## Customization
|
|
76
|
+
|
|
77
|
+
### Quick Styling Changes
|
|
78
|
+
|
|
79
|
+
After installation, the modal HTML lives in `app/views/shared/_moderntw_confirms_modal.html.erb`. You can directly edit:
|
|
80
|
+
|
|
81
|
+
```erb
|
|
82
|
+
<!-- Change colors, sizes, spacing as needed -->
|
|
83
|
+
<div class="rounded-2xl bg-white p-6">
|
|
84
|
+
<!-- Your customizations here -->
|
|
85
|
+
</div>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Configuration Options
|
|
89
|
+
|
|
90
|
+
Create an initializer to configure the gem:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
# config/initializers/moderntw_confirms.rb
|
|
94
|
+
ModerntwConfirms.configure do |config|
|
|
95
|
+
config[:backdrop_class] = "bg-black/50"
|
|
96
|
+
config[:modal_class] = "rounded-3xl shadow-2xl"
|
|
97
|
+
config[:confirm_button_class] = "bg-indigo-600 hover:bg-indigo-700"
|
|
98
|
+
config[:cancel_button_class] = "bg-gray-200 hover:bg-gray-300"
|
|
99
|
+
end
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Animation Customization
|
|
103
|
+
|
|
104
|
+
The animations use CSS transitions and can be modified in the modal partial:
|
|
105
|
+
|
|
106
|
+
```css
|
|
107
|
+
/* Smooth fade in/out */
|
|
108
|
+
#moderntw-confirm-modal {
|
|
109
|
+
transition: opacity 300ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Bounce effect on modal */
|
|
113
|
+
#moderntw-confirm-modal [data-modal-panel] {
|
|
114
|
+
transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Browser Support
|
|
119
|
+
|
|
120
|
+
- Chrome/Edge 88+
|
|
121
|
+
- Firefox 78+
|
|
122
|
+
- Safari 14+
|
|
123
|
+
- Mobile browsers (iOS Safari, Chrome Mobile)
|
|
124
|
+
|
|
125
|
+
The modal includes automatic fallback to native browser confirms for older browsers or if JavaScript fails.
|
|
126
|
+
|
|
127
|
+
## Rails Compatibility
|
|
128
|
+
|
|
129
|
+
- Rails 6.1+ with Webpacker or Asset Pipeline
|
|
130
|
+
- Rails 7.0+ with Importmap, Webpacker, or Asset Pipeline
|
|
131
|
+
- Full Turbo and Turbo Streams support
|
|
132
|
+
- Works with both `data-confirm` (Rails UJS) and `data-turbo-confirm` (Turbo)
|
|
133
|
+
|
|
134
|
+
## Development
|
|
135
|
+
|
|
136
|
+
After checking out the repo, set up the test app:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
cd test_confirms_app
|
|
140
|
+
bundle install
|
|
141
|
+
rails server
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Visit http://localhost:3000 to see the examples.
|
|
145
|
+
|
|
146
|
+
### Running Tests
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
bundle exec rspec
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Contributing
|
|
153
|
+
|
|
154
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/rcbt17/moderntw_confirms. This project is intended to be a safe, welcoming space for collaboration.
|
|
155
|
+
|
|
156
|
+
1. Fork it
|
|
157
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
158
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
159
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
160
|
+
5. Create a new Pull Request
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
165
|
+
|
|
166
|
+
## Author
|
|
167
|
+
|
|
168
|
+
**Robin Ciubotaru**
|
|
169
|
+
[GitHub](https://github.com/rcbt17)
|
|
170
|
+
|
|
171
|
+
## Acknowledgments
|
|
172
|
+
|
|
173
|
+
Built with Rails and Tailwind CSS. Special thanks to the Rails and Hotwire communities for their excellent frameworks.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module ModerntwConfirms
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Install ModerntwConfirms and configure your application"
|
|
9
|
+
|
|
10
|
+
def install_javascript
|
|
11
|
+
if using_importmap?
|
|
12
|
+
install_with_importmap
|
|
13
|
+
elsif using_webpacker?
|
|
14
|
+
install_with_webpacker
|
|
15
|
+
else
|
|
16
|
+
install_with_asset_pipeline
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def copy_modal_partial
|
|
21
|
+
say "Copying modal partial", :green
|
|
22
|
+
copy_file "_moderntw_confirms_modal.html.erb", "app/views/shared/_moderntw_confirms_modal.html.erb"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add_modal_to_layout
|
|
26
|
+
say "Adding modal to application layout", :green
|
|
27
|
+
|
|
28
|
+
layout_file = "app/views/layouts/application.html.erb"
|
|
29
|
+
if File.exist?(Rails.root.join(layout_file))
|
|
30
|
+
insert_into_file layout_file, before: "</body>" do
|
|
31
|
+
"\n <%= render 'shared/moderntw_confirms_modal' %>\n "
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
say "Please add <%= render 'shared/moderntw_confirms_modal' %> before </body> in your layout", :yellow
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add_stimulus_controller
|
|
39
|
+
if using_stimulus?
|
|
40
|
+
say "Adding Stimulus controller for ModerntwConfirms", :green
|
|
41
|
+
append_to_file "app/javascript/controllers/index.js" do
|
|
42
|
+
<<~JS
|
|
43
|
+
|
|
44
|
+
import ConfirmModalController from "moderntw_confirms/confirm_modal_controller"
|
|
45
|
+
application.register("moderntw-confirms", ConfirmModalController)
|
|
46
|
+
JS
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def create_initializer
|
|
52
|
+
template "moderntw_confirms.rb", "config/initializers/moderntw_confirms.rb"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def display_post_install_message
|
|
56
|
+
say "\n ModerntwConfirms has been installed successfully! 🎉", :green
|
|
57
|
+
say "\n Next steps:", :yellow
|
|
58
|
+
say " 1. Make sure Tailwind CSS is installed and configured in your app"
|
|
59
|
+
say " 2. Restart your Rails server"
|
|
60
|
+
say " 3. All your data-confirm attributes will now use beautiful modals!"
|
|
61
|
+
say "\n Configuration:", :yellow
|
|
62
|
+
say " Edit config/initializers/moderntw_confirms.rb to customize modal styles"
|
|
63
|
+
say "\n Usage:", :yellow
|
|
64
|
+
say " Just use data-confirm as usual:"
|
|
65
|
+
say ' <%= link_to "Delete", item_path(@item), method: :delete, data: { confirm: "Are you sure?" } %>'
|
|
66
|
+
say "\n"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def using_importmap?
|
|
72
|
+
Rails.root.join("config", "importmap.rb").exist?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def using_webpacker?
|
|
76
|
+
defined?(Webpacker)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def using_stimulus?
|
|
80
|
+
Rails.root.join("app", "javascript", "controllers").exist?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def install_with_importmap
|
|
84
|
+
say "Installing ModerntwConfirms with importmap", :green
|
|
85
|
+
append_to_file "config/importmap.rb" do
|
|
86
|
+
<<~RUBY
|
|
87
|
+
|
|
88
|
+
# ModerntwConfirms
|
|
89
|
+
pin "moderntw_confirms", to: "moderntw_confirms.js"
|
|
90
|
+
pin "moderntw_confirms/confirm_modal_controller", to: "moderntw_confirms/confirm_modal_controller.js"
|
|
91
|
+
RUBY
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
append_to_file "app/javascript/application.js" do
|
|
95
|
+
<<~JS
|
|
96
|
+
|
|
97
|
+
// ModerntwConfirms - Replace browser confirms with Tailwind modals
|
|
98
|
+
import "moderntw_confirms"
|
|
99
|
+
JS
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def install_with_webpacker
|
|
104
|
+
say "Installing ModerntwConfirms with Webpacker", :green
|
|
105
|
+
append_to_file "app/javascript/packs/application.js" do
|
|
106
|
+
<<~JS
|
|
107
|
+
|
|
108
|
+
// ModerntwConfirms - Replace browser confirms with Tailwind modals
|
|
109
|
+
require("moderntw_confirms")
|
|
110
|
+
JS
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def install_with_asset_pipeline
|
|
115
|
+
say "Installing ModerntwConfirms with Asset Pipeline", :green
|
|
116
|
+
append_to_file "app/assets/javascripts/application.js" do
|
|
117
|
+
<<~JS
|
|
118
|
+
|
|
119
|
+
//= require moderntw_confirms
|
|
120
|
+
JS
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<div id="moderntw-confirm-modal"
|
|
2
|
+
class="hidden fixed inset-0 z-[99999] overflow-y-auto"
|
|
3
|
+
aria-labelledby="moderntw-modal-title"
|
|
4
|
+
aria-modal="true"
|
|
5
|
+
role="dialog"
|
|
6
|
+
data-turbo-permanent>
|
|
7
|
+
|
|
8
|
+
<div class="fixed inset-0" data-modal-backdrop>
|
|
9
|
+
<div class="absolute inset-0 bg-gray-900/40 backdrop-blur-sm"></div>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="fixed inset-0 overflow-y-auto">
|
|
13
|
+
<div class="flex min-h-full items-center justify-center p-4">
|
|
14
|
+
|
|
15
|
+
<div class="relative w-full max-w-md transform rounded-2xl bg-white p-0 text-left shadow-2xl transition-all"
|
|
16
|
+
data-modal-panel>
|
|
17
|
+
|
|
18
|
+
<div class="p-6">
|
|
19
|
+
<div class="flex items-start space-x-4">
|
|
20
|
+
<div class="flex-shrink-0">
|
|
21
|
+
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-blue-50" data-modal-icon-wrapper>
|
|
22
|
+
<svg data-modal-icon="info" class="h-6 w-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
23
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
24
|
+
</svg>
|
|
25
|
+
<svg data-modal-icon="danger" class="hidden h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
26
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
27
|
+
</svg>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div class="flex-1 pt-1">
|
|
32
|
+
<h3 class="text-lg font-semibold leading-6 text-gray-900" id="moderntw-modal-title" data-modal-title>
|
|
33
|
+
Confirmation Required
|
|
34
|
+
</h3>
|
|
35
|
+
<div class="mt-2">
|
|
36
|
+
<p class="text-sm text-gray-600" data-modal-message>
|
|
37
|
+
Are you sure you want to continue with this action?
|
|
38
|
+
</p>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div class="mt-6 flex flex-col-reverse gap-3 sm:flex-row sm:justify-end">
|
|
44
|
+
<button type="button"
|
|
45
|
+
data-modal-cancel
|
|
46
|
+
class="inline-flex w-full justify-center rounded-xl border border-gray-300 bg-white px-4 py-2.5 text-sm font-semibold text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 sm:w-auto">
|
|
47
|
+
Cancel
|
|
48
|
+
</button>
|
|
49
|
+
<button type="button"
|
|
50
|
+
data-modal-confirm
|
|
51
|
+
class="inline-flex w-full justify-center rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 sm:w-auto"
|
|
52
|
+
data-confirm-default-class="inline-flex w-full justify-center rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 sm:w-auto"
|
|
53
|
+
data-confirm-danger-class="inline-flex w-full justify-center rounded-xl bg-red-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus:outline-none focus:ring-2 focus:ring-red-600 focus:ring-offset-2 sm:w-auto">
|
|
54
|
+
Confirm
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
#moderntw-confirm-modal {
|
|
65
|
+
transition: opacity 300ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
66
|
+
will-change: opacity;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#moderntw-confirm-modal.modal-showing {
|
|
70
|
+
opacity: 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#moderntw-confirm-modal:not(.modal-showing) {
|
|
74
|
+
opacity: 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#moderntw-confirm-modal [data-modal-panel] {
|
|
78
|
+
transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
79
|
+
will-change: transform, opacity;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#moderntw-confirm-modal.modal-showing [data-modal-panel] {
|
|
83
|
+
transform: scale(1) translateY(0);
|
|
84
|
+
opacity: 1;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#moderntw-confirm-modal:not(.modal-showing) [data-modal-panel] {
|
|
88
|
+
transform: scale(0.9) translateY(20px);
|
|
89
|
+
opacity: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@supports (backdrop-filter: blur(0px)) or (-webkit-backdrop-filter: blur(0px)) {
|
|
93
|
+
#moderntw-confirm-modal [data-modal-backdrop] > div {
|
|
94
|
+
-webkit-backdrop-filter: blur(12px);
|
|
95
|
+
backdrop-filter: blur(12px);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@supports not ((backdrop-filter: blur(0px)) or (-webkit-backdrop-filter: blur(0px))) {
|
|
100
|
+
#moderntw-confirm-modal [data-modal-backdrop] > div {
|
|
101
|
+
background-color: rgba(17, 24, 39, 0.75);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
ModerntwConfirms.configure do |config|
|
|
2
|
+
# Customize the Tailwind classes for the modal components
|
|
3
|
+
# These are the default values - modify them to match your design system
|
|
4
|
+
|
|
5
|
+
# Background overlay classes
|
|
6
|
+
config[:backdrop_class] = "bg-black bg-opacity-50"
|
|
7
|
+
|
|
8
|
+
# Modal container classes
|
|
9
|
+
config[:modal_class] = "bg-white rounded-lg shadow-xl"
|
|
10
|
+
|
|
11
|
+
# Confirm button classes
|
|
12
|
+
config[:confirm_button_class] = "bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded"
|
|
13
|
+
|
|
14
|
+
# Cancel button classes
|
|
15
|
+
config[:cancel_button_class] = "bg-gray-300 hover:bg-gray-400 text-gray-800 font-semibold py-2 px-4 rounded"
|
|
16
|
+
|
|
17
|
+
# You can also add custom classes for specific use cases:
|
|
18
|
+
# For delete confirmations, you might want red buttons:
|
|
19
|
+
# config[:delete_confirm_button_class] = "bg-red-500 hover:bg-red-600 text-white font-semibold py-2 px-4 rounded"
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module ModerntwConfirms
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
isolate_namespace ModerntwConfirms
|
|
4
|
+
|
|
5
|
+
initializer "moderntw_confirms.assets" do |app|
|
|
6
|
+
app.config.assets.paths << root.join("vendor", "assets", "javascripts")
|
|
7
|
+
app.config.assets.precompile += %w( moderntw_confirms.js )
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
initializer "moderntw_confirms.importmap", before: "importmap" do |app|
|
|
11
|
+
if defined?(Importmap)
|
|
12
|
+
app.config.importmap.paths << Engine.root.join("config/importmap.rb")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "moderntw_confirms/version"
|
|
4
|
+
require_relative "moderntw_confirms/engine" if defined?(Rails)
|
|
5
|
+
|
|
6
|
+
module ModerntwConfirms
|
|
7
|
+
class Error < StandardError; end
|
|
8
|
+
|
|
9
|
+
mattr_accessor :config
|
|
10
|
+
self.config = {
|
|
11
|
+
backdrop_class: "bg-black bg-opacity-50",
|
|
12
|
+
modal_class: "bg-white rounded-lg shadow-xl",
|
|
13
|
+
confirm_button_class: "bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded",
|
|
14
|
+
cancel_button_class: "bg-gray-300 hover:bg-gray-400 text-gray-800 font-semibold py-2 px-4 rounded"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def self.configure
|
|
18
|
+
yield config
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
class ModerntwConfirms {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.modal = null;
|
|
7
|
+
this.currentCallback = null;
|
|
8
|
+
this.focusedElementBeforeModal = null;
|
|
9
|
+
this.initialized = false;
|
|
10
|
+
this.initializationAttempts = 0;
|
|
11
|
+
this.maxInitAttempts = 3;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
this.init();
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error('ModerntwConfirms: Initialization failed', error);
|
|
17
|
+
this.fallbackToNative();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
init() {
|
|
22
|
+
if (this.initialized || this.initializationAttempts >= this.maxInitAttempts) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this.initializationAttempts++;
|
|
27
|
+
|
|
28
|
+
if (document.readyState === 'loading') {
|
|
29
|
+
document.addEventListener('DOMContentLoaded', () => this.setup());
|
|
30
|
+
} else {
|
|
31
|
+
this.setup();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setup() {
|
|
36
|
+
try {
|
|
37
|
+
this.modal = document.getElementById('moderntw-confirm-modal');
|
|
38
|
+
|
|
39
|
+
if (!this.modal) {
|
|
40
|
+
if (this.initializationAttempts < this.maxInitAttempts) {
|
|
41
|
+
setTimeout(() => this.init(), 500);
|
|
42
|
+
return;
|
|
43
|
+
} else {
|
|
44
|
+
console.warn('ModerntwConfirms: Modal not found after multiple attempts. Falling back to native confirms.');
|
|
45
|
+
this.fallbackToNative();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.setupModalElements();
|
|
51
|
+
this.interceptTurboConfirms();
|
|
52
|
+
this.setupModalHandlers();
|
|
53
|
+
this.initialized = true;
|
|
54
|
+
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error('ModerntwConfirms: Setup failed', error);
|
|
57
|
+
this.fallbackToNative();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fallbackToNative() {
|
|
62
|
+
if (window.Turbo && window.Turbo.config && window.Turbo.config.forms) {
|
|
63
|
+
Turbo.config.forms.confirm = (message) => {
|
|
64
|
+
return Promise.resolve(window.confirm(message));
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setupModalElements() {
|
|
70
|
+
this.backdrop = this.modal.querySelector('[data-modal-backdrop]');
|
|
71
|
+
this.panel = this.modal.querySelector('[data-modal-panel]');
|
|
72
|
+
this.messageEl = this.modal.querySelector('[data-modal-message]');
|
|
73
|
+
this.titleEl = this.modal.querySelector('[data-modal-title]');
|
|
74
|
+
this.confirmBtn = this.modal.querySelector('[data-modal-confirm]');
|
|
75
|
+
this.cancelBtn = this.modal.querySelector('[data-modal-cancel]');
|
|
76
|
+
this.iconWrapper = this.modal.querySelector('[data-modal-icon-wrapper]');
|
|
77
|
+
this.infoIcon = this.modal.querySelector('[data-modal-icon="info"]');
|
|
78
|
+
this.dangerIcon = this.modal.querySelector('[data-modal-icon="danger"]');
|
|
79
|
+
|
|
80
|
+
if (!this.messageEl || !this.confirmBtn || !this.cancelBtn) {
|
|
81
|
+
throw new Error('Critical modal elements not found');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interceptTurboConfirms() {
|
|
86
|
+
const self = this;
|
|
87
|
+
|
|
88
|
+
if (window.Turbo && window.Turbo.config && window.Turbo.config.forms) {
|
|
89
|
+
Turbo.config.forms.confirm = (message) => {
|
|
90
|
+
return new Promise((resolve) => {
|
|
91
|
+
try {
|
|
92
|
+
self.showModal(message, resolve);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('ModerntwConfirms: Error showing modal', error);
|
|
95
|
+
resolve(window.confirm(message));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.formSubmitHandler = (event) => {
|
|
102
|
+
try {
|
|
103
|
+
const element = event.target;
|
|
104
|
+
const message = element.getAttribute('data-turbo-confirm') || element.getAttribute('data-confirm');
|
|
105
|
+
|
|
106
|
+
if (message && !element.hasAttribute('data-moderntw-confirmed')) {
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
|
|
109
|
+
if (event.detail && event.detail.formSubmission) {
|
|
110
|
+
event.detail.formSubmission.stop();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.showModal(message, (confirmed) => {
|
|
114
|
+
if (confirmed) {
|
|
115
|
+
element.setAttribute('data-moderntw-confirmed', 'true');
|
|
116
|
+
element.requestSubmit();
|
|
117
|
+
setTimeout(() => element.removeAttribute('data-moderntw-confirmed'), 100);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error('ModerntwConfirms: Error in form submit handler', error);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
document.addEventListener('turbo:submit-start', this.formSubmitHandler);
|
|
127
|
+
|
|
128
|
+
this.clickHandler = (event) => {
|
|
129
|
+
try {
|
|
130
|
+
let element = event.target;
|
|
131
|
+
|
|
132
|
+
while (element && element !== document.body) {
|
|
133
|
+
if (element.hasAttribute('data-turbo-confirm') || element.hasAttribute('data-confirm')) {
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
element = element.parentElement;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!element || element === document.body) return;
|
|
140
|
+
|
|
141
|
+
const message = element.getAttribute('data-turbo-confirm') || element.getAttribute('data-confirm');
|
|
142
|
+
|
|
143
|
+
if (message && !element.hasAttribute('data-moderntw-confirmed')) {
|
|
144
|
+
event.preventDefault();
|
|
145
|
+
event.stopPropagation();
|
|
146
|
+
event.stopImmediatePropagation();
|
|
147
|
+
|
|
148
|
+
this.showModal(message, (confirmed) => {
|
|
149
|
+
if (confirmed) {
|
|
150
|
+
element.setAttribute('data-moderntw-confirmed', 'true');
|
|
151
|
+
|
|
152
|
+
const turboConfirm = element.getAttribute('data-turbo-confirm');
|
|
153
|
+
const dataConfirm = element.getAttribute('data-confirm');
|
|
154
|
+
element.removeAttribute('data-turbo-confirm');
|
|
155
|
+
element.removeAttribute('data-confirm');
|
|
156
|
+
|
|
157
|
+
if (element.tagName === 'FORM') {
|
|
158
|
+
element.requestSubmit();
|
|
159
|
+
} else if (element.tagName === 'BUTTON' && element.form) {
|
|
160
|
+
element.form.requestSubmit(element);
|
|
161
|
+
} else {
|
|
162
|
+
element.click();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
setTimeout(() => {
|
|
166
|
+
if (turboConfirm) element.setAttribute('data-turbo-confirm', turboConfirm);
|
|
167
|
+
if (dataConfirm) element.setAttribute('data-confirm', dataConfirm);
|
|
168
|
+
element.removeAttribute('data-moderntw-confirmed');
|
|
169
|
+
}, 100);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error('ModerntwConfirms: Error in click handler', error);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
document.addEventListener('click', this.clickHandler, true);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
setupModalHandlers() {
|
|
184
|
+
if (this.cancelBtn) {
|
|
185
|
+
this.cancelBtnHandler = () => this.handleAction(false);
|
|
186
|
+
this.cancelBtn.addEventListener('click', this.cancelBtnHandler);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (this.backdrop) {
|
|
190
|
+
this.backdropHandler = () => this.handleAction(false);
|
|
191
|
+
this.backdrop.addEventListener('click', this.backdropHandler);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (this.confirmBtn) {
|
|
195
|
+
this.confirmBtnHandler = () => this.handleAction(true);
|
|
196
|
+
this.confirmBtn.addEventListener('click', this.confirmBtnHandler);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
this.escHandler = (e) => {
|
|
200
|
+
if (e.key === 'Escape' && this.isModalOpen()) {
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
this.handleAction(false);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
document.addEventListener('keydown', this.escHandler);
|
|
206
|
+
|
|
207
|
+
this.tabHandler = (e) => {
|
|
208
|
+
if (e.key === 'Tab' && this.isModalOpen()) {
|
|
209
|
+
this.trapFocus(e);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
this.modal.addEventListener('keydown', this.tabHandler);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
showModal(message, callback) {
|
|
216
|
+
if (!this.modal || !this.initialized) {
|
|
217
|
+
const result = window.confirm(message);
|
|
218
|
+
if (callback) callback(result);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
this.currentCallback = callback;
|
|
224
|
+
this.focusedElementBeforeModal = document.activeElement;
|
|
225
|
+
|
|
226
|
+
if (this.messageEl) {
|
|
227
|
+
this.messageEl.textContent = String(message || 'Are you sure?');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const isDanger = /delete|remove|destroy|reset|clear|drop|erase|cancel/i.test(message);
|
|
231
|
+
this.setModalType(isDanger ? 'danger' : 'info');
|
|
232
|
+
|
|
233
|
+
this.modal.classList.remove('hidden');
|
|
234
|
+
this.modal.offsetHeight;
|
|
235
|
+
|
|
236
|
+
requestAnimationFrame(() => {
|
|
237
|
+
this.modal.classList.add('modal-showing');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
setTimeout(() => {
|
|
241
|
+
if (this.confirmBtn) {
|
|
242
|
+
this.confirmBtn.focus();
|
|
243
|
+
}
|
|
244
|
+
}, 100);
|
|
245
|
+
|
|
246
|
+
document.body.style.overflow = 'hidden';
|
|
247
|
+
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.error('ModerntwConfirms: Error showing modal', error);
|
|
250
|
+
const result = window.confirm(message);
|
|
251
|
+
if (callback) callback(result);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
setModalType(type) {
|
|
256
|
+
try {
|
|
257
|
+
const defaultBtnClass = this.confirmBtn?.getAttribute('data-confirm-default-class');
|
|
258
|
+
const dangerBtnClass = this.confirmBtn?.getAttribute('data-confirm-danger-class');
|
|
259
|
+
|
|
260
|
+
if (type === 'danger') {
|
|
261
|
+
if (this.infoIcon) this.infoIcon.classList.add('hidden');
|
|
262
|
+
if (this.dangerIcon) this.dangerIcon.classList.remove('hidden');
|
|
263
|
+
if (this.iconWrapper) {
|
|
264
|
+
this.iconWrapper.className = 'flex h-12 w-12 items-center justify-center rounded-full bg-red-50';
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (this.confirmBtn && dangerBtnClass) {
|
|
268
|
+
this.confirmBtn.className = dangerBtnClass;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (this.titleEl) {
|
|
272
|
+
this.titleEl.textContent = 'Are you absolutely sure?';
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
if (this.infoIcon) this.infoIcon.classList.remove('hidden');
|
|
276
|
+
if (this.dangerIcon) this.dangerIcon.classList.add('hidden');
|
|
277
|
+
if (this.iconWrapper) {
|
|
278
|
+
this.iconWrapper.className = 'flex h-12 w-12 items-center justify-center rounded-full bg-blue-50';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (this.confirmBtn && defaultBtnClass) {
|
|
282
|
+
this.confirmBtn.className = defaultBtnClass;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (this.titleEl) {
|
|
286
|
+
this.titleEl.textContent = 'Confirmation Required';
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
} catch (error) {
|
|
290
|
+
console.error('ModerntwConfirms: Error setting modal type', error);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
handleAction(confirmed) {
|
|
295
|
+
if (!this.modal) return;
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
this.modal.classList.remove('modal-showing');
|
|
299
|
+
|
|
300
|
+
setTimeout(() => {
|
|
301
|
+
this.modal.classList.add('hidden');
|
|
302
|
+
|
|
303
|
+
document.body.style.overflow = '';
|
|
304
|
+
|
|
305
|
+
if (this.focusedElementBeforeModal && this.focusedElementBeforeModal.focus) {
|
|
306
|
+
try {
|
|
307
|
+
this.focusedElementBeforeModal.focus();
|
|
308
|
+
} catch (e) {
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (this.currentCallback) {
|
|
313
|
+
const callback = this.currentCallback;
|
|
314
|
+
this.currentCallback = null;
|
|
315
|
+
callback(confirmed);
|
|
316
|
+
}
|
|
317
|
+
}, 300);
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error('ModerntwConfirms: Error handling action', error);
|
|
320
|
+
if (this.currentCallback) {
|
|
321
|
+
this.currentCallback(confirmed);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
isModalOpen() {
|
|
327
|
+
return this.modal && !this.modal.classList.contains('hidden');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
trapFocus(e) {
|
|
331
|
+
try {
|
|
332
|
+
const focusableElements = this.modal.querySelectorAll(
|
|
333
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
if (focusableElements.length === 0) return;
|
|
337
|
+
|
|
338
|
+
const firstFocusable = focusableElements[0];
|
|
339
|
+
const lastFocusable = focusableElements[focusableElements.length - 1];
|
|
340
|
+
|
|
341
|
+
if (e.shiftKey && document.activeElement === firstFocusable) {
|
|
342
|
+
e.preventDefault();
|
|
343
|
+
lastFocusable.focus();
|
|
344
|
+
} else if (!e.shiftKey && document.activeElement === lastFocusable) {
|
|
345
|
+
e.preventDefault();
|
|
346
|
+
firstFocusable.focus();
|
|
347
|
+
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
console.error('ModerntwConfirms: Error trapping focus', error);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
destroy() {
|
|
354
|
+
if (this.formSubmitHandler) {
|
|
355
|
+
document.removeEventListener('turbo:submit-start', this.formSubmitHandler);
|
|
356
|
+
}
|
|
357
|
+
if (this.clickHandler) {
|
|
358
|
+
document.removeEventListener('click', this.clickHandler, true);
|
|
359
|
+
}
|
|
360
|
+
if (this.escHandler) {
|
|
361
|
+
document.removeEventListener('keydown', this.escHandler);
|
|
362
|
+
}
|
|
363
|
+
if (this.cancelBtn && this.cancelBtnHandler) {
|
|
364
|
+
this.cancelBtn.removeEventListener('click', this.cancelBtnHandler);
|
|
365
|
+
}
|
|
366
|
+
if (this.backdrop && this.backdropHandler) {
|
|
367
|
+
this.backdrop.removeEventListener('click', this.backdropHandler);
|
|
368
|
+
}
|
|
369
|
+
if (this.confirmBtn && this.confirmBtnHandler) {
|
|
370
|
+
this.confirmBtn.removeEventListener('click', this.confirmBtnHandler);
|
|
371
|
+
}
|
|
372
|
+
if (this.modal && this.tabHandler) {
|
|
373
|
+
this.modal.removeEventListener('keydown', this.tabHandler);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (typeof window !== 'undefined') {
|
|
379
|
+
window.ModerntwConfirms = ModerntwConfirms;
|
|
380
|
+
|
|
381
|
+
const initializeOnce = () => {
|
|
382
|
+
try {
|
|
383
|
+
if (window.moderntwConfirmsInstance && window.moderntwConfirmsInstance.destroy) {
|
|
384
|
+
window.moderntwConfirmsInstance.destroy();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
window.moderntwConfirmsInstance = new ModerntwConfirms();
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.error('ModerntwConfirms: Failed to initialize', error);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
if (window.Turbo) {
|
|
394
|
+
initializeOnce();
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
document.addEventListener('turbo:load', initializeOnce);
|
|
398
|
+
document.addEventListener('DOMContentLoaded', initializeOnce);
|
|
399
|
+
|
|
400
|
+
if (document.readyState !== 'loading') {
|
|
401
|
+
setTimeout(initializeOnce, 100);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
})();
|
metadata
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: moderntw_confirms
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Robin Ciubotaru
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-10-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '6.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '6.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: sqlite3
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: puma
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
description: Automatically intercepts Rails data-confirm and data-turbo-confirm attributes,
|
|
56
|
+
replacing native browser dialogs with customizable Tailwind CSS modals
|
|
57
|
+
email:
|
|
58
|
+
- robinciubotaru17@gmail.com
|
|
59
|
+
executables: []
|
|
60
|
+
extensions: []
|
|
61
|
+
extra_rdoc_files: []
|
|
62
|
+
files:
|
|
63
|
+
- ".rspec"
|
|
64
|
+
- CHANGELOG.md
|
|
65
|
+
- LICENSE.txt
|
|
66
|
+
- README.md
|
|
67
|
+
- Rakefile
|
|
68
|
+
- lib/generators/moderntw_confirms/install/install_generator.rb
|
|
69
|
+
- lib/generators/moderntw_confirms/install/templates/_moderntw_confirms_modal.html.erb
|
|
70
|
+
- lib/generators/moderntw_confirms/install/templates/moderntw_confirms.rb
|
|
71
|
+
- lib/moderntw_confirms.rb
|
|
72
|
+
- lib/moderntw_confirms/engine.rb
|
|
73
|
+
- lib/moderntw_confirms/version.rb
|
|
74
|
+
- vendor/assets/javascripts/moderntw_confirms.js
|
|
75
|
+
homepage: https://github.com/rcbt17/moderntw_confirms
|
|
76
|
+
licenses:
|
|
77
|
+
- MIT
|
|
78
|
+
metadata:
|
|
79
|
+
homepage_uri: https://github.com/rcbt17/moderntw_confirms
|
|
80
|
+
source_code_uri: https://github.com/rcbt17/moderntw_confirms
|
|
81
|
+
changelog_uri: https://github.com/rcbt17/moderntw_confirms/blob/main/CHANGELOG.md
|
|
82
|
+
post_install_message:
|
|
83
|
+
rdoc_options: []
|
|
84
|
+
require_paths:
|
|
85
|
+
- lib
|
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: 3.0.0
|
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
requirements: []
|
|
97
|
+
rubygems_version: 3.5.9
|
|
98
|
+
signing_key:
|
|
99
|
+
specification_version: 4
|
|
100
|
+
summary: Replace browser confirm dialogs with modern Tailwind CSS modals in Rails
|
|
101
|
+
test_files: []
|