ultimate_turbo_modal 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 896c6379f97064006b57fbb612cab3c16560af307d2271da0c488785dfc366db
4
+ data.tar.gz: aa78d481f27597261e57ca07c0b80f0fc681529e63d92df803347a18c4c1b9da
5
+ SHA512:
6
+ metadata.gz: 260a1fddf247e51ed411f0ae83d85faa2d581f18c8ef71664129b11ad1e7480c6be9da04f7e86321999c0ad9098643a23cb83480b39f79f847ee59939a47098f
7
+ data.tar.gz: 102593d43c1b8a94ca85787d53a2f8a8064fd3d17fdec96e382c2f6795baffa599d8478fcd759c85b23d694f091e643e76e6516d1fa8c1d3911487e43f0738f8
data/.rubocop.yml ADDED
@@ -0,0 +1,6 @@
1
+ require:
2
+ - standard
3
+ inherit_gem:
4
+ standard: config/base.yml
5
+ AllCops:
6
+ SuggestExtensions: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-3.2.0
data/.standard.yml ADDED
@@ -0,0 +1,9 @@
1
+ ruby_version: 3.2.0
2
+ parallel: true
3
+ formatter: progress
4
+ plugins:
5
+ - standard-rails:
6
+ target_rails_version: 7.0
7
+
8
+ ignore:
9
+ - 'vendor/**/*'
data/.tool-versions ADDED
@@ -0,0 +1,2 @@
1
+ ruby 3.2.0
2
+ nodejs 18.9.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [1.0.0] - 2023-10-31
4
+
5
+ - Initial release as a Ruby Gem
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in ultimate_turbo_modal.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "standard", "~> 1.3"
11
+ gem "standard-rails"
data/Gemfile.lock ADDED
@@ -0,0 +1,261 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ultimate_turbo_modal (1.0.0)
5
+ phlex-rails (>= 1.0, < 2.0)
6
+ rails (>= 7)
7
+ stimulus-rails
8
+ turbo-rails
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ actioncable (7.1.1)
14
+ actionpack (= 7.1.1)
15
+ activesupport (= 7.1.1)
16
+ nio4r (~> 2.0)
17
+ websocket-driver (>= 0.6.1)
18
+ zeitwerk (~> 2.6)
19
+ actionmailbox (7.1.1)
20
+ actionpack (= 7.1.1)
21
+ activejob (= 7.1.1)
22
+ activerecord (= 7.1.1)
23
+ activestorage (= 7.1.1)
24
+ activesupport (= 7.1.1)
25
+ mail (>= 2.7.1)
26
+ net-imap
27
+ net-pop
28
+ net-smtp
29
+ actionmailer (7.1.1)
30
+ actionpack (= 7.1.1)
31
+ actionview (= 7.1.1)
32
+ activejob (= 7.1.1)
33
+ activesupport (= 7.1.1)
34
+ mail (~> 2.5, >= 2.5.4)
35
+ net-imap
36
+ net-pop
37
+ net-smtp
38
+ rails-dom-testing (~> 2.2)
39
+ actionpack (7.1.1)
40
+ actionview (= 7.1.1)
41
+ activesupport (= 7.1.1)
42
+ nokogiri (>= 1.8.5)
43
+ rack (>= 2.2.4)
44
+ rack-session (>= 1.0.1)
45
+ rack-test (>= 0.6.3)
46
+ rails-dom-testing (~> 2.2)
47
+ rails-html-sanitizer (~> 1.6)
48
+ actiontext (7.1.1)
49
+ actionpack (= 7.1.1)
50
+ activerecord (= 7.1.1)
51
+ activestorage (= 7.1.1)
52
+ activesupport (= 7.1.1)
53
+ globalid (>= 0.6.0)
54
+ nokogiri (>= 1.8.5)
55
+ actionview (7.1.1)
56
+ activesupport (= 7.1.1)
57
+ builder (~> 3.1)
58
+ erubi (~> 1.11)
59
+ rails-dom-testing (~> 2.2)
60
+ rails-html-sanitizer (~> 1.6)
61
+ activejob (7.1.1)
62
+ activesupport (= 7.1.1)
63
+ globalid (>= 0.3.6)
64
+ activemodel (7.1.1)
65
+ activesupport (= 7.1.1)
66
+ activerecord (7.1.1)
67
+ activemodel (= 7.1.1)
68
+ activesupport (= 7.1.1)
69
+ timeout (>= 0.4.0)
70
+ activestorage (7.1.1)
71
+ actionpack (= 7.1.1)
72
+ activejob (= 7.1.1)
73
+ activerecord (= 7.1.1)
74
+ activesupport (= 7.1.1)
75
+ marcel (~> 1.0)
76
+ activesupport (7.1.1)
77
+ base64
78
+ bigdecimal
79
+ concurrent-ruby (~> 1.0, >= 1.0.2)
80
+ connection_pool (>= 2.2.5)
81
+ drb
82
+ i18n (>= 1.6, < 2)
83
+ minitest (>= 5.1)
84
+ mutex_m
85
+ tzinfo (~> 2.0)
86
+ ast (2.4.2)
87
+ base64 (0.1.1)
88
+ bigdecimal (3.1.4)
89
+ builder (3.2.4)
90
+ cgi (0.3.6)
91
+ concurrent-ruby (1.2.2)
92
+ connection_pool (2.4.1)
93
+ crass (1.0.6)
94
+ date (3.3.3)
95
+ drb (2.1.1)
96
+ ruby2_keywords
97
+ erb (4.0.3)
98
+ cgi (>= 0.3.3)
99
+ erubi (1.12.0)
100
+ globalid (1.2.1)
101
+ activesupport (>= 6.1)
102
+ i18n (1.14.1)
103
+ concurrent-ruby (~> 1.0)
104
+ io-console (0.6.0)
105
+ irb (1.8.3)
106
+ rdoc
107
+ reline (>= 0.3.8)
108
+ json (2.6.3)
109
+ language_server-protocol (3.17.0.3)
110
+ lint_roller (1.1.0)
111
+ loofah (2.21.4)
112
+ crass (~> 1.0.2)
113
+ nokogiri (>= 1.12.0)
114
+ mail (2.8.1)
115
+ mini_mime (>= 0.1.1)
116
+ net-imap
117
+ net-pop
118
+ net-smtp
119
+ marcel (1.0.2)
120
+ mini_mime (1.1.5)
121
+ minitest (5.20.0)
122
+ mutex_m (0.1.2)
123
+ net-imap (0.4.2)
124
+ date
125
+ net-protocol
126
+ net-pop (0.1.2)
127
+ net-protocol
128
+ net-protocol (0.2.1)
129
+ timeout
130
+ net-smtp (0.4.0)
131
+ net-protocol
132
+ nio4r (2.5.9)
133
+ nokogiri (1.15.4-arm64-darwin)
134
+ racc (~> 1.4)
135
+ parallel (1.23.0)
136
+ parser (3.2.2.4)
137
+ ast (~> 2.4.1)
138
+ racc
139
+ phlex (1.8.1)
140
+ concurrent-ruby (~> 1.2)
141
+ erb (>= 4)
142
+ zeitwerk (~> 2.6)
143
+ phlex-rails (1.0.0)
144
+ phlex (~> 1.7)
145
+ rails (>= 6.1, < 8)
146
+ zeitwerk (~> 2.6)
147
+ psych (5.1.1.1)
148
+ stringio
149
+ racc (1.7.1)
150
+ rack (3.0.8)
151
+ rack-session (2.0.0)
152
+ rack (>= 3.0.0)
153
+ rack-test (2.1.0)
154
+ rack (>= 1.3)
155
+ rackup (2.1.0)
156
+ rack (>= 3)
157
+ webrick (~> 1.8)
158
+ rails (7.1.1)
159
+ actioncable (= 7.1.1)
160
+ actionmailbox (= 7.1.1)
161
+ actionmailer (= 7.1.1)
162
+ actionpack (= 7.1.1)
163
+ actiontext (= 7.1.1)
164
+ actionview (= 7.1.1)
165
+ activejob (= 7.1.1)
166
+ activemodel (= 7.1.1)
167
+ activerecord (= 7.1.1)
168
+ activestorage (= 7.1.1)
169
+ activesupport (= 7.1.1)
170
+ bundler (>= 1.15.0)
171
+ railties (= 7.1.1)
172
+ rails-dom-testing (2.2.0)
173
+ activesupport (>= 5.0.0)
174
+ minitest
175
+ nokogiri (>= 1.6)
176
+ rails-html-sanitizer (1.6.0)
177
+ loofah (~> 2.21)
178
+ nokogiri (~> 1.14)
179
+ railties (7.1.1)
180
+ actionpack (= 7.1.1)
181
+ activesupport (= 7.1.1)
182
+ irb
183
+ rackup (>= 1.0.0)
184
+ rake (>= 12.2)
185
+ thor (~> 1.0, >= 1.2.2)
186
+ zeitwerk (~> 2.6)
187
+ rainbow (3.1.1)
188
+ rake (13.1.0)
189
+ rdoc (6.5.0)
190
+ psych (>= 4.0.0)
191
+ regexp_parser (2.8.2)
192
+ reline (0.3.9)
193
+ io-console (~> 0.5)
194
+ rexml (3.2.6)
195
+ rubocop (1.56.4)
196
+ base64 (~> 0.1.1)
197
+ json (~> 2.3)
198
+ language_server-protocol (>= 3.17.0)
199
+ parallel (~> 1.10)
200
+ parser (>= 3.2.2.3)
201
+ rainbow (>= 2.2.2, < 4.0)
202
+ regexp_parser (>= 1.8, < 3.0)
203
+ rexml (>= 3.2.5, < 4.0)
204
+ rubocop-ast (>= 1.28.1, < 2.0)
205
+ ruby-progressbar (~> 1.7)
206
+ unicode-display_width (>= 2.4.0, < 3.0)
207
+ rubocop-ast (1.30.0)
208
+ parser (>= 3.2.1.0)
209
+ rubocop-performance (1.19.1)
210
+ rubocop (>= 1.7.0, < 2.0)
211
+ rubocop-ast (>= 0.4.0)
212
+ rubocop-rails (2.20.2)
213
+ activesupport (>= 4.2.0)
214
+ rack (>= 1.1)
215
+ rubocop (>= 1.33.0, < 2.0)
216
+ ruby-progressbar (1.13.0)
217
+ ruby2_keywords (0.0.5)
218
+ standard (1.31.2)
219
+ language_server-protocol (~> 3.17.0.2)
220
+ lint_roller (~> 1.0)
221
+ rubocop (~> 1.56.4)
222
+ standard-custom (~> 1.0.0)
223
+ standard-performance (~> 1.2)
224
+ standard-custom (1.0.2)
225
+ lint_roller (~> 1.0)
226
+ rubocop (~> 1.50)
227
+ standard-performance (1.2.1)
228
+ lint_roller (~> 1.1)
229
+ rubocop-performance (~> 1.19.1)
230
+ standard-rails (0.2.0)
231
+ lint_roller (~> 1.0)
232
+ rubocop-rails (~> 2.20.2)
233
+ stimulus-rails (1.3.0)
234
+ railties (>= 6.0.0)
235
+ stringio (3.0.8)
236
+ thor (1.3.0)
237
+ timeout (0.4.0)
238
+ turbo-rails (1.5.0)
239
+ actionpack (>= 6.0.0)
240
+ activejob (>= 6.0.0)
241
+ railties (>= 6.0.0)
242
+ tzinfo (2.0.6)
243
+ concurrent-ruby (~> 1.0)
244
+ unicode-display_width (2.5.0)
245
+ webrick (1.8.1)
246
+ websocket-driver (0.7.6)
247
+ websocket-extensions (>= 0.1.0)
248
+ websocket-extensions (0.1.5)
249
+ zeitwerk (2.6.12)
250
+
251
+ PLATFORMS
252
+ arm64-darwin-22
253
+
254
+ DEPENDENCIES
255
+ rake (~> 13.0)
256
+ standard (~> 1.3)
257
+ standard-rails
258
+ ultimate_turbo_modal!
259
+
260
+ BUNDLED WITH
261
+ 2.4.13
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Carl Mercier
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,161 @@
1
+ # The Ultimate Turbo Modal for Rails (UTMR)
2
+
3
+ There are MANY Turbo/Hotwire/Stimulus modal dialog implementations out there, and it seems like everyone goes about it a different way. However, as you may have learned the hard way, the majority fall short in different, often subtle ways. They generally cover the basics quite well, but do not check all the boxes for real-world use.
4
+
5
+ UTMR aims to be the be-all and end-all of Turbo Modals. I believe it is the best implementation and checks all the boxes. It is feature-rich, yet extremely easy to use.
6
+
7
+ Under the hood, it uses [Stimulus](https://stimulus.hotwired.dev), [Turbo](https://turbo.hotwired.dev/), [el-transition](https://github.com/mmccall10/el-transition), and optionally [Idiomorph](https://github.com/bigskysoftware/idiomorph).
8
+
9
+ It currently ships in a single flavor: Tailwind CSS. It is easy to create your own to suit your needs such as vanilla CSS or any other CSS framework you may prefer. See `lib/ultimate_turbo_modal/flavors/tailwind.rb` for the Tailwind code.
10
+
11
+
12
+ &nbsp;
13
+ &nbsp;
14
+ ## Features and capabilities
15
+
16
+ - Extremely easy to use
17
+ - Fully responsive
18
+ - Does not break if a user navigates directly to a page that is usually shown in a modal
19
+ - Opening a modal in a new browser tab (ie: right click) gracefully degrades without having to code a modal and non-modal version of the same page
20
+ - Automatically handles URL history (ie: pushState) for shareable URLs
21
+ - pushState URL optionally overrideable
22
+ - Seamless support for multi-page navigation within the modal
23
+ - Seamless support for forms with validations
24
+ - Seamless support for Rails flash messages
25
+ - Enter/leave animation (fade in/out)
26
+ - Support for long, scrollable modals
27
+ - Properly locks the background page when scrolling a long modal
28
+ - Click outside the modal to dismiss
29
+ - Keyboard control; ESC to dismiss
30
+ - Automatic (or not) close button
31
+
32
+
33
+ &nbsp;
34
+ &nbsp;
35
+ ## Demo
36
+
37
+ A demo application can be found at https://github.com/cmer/ultimate_turbo_modal-demo. A video demo can be seen here: [https://youtu.be/eG5uWTH74NA](https://youtu.be/eG5uWTH74NA)
38
+
39
+
40
+ &nbsp;
41
+ &nbsp;
42
+ ## Installation
43
+
44
+ 1. Install the gem and add to the application's Gemfile by executing:
45
+
46
+ $ bundle add ultimate_turbo_modal
47
+
48
+ 2. Install the npm package:
49
+
50
+ $ yarn add ultimate_turbo_modal
51
+
52
+ - or -
53
+
54
+ $ bin/rails importmaps pin ultimate_turbo_modal
55
+
56
+ 3. Add the following as the first element in the `body` tag of `views/layouts/application.html.erb`:
57
+
58
+ ```erb
59
+ <%= turbo_frame_tag "modal" %>
60
+ ``````
61
+
62
+ 4. Register the Stimulus controller in `app/javascript/controllers/index.js` adding the following lines at the end.
63
+
64
+ ```js
65
+ import setupUltimateTurboModal from "ultimate_turbo_modal";
66
+ setupUltimateTurboModal(application);
67
+ ```
68
+
69
+ 5. Optionally (but recommended), configure UTMR to use Idiomorph. See below for details.
70
+
71
+ &nbsp;
72
+ &nbsp;
73
+ ## Usage
74
+
75
+ 1. Wrap your view inside a `modal` block as follow:
76
+
77
+ ```erb
78
+ <%= modal do %>
79
+ Hello World!
80
+ <% end %>
81
+ ```
82
+
83
+ 2. Link to your view by specifying `modal` as the target Turbo Frame:
84
+
85
+ ```erb
86
+ <%= link_to "Open Modal", "/hello_world", data: { turbo_frame: "modal" } %>
87
+ ```
88
+
89
+ Clicking on the link will automatically open the content of the view inside a modal. If you open the link in a new tab, it will render normally outside of the modal. Nothing to do!
90
+
91
+ If you need to do something a little bit more advanced when the view is shown outside of a modal, you can use the `#inside_modal?` method as such:
92
+
93
+ ```erb
94
+ <% if inside_modal? %>
95
+ <h1 class="text-2xl mb-8">Hello from modal</h1>
96
+ <% else %>
97
+ <h1 class="text-2xl mb-8">Hello from a normal page render</h1>
98
+ <% end %>
99
+ ```
100
+
101
+ &nbsp;
102
+ &nbsp;
103
+ ## Options
104
+
105
+ ### `padding`, default: `true`
106
+
107
+ Adds padding inside the modal.
108
+
109
+ ### `close_button`, default: `true`
110
+
111
+ Shows or hide a close button (X) at the top right of the modal.
112
+
113
+ ### `advance_history`, default: `true`
114
+
115
+ When opening the modal, the URL in the URL bar will change to the URL of the view being shown in the modal. The Back button dismisses the modal and navigates back.
116
+
117
+ ### `advance_history_url`, default: `nil`
118
+
119
+ Override for the URL being shown in the URL bar when `advance_history` is enabled. Default is the actual URL.
120
+
121
+
122
+ ### Example usage with options
123
+
124
+ ```erb
125
+ <%= modal(padding: true, close_button: false, advance_history_url: "/foo/bar") do %>
126
+ Hello World!
127
+ <% end %>
128
+ ```
129
+
130
+ ## Installing & Configuring Idiomorph
131
+
132
+ // Morph Turbo Frame rendering to allow navigation within Turbo Frames
133
+ // without having to teardown the entire frame. This is needed to prevent
134
+ // the leaving and entering animations from repeating when navigating
135
+ // within the modal. You could optionally not use the code below if you
136
+ // do not intend to allow navigation within the modal.
137
+ //
138
+ // Note that Turbo 8 will include Idiomorph by default.
139
+ //
140
+ // In the meantime, add `<script src="https://unpkg.com/idiomorph"></script>`
141
+ // to your HTML <head>.
142
+ addEventListener("turbo:before-frame-render", (event) => {
143
+ event.detail.render = (currentElement, newElement) => {
144
+ Idiomorph.morph(currentElement, newElement, {
145
+ morphstyle: 'innerHTML'
146
+ })
147
+ }
148
+ })
149
+
150
+
151
+ &nbsp;
152
+ &nbsp;
153
+ ## Contributing
154
+
155
+ Bug reports and pull requests are welcome on GitHub at https://github.com/cmer/ultimate_turbo_modal.
156
+
157
+ &nbsp;
158
+ &nbsp;
159
+ ## License
160
+
161
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "standard/rake"
5
+
6
+ task default: :standard
@@ -0,0 +1,93 @@
1
+ class UltimateTurboModal::Base < Phlex::HTML
2
+ INCLUDES = %w[
3
+ Turbo::FramesHelper
4
+ Turbo::StreamsHelper
5
+ Phlex::Rails::Helpers::ContentTag
6
+ Phlex::Rails::Helpers::Routes
7
+ Phlex::Rails::Helpers::Tag
8
+ ].freeze
9
+
10
+ # @param padding [Boolean] Whether to add padding around the modal content
11
+ # @param close_button [Boolean] Whether to show a close button.
12
+ # @param advance_history [Boolean] Whether to update the browser history when opening and closing the modal
13
+ # @param advance_history_url [String] Override the URL to use when advancing the history
14
+ # @param request [ActionDispatch::Request] The current Rails request object
15
+ def initialize(padding: true, close_button: true, advance_history: true, advance_history_url: nil, request: nil)
16
+ @padding = padding
17
+ @close_button = close_button
18
+ @advance_history = advance_history
19
+ @advance_history_url = advance_history_url
20
+ @request = request
21
+
22
+ self.class.include Turbo::FramesHelper
23
+ self.class.include Turbo::StreamsHelper
24
+ self.class.include Phlex::Rails::Helpers::ContentTag
25
+ self.class.include Phlex::Rails::Helpers::Routes
26
+ self.class.include Phlex::Rails::Helpers::Tag
27
+ end
28
+
29
+ def template(&)
30
+ if turbo_frame?
31
+ turbo_frame_tag("modal") { modal(&) }
32
+ elsif turbo_stream?
33
+ Turbo::StreamsHelper.turbo_stream_action_tag("update", target: "modal") do
34
+ template { modal(&) }
35
+ end
36
+ else
37
+ modal(&)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ attr_accessor :request
44
+
45
+ def padding?
46
+ !!@padding
47
+ end
48
+
49
+ def advance_history?
50
+ !!@advance_history
51
+ end
52
+
53
+ def close_button?
54
+ !!@close_button
55
+ end
56
+
57
+ def turbo_stream?
58
+ !!request&.format&.turbo_stream?
59
+ end
60
+
61
+ def turbo_frame?
62
+ !!request&.headers&.key?("Turbo-Frame")
63
+ end
64
+
65
+ def turbo?
66
+ turbo_stream? || turbo_frame?
67
+ end
68
+
69
+ def advance_history_url
70
+ return nil unless advance_history?
71
+ @advance_history_url || request.original_url
72
+ end
73
+
74
+ def method_missing(method, *, &block)
75
+ INCLUDES.each { |mod| include_if_defined(mod) }
76
+
77
+ if self.class.method_defined?(method)
78
+ send(method, *, &block)
79
+ else
80
+ super
81
+ end
82
+ end
83
+
84
+ def include_if_defined(mod_str)
85
+ if defined?(mod.constantize) && !self.class.included_modules.include?(mod.constantize)
86
+ self.class.include mod.constantize
87
+ end
88
+ end
89
+
90
+ def respond_to_missing?(method, include_private = false)
91
+ self.class.included_modules.any? { |mod| mod.instance_methods.include?(method) } || super
92
+ end
93
+ end
@@ -0,0 +1,101 @@
1
+ module UltimateTurboModal::Flavors
2
+ class Tailwind < UltimateTurboModal::Base
3
+ private
4
+
5
+ def modal(&)
6
+ div_dialog do
7
+ div_overlay
8
+ div_outer do
9
+ div_inner do
10
+ div_border do
11
+ button_close if close_button?
12
+ yield
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ def div_dialog(&)
20
+ div(id: "modal-container",
21
+ class: "relative z-10",
22
+ role: "dialog",
23
+ aria: {
24
+ labeled_by: "modal-title",
25
+ modal: true
26
+ },
27
+ data: {
28
+ controller: "modal",
29
+ modal_target: "container",
30
+ modal_advance_history_url_value: advance_history_url,
31
+ action: "turbo:submit-end->modal#submitEnd keyup@window->modal#closeWithKeyboard click@window->modal#outsideModalClicked click->modal#outsideModalClicked",
32
+ transition_enter: "ease-out duration-300",
33
+ transition_enter_start: "opacity-0",
34
+ transition_enter_end: "opacity-100",
35
+ transition_leave: "ease-in duration-200",
36
+ transition_leave_start: "opacity-100",
37
+ transition_leave_end: "opacity-0"
38
+ }, &)
39
+ end
40
+
41
+ def div_overlay(&)
42
+ div(id: "modal-overlay",
43
+ class: "fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity",
44
+ data: {
45
+ modal_target: "overlay",
46
+ action: "click->modal#outsideModalClicked"
47
+ })
48
+ end
49
+
50
+ def div_outer(&)
51
+ div(id: "modal-outer",
52
+ class: "fixed inset-0 z-10 overflow-y-auto sm:max-w-[80%] md:max-w-3xl sm:mx-auto m-4",
53
+ data: {
54
+ modal_target: "modal"
55
+ }, &)
56
+ end
57
+
58
+ def div_inner(&)
59
+ div(id: "modal-inner",
60
+ class: "flex min-h-full items-center justify-center p-1 sm:p-4",
61
+ data: {
62
+ modal_target: "innerModal"
63
+ }, &)
64
+ end
65
+
66
+ def div_border(&)
67
+ klass = "relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 sm:max-w-3xl"
68
+ klass = "#{klass} p-2 sm:p-4 md:p-6" if @padding == true
69
+ klass = "#{klass} #{@padding}" if @padding.is_a?(String)
70
+ div(id: "modal-border", class: klass, &)
71
+ end
72
+
73
+ def button_close(&)
74
+ div(class: "absolute top-3 right-3") do
75
+ button(type: "button",
76
+ aria: {label: "close"},
77
+ class: "ml-auto inline-flex items-center rounded bg-transparent p-1 text-sm text-gray-400 bg-white bg-opacity-20 hover:bg-gray-100 hover:bg-opacity-70 hover:text-gray-900",
78
+ data: {
79
+ action: "modal#hideModal"
80
+ }) { icon_close }
81
+ end
82
+ end
83
+
84
+ def icon_close
85
+ svg(
86
+ xmlns: "http://www.w3.org/2000/svg",
87
+ fill: "none",
88
+ viewbox: "0 0 24 24",
89
+ stroke_width: "1.5",
90
+ stroke: "currentColor",
91
+ class: "w-5 h-5"
92
+ ) do |s|
93
+ s.path(
94
+ stroke_linecap: "round",
95
+ stroke_linejoin: "round",
96
+ d: "M6 18L18 6M6 6l12 12"
97
+ )
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,13 @@
1
+ module UltimateTurboModal::Helpers
2
+ module ControllerHelper
3
+ extend ActiveSupport::Concern
4
+
5
+ def inside_modal?
6
+ request.headers["Turbo-Frame"] == "modal"
7
+ end
8
+
9
+ included do
10
+ helper_method :inside_modal?
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module UltimateTurboModal::Helpers
2
+ module StreamHelper
3
+ def modal(message)
4
+ case message.to_s.downcase.to_sym
5
+ when :close, :hide
6
+ turbo_stream_action_tag "modal", message: "hide"
7
+ else
8
+ raise ArgumentError, "Unknown modal message: #{message}"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module UltimateTurboModal::Helpers
2
+ module ViewHelper
3
+ def modal(**)
4
+ render UltimateTurboModal.new(request:, **) do
5
+ yield
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ require "rails"
2
+ require "rails/railtie"
3
+ require "phlex-rails"
4
+ require "turbo-rails"
5
+ require "ultimate_turbo_modal/helpers/controller_helper"
6
+ require "ultimate_turbo_modal/helpers/view_helper"
7
+ require "ultimate_turbo_modal/helpers/stream_helper"
8
+
9
+ module UltimateTurboModal
10
+ class Railtie < Rails::Railtie
11
+ initializer "ultimate_turbo_modal.action_controller" do
12
+ ActiveSupport.on_load(:action_controller) do
13
+ include UltimateTurboModal::Helpers::ControllerHelper
14
+ end
15
+ end
16
+
17
+ initializer "ultimate_turbo_modal.action_view" do
18
+ ActiveSupport.on_load(:action_view) do
19
+ include UltimateTurboModal::Helpers::ViewHelper
20
+ end
21
+ Turbo::Streams::TagBuilder.include(UltimateTurboModal::Helpers::StreamHelper)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UltimateTurboModal
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ultimate_turbo_modal/version"
4
+ require "ultimate_turbo_modal/railtie"
5
+ require "ultimate_turbo_modal/base"
6
+ Dir[File.join(__dir__, "ultimate_turbo_modal/flavors", "*.rb")].sort.each do |file|
7
+ require file
8
+ end
9
+
10
+ module UltimateTurboModal
11
+ extend self
12
+
13
+ DEFAULT_FLAVOR = :tailwind
14
+
15
+ def new(**)
16
+ modal_class.new(**)
17
+ end
18
+
19
+ def modal_class
20
+ "UltimateTurboModal::Flavors::#{flavor.to_s.classify}".constantize
21
+ end
22
+
23
+ def flavor=(flavor)
24
+ @flavor = flavor
25
+ end
26
+
27
+ def flavor
28
+ defined?(@flavor) ? @flavor&.to_sym : DEFAULT_FLAVOR
29
+ end
30
+
31
+ class Error < StandardError; end
32
+ end
@@ -0,0 +1,3 @@
1
+ module UltimateTurboModal
2
+ VERSION: String
3
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ultimate_turbo_modal
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Carl Mercier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-10-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: phlex-rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: rails
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '7'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '7'
47
+ - !ruby/object:Gem::Dependency
48
+ name: stimulus-rails
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: turbo-rails
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ description: An easy-to-use, flexible, and powerful Turbo Modal solution for Rails
76
+ 7+ built with Stimulus.js, Tailwind CSS and Hotwire.
77
+ email:
78
+ - foss@carlmercier.com
79
+ executables: []
80
+ extensions: []
81
+ extra_rdoc_files: []
82
+ files:
83
+ - ".rubocop.yml"
84
+ - ".ruby-version"
85
+ - ".standard.yml"
86
+ - ".tool-versions"
87
+ - CHANGELOG.md
88
+ - Gemfile
89
+ - Gemfile.lock
90
+ - LICENSE.txt
91
+ - README.md
92
+ - Rakefile
93
+ - lib/ultimate_turbo_modal.rb
94
+ - lib/ultimate_turbo_modal/base.rb
95
+ - lib/ultimate_turbo_modal/flavors/tailwind.rb
96
+ - lib/ultimate_turbo_modal/helpers/controller_helper.rb
97
+ - lib/ultimate_turbo_modal/helpers/stream_helper.rb
98
+ - lib/ultimate_turbo_modal/helpers/view_helper.rb
99
+ - lib/ultimate_turbo_modal/railtie.rb
100
+ - lib/ultimate_turbo_modal/version.rb
101
+ - sig/ultimate_turbo_modal.rbs
102
+ homepage: https://github.com/cmer/ultimate_turbo_modal
103
+ licenses:
104
+ - MIT
105
+ metadata:
106
+ homepage_uri: https://github.com/cmer/ultimate_turbo_modal
107
+ source_code_uri: https://github.com/cmer/ultimate_turbo_modal
108
+ changelog_uri: https://github.com/cmer/ultimate_turbo_modal/CHANGELOG.md
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '3.0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubygems_version: 3.4.10
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: UTMR aims to be the be-all and end-all of Turbo Modals.
128
+ test_files: []