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 +7 -0
- data/.rubocop.yml +6 -0
- data/.ruby-version +1 -0
- data/.standard.yml +9 -0
- data/.tool-versions +2 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +261 -0
- data/LICENSE.txt +21 -0
- data/README.md +161 -0
- data/Rakefile +6 -0
- data/lib/ultimate_turbo_modal/base.rb +93 -0
- data/lib/ultimate_turbo_modal/flavors/tailwind.rb +101 -0
- data/lib/ultimate_turbo_modal/helpers/controller_helper.rb +13 -0
- data/lib/ultimate_turbo_modal/helpers/stream_helper.rb +12 -0
- data/lib/ultimate_turbo_modal/helpers/view_helper.rb +9 -0
- data/lib/ultimate_turbo_modal/railtie.rb +24 -0
- data/lib/ultimate_turbo_modal/version.rb +5 -0
- data/lib/ultimate_turbo_modal.rb +32 -0
- data/sig/ultimate_turbo_modal.rbs +3 -0
- metadata +128 -0
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
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-3.2.0
|
data/.standard.yml
ADDED
data/.tool-versions
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
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
|
+
|
13
|
+
|
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
|
+
|
34
|
+
|
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
|
+
|
41
|
+
|
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
|
+
|
72
|
+
|
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
|
+
|
102
|
+
|
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
|
+
|
152
|
+
|
153
|
+
## Contributing
|
154
|
+
|
155
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/cmer/ultimate_turbo_modal.
|
156
|
+
|
157
|
+
|
158
|
+
|
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,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,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,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,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
|
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: []
|