ultimate_turbo_modal 3.0.4 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +1 -1
- data/README.md +43 -0
- data/VERSION +1 -1
- data/lib/generators/ultimate_turbo_modal/templates/flavors/tailwind.rb +27 -22
- data/lib/generators/ultimate_turbo_modal/templates/flavors/vanilla.rb +1 -1
- data/lib/ultimate_turbo_modal/base.rb +41 -15
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e94fa20d1e6e7f5cf6939cb0a346af7159673c25dc287c88eaf58b08f65aab8c
|
|
4
|
+
data.tar.gz: 95a8f4beb5bbe52a39bca43a905e2e3bb2723d21e49e719896a318cf6302fcc0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b3acb970be04442953aff94efbbf9f422c36d979a9d54993b697074b853ed2fb801f73d963f82dd7523c93f65728f05a646642398b6531a41a197bb10c98de98
|
|
7
|
+
data.tar.gz: 52e151ca88edc805d9d7d450c9a14ca09d58b7b3bb6ffedbdb40d59b5ea43cd60e4da12d0af70cb5a1570b5479f41601ff06c9d909747374603cd73e7a2761a3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## [3.1.0] - 2026-05-01
|
|
4
|
+
|
|
5
|
+
- Tweaked vertically centering of modals on desktop (≥640px) with a slight optical-center bias, instead of anchoring them near the top.
|
|
6
|
+
- Added support for opening a modal from inside a drawer. See [docs/modal-from-drawer.md](docs/modal-from-drawer.md).
|
|
7
|
+
- Fixed a race in `closeAllDialogs` when a stacked modal redirected off-page, which could leave a dialog open or animations in a bad state.
|
|
8
|
+
|
|
9
|
+
## [3.0.5] - 2026-04-22
|
|
10
|
+
|
|
11
|
+
- Fixed modal being dismissed when a body-appended widget (e.g. flatpickr, Select2, Tippy) opens over its trigger between mousedown and mouseup. The browser fires `click` on the dialog (common ancestor), which was treated as a backdrop dismissal. The dialog now tracks mousedown origin and skips dismissal when the press started inside content.
|
|
12
|
+
|
|
1
13
|
## [3.0.4] - 2026-04-08
|
|
2
14
|
|
|
3
15
|
- Fixed form fields not being auto-focused inside modals due to an unnecessary `invisible` class on the modal inner container.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -203,6 +203,49 @@ Link to it the same way as a modal:
|
|
|
203
203
|
| CSS string | Custom value, e.g. `"500px"` or `"50vw"` |
|
|
204
204
|
|
|
205
205
|
|
|
206
|
+
## Opening a Modal from a Drawer
|
|
207
|
+
|
|
208
|
+
You can open a regular modal that stacks on top of an open drawer. This is built in — no setup or opt-in required.
|
|
209
|
+
|
|
210
|
+
Inside any drawer, link to a modal action with `data-turbo-frame="drawer-modal"`:
|
|
211
|
+
|
|
212
|
+
```erb
|
|
213
|
+
<%= drawer(title: "Notifications") do %>
|
|
214
|
+
<p>Activity list here…</p>
|
|
215
|
+
<%= link_to "Edit preferences",
|
|
216
|
+
edit_preferences_path,
|
|
217
|
+
data: { turbo_frame: "drawer-modal" } %>
|
|
218
|
+
<% end %>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The destination is rendered with the same `modal()` helper you use elsewhere:
|
|
222
|
+
|
|
223
|
+
```erb
|
|
224
|
+
<%= modal(title: "Notification preferences") do %>
|
|
225
|
+
<p>Form here…</p>
|
|
226
|
+
<% end %>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The gem detects the request and renders the modal into a stacked `<dialog>` that sits visually on top of the drawer.
|
|
230
|
+
|
|
231
|
+
### Behavior
|
|
232
|
+
|
|
233
|
+
- **ESC** closes the modal first, then the drawer (native top-layer behavior).
|
|
234
|
+
- **Click outside** the modal closes the modal only; the drawer stays open.
|
|
235
|
+
- **`turbo_stream.modal(:close)`** closes the topmost dialog (the modal).
|
|
236
|
+
- **Form submission with same-page redirect** closes the modal smoothly; the drawer stays.
|
|
237
|
+
- **Form submission with a different-page redirect** closes both dialogs, then navigates.
|
|
238
|
+
- **Closing the drawer** (via close button, ESC after the modal closes, etc.) also tears down any modal opened from it.
|
|
239
|
+
|
|
240
|
+
### Constraints
|
|
241
|
+
|
|
242
|
+
- Modal-from-drawer only. You cannot open a drawer from inside a modal, and you cannot stack a modal on top of another modal. The `drawer-modal` frame is only rendered inside drawers.
|
|
243
|
+
- Stacked modals always force `advance: false` (history is not pushed). All other modal options (overlay, padding, header, footer, etc.) work normally.
|
|
244
|
+
- Both backdrops are drawn when both dialogs have `overlay: true`. Pass `overlay: false` to the inner modal if you don't want the drawer to look slightly darker while the modal is open.
|
|
245
|
+
|
|
246
|
+
For a full lifecycle walkthrough and edge-case notes, see [docs/modal-from-drawer.md](docs/modal-from-drawer.md).
|
|
247
|
+
|
|
248
|
+
|
|
206
249
|
## Features and capabilities
|
|
207
250
|
|
|
208
251
|
- Extremely easy to use
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.0
|
|
1
|
+
3.1.0
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Tailwind CSS v4
|
|
4
|
+
#
|
|
5
|
+
# Group selectors are scoped with the named groups `group/utmr-modal` and
|
|
6
|
+
# `group/utmr-drawer` so that when a modal stacks on top of an open drawer,
|
|
7
|
+
# the inner modal's transition styles don't pick up the outer drawer's
|
|
8
|
+
# `data-entered` state (which would prevent the modal from animating out).
|
|
4
9
|
module UltimateTurboModal::Flavors
|
|
5
10
|
class Tailwind < UltimateTurboModal::Base
|
|
6
|
-
STYLES = "html:has(dialog#modal-container[open]) { overflow: hidden; }"
|
|
11
|
+
STYLES = "html:has(dialog#modal-container[open]), html:has(dialog#modal-container-stacked[open]) { overflow: hidden; }"
|
|
7
12
|
|
|
8
13
|
# Modal constants
|
|
9
14
|
|
|
10
15
|
MODAL_DIALOG_CLASSES = [
|
|
11
|
-
"group",
|
|
16
|
+
"group/utmr-modal",
|
|
12
17
|
# Dialog reset
|
|
13
18
|
"fixed inset-0 p-0 m-0 border-none bg-transparent",
|
|
14
19
|
"max-w-[100vw] max-h-dvh w-full h-full overflow-y-auto",
|
|
@@ -21,23 +26,23 @@ module UltimateTurboModal::Flavors
|
|
|
21
26
|
].join(" ")
|
|
22
27
|
|
|
23
28
|
MODAL_INNER_CLASSES = [
|
|
24
|
-
"flex min-h-full items-start justify-center pt-[10vh] sm:p-4",
|
|
29
|
+
"flex min-h-full items-start justify-center pt-[10vh] sm:items-center sm:pt-0 sm:p-4 sm:pb-[10vh]",
|
|
25
30
|
# Transition
|
|
26
31
|
"transition duration-300 ease-out",
|
|
27
|
-
"group-data-[closing]:duration-200 group-data-[closing]:ease-in",
|
|
32
|
+
"group-data-[closing]/utmr-modal:duration-200 group-data-[closing]/utmr-modal:ease-in",
|
|
28
33
|
# Default state (closed): faded + shifted
|
|
29
34
|
"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95",
|
|
30
35
|
# Entered state
|
|
31
|
-
"group-data-[entered]:opacity-100 group-data-[entered]:translate-y-0 group-data-[entered]:scale-100"
|
|
36
|
+
"group-data-[entered]/utmr-modal:opacity-100 group-data-[entered]/utmr-modal:translate-y-0 group-data-[entered]/utmr-modal:scale-100"
|
|
32
37
|
].join(" ")
|
|
33
38
|
|
|
34
|
-
MODAL_CONTENT_CLASSES = "relative transform max-h-screen overflow-hidden rounded-lg bg-white text-left shadow-lg transition-all sm:
|
|
35
|
-
MODAL_MAIN_CLASSES = "group-data-[padding=true]:p-4 group-data-[padding=true]:pt-2 overflow-y-auto max-h-[75vh]"
|
|
36
|
-
MODAL_HEADER_CLASSES = "flex justify-between items-center w-full py-4 rounded-t dark:border-gray-600 group-data-[header-divider=true]:border-b group-data-[header=false]:absolute"
|
|
39
|
+
MODAL_CONTENT_CLASSES = "relative transform max-h-screen overflow-hidden rounded-lg bg-white text-left shadow-lg transition-all sm:max-w-3xl dark:bg-gray-800 dark:text-white"
|
|
40
|
+
MODAL_MAIN_CLASSES = "group-data-[padding=true]/utmr-modal:p-4 group-data-[padding=true]/utmr-modal:pt-2 overflow-y-auto max-h-[75vh]"
|
|
41
|
+
MODAL_HEADER_CLASSES = "flex justify-between items-center w-full py-4 rounded-t dark:border-gray-600 group-data-[header-divider=true]/utmr-modal:border-b group-data-[header=false]/utmr-modal:absolute"
|
|
37
42
|
MODAL_TITLE_CLASSES = "pl-4"
|
|
38
|
-
MODAL_TITLE_H_CLASSES = "group-data-[title=false]:hidden text-lg font-semibold text-gray-900 dark:text-white"
|
|
39
|
-
MODAL_FOOTER_CLASSES = "flex p-4 rounded-b dark:border-gray-600 group-data-[footer-divider=true]:border-t"
|
|
40
|
-
MODAL_CLOSE_CLASSES = "mr-4 group-data-[close-button=false]:hidden"
|
|
43
|
+
MODAL_TITLE_H_CLASSES = "group-data-[title=false]/utmr-modal:hidden text-lg font-semibold text-gray-900 dark:text-white"
|
|
44
|
+
MODAL_FOOTER_CLASSES = "flex p-4 rounded-b dark:border-gray-600 group-data-[footer-divider=true]/utmr-modal:border-t"
|
|
45
|
+
MODAL_CLOSE_CLASSES = "mr-4 group-data-[close-button=false]/utmr-modal:hidden"
|
|
41
46
|
MODAL_CLOSE_SR_CLASSES = "sr-only"
|
|
42
47
|
MODAL_CLOSE_BUTTON_CLASSES = "text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"
|
|
43
48
|
MODAL_CLOSE_ICON_CLASSES = "w-5 h-5"
|
|
@@ -45,7 +50,7 @@ module UltimateTurboModal::Flavors
|
|
|
45
50
|
# Drawer constants
|
|
46
51
|
|
|
47
52
|
DRAWER_DIALOG_CLASSES = [
|
|
48
|
-
"group",
|
|
53
|
+
"group/utmr-drawer",
|
|
49
54
|
# Dialog reset
|
|
50
55
|
"fixed inset-0 p-0 m-0 border-none bg-transparent",
|
|
51
56
|
"max-w-[100vw] max-h-dvh w-full h-full overflow-y-auto",
|
|
@@ -75,30 +80,30 @@ module UltimateTurboModal::Flavors
|
|
|
75
80
|
DRAWER_PANEL_CLASSES = [
|
|
76
81
|
"absolute inset-y-0",
|
|
77
82
|
# Position based on direction
|
|
78
|
-
"group-data-[drawer=left]:left-0 group-data-[drawer=right]:right-0",
|
|
83
|
+
"group-data-[drawer=left]/utmr-drawer:left-0 group-data-[drawer=right]/utmr-drawer:right-0",
|
|
79
84
|
# Width (size variable + gutter)
|
|
80
85
|
"w-[min(var(--utmr-w),calc(100vw_-_var(--utmr-gutter)))]",
|
|
81
86
|
# Default: translated off-screen
|
|
82
87
|
"[translate:var(--utmr-hide)]",
|
|
83
88
|
# Entered: in place
|
|
84
|
-
"group-data-[entered]:[translate:0]",
|
|
89
|
+
"group-data-[entered]/utmr-drawer:[translate:0]",
|
|
85
90
|
# Closing: back off-screen
|
|
86
|
-
"group-data-[closing]:[translate:var(--utmr-hide)]",
|
|
91
|
+
"group-data-[closing]/utmr-drawer:[translate:var(--utmr-hide)]",
|
|
87
92
|
# Transition
|
|
88
93
|
"transition-[translate] duration-250 ease-in-out sm:duration-400",
|
|
89
94
|
"will-change-[translate]",
|
|
90
95
|
# Hidden before animation ready
|
|
91
|
-
"group-[&:not([data-enter-ready]):not([data-entered])]:invisible"
|
|
96
|
+
"group-[&:not([data-enter-ready]):not([data-entered])]/utmr-drawer:invisible"
|
|
92
97
|
].join(" ")
|
|
93
98
|
|
|
94
|
-
DRAWER_CONTENT_CLASSES = "relative flex h-full w-full flex-col bg-white group-data-[padding=true]:pt-6 shadow-xl dark:bg-gray-800 dark:text-white"
|
|
99
|
+
DRAWER_CONTENT_CLASSES = "relative flex h-full w-full flex-col bg-white group-data-[padding=true]/utmr-drawer:pt-6 shadow-xl dark:bg-gray-800 dark:text-white"
|
|
95
100
|
|
|
96
|
-
DRAWER_HEADER_CLASSES = "flex items-start justify-between w-full px-4 sm:px-6 group-data-[header-divider=true]:pb-4 group-data-[header-divider=true]:border-b group-data-[header-divider=true]:border-gray-200 dark:group-data-[header-divider=true]:border-gray-600 group-data-[header=false]:hidden"
|
|
101
|
+
DRAWER_HEADER_CLASSES = "flex items-start justify-between w-full px-4 sm:px-6 group-data-[header-divider=true]/utmr-drawer:pb-4 group-data-[header-divider=true]/utmr-drawer:border-b group-data-[header-divider=true]/utmr-drawer:border-gray-200 dark:group-data-[header-divider=true]/utmr-drawer:border-gray-600 group-data-[header=false]/utmr-drawer:hidden"
|
|
97
102
|
DRAWER_TITLE_CLASSES = ""
|
|
98
|
-
DRAWER_TITLE_H_CLASSES = "group-data-[title=false]:hidden text-base font-semibold text-gray-900 dark:text-white"
|
|
99
|
-
DRAWER_MAIN_CLASSES = "relative group-data-[padding=true]:mt-6 flex-1 overflow-y-auto group-data-[padding=true]:px-4 group-data-[padding=true]:sm:px-6 group-data-[padding=true]:pb-6"
|
|
100
|
-
DRAWER_FOOTER_CLASSES = "flex shrink-0 px-4 py-4 sm:px-6 group-data-[footer-divider=true]:border-t group-data-[footer-divider=true]:border-gray-200 dark:group-data-[footer-divider=true]:border-gray-600"
|
|
101
|
-
DRAWER_CLOSE_CLASSES = "ml-3 flex h-7 items-center group-data-[close-button=false]:hidden"
|
|
103
|
+
DRAWER_TITLE_H_CLASSES = "group-data-[title=false]/utmr-drawer:hidden text-base font-semibold text-gray-900 dark:text-white"
|
|
104
|
+
DRAWER_MAIN_CLASSES = "relative group-data-[padding=true]/utmr-drawer:mt-6 flex-1 overflow-y-auto group-data-[padding=true]/utmr-drawer:px-4 group-data-[padding=true]/utmr-drawer:sm:px-6 group-data-[padding=true]/utmr-drawer:pb-6"
|
|
105
|
+
DRAWER_FOOTER_CLASSES = "flex shrink-0 px-4 py-4 sm:px-6 group-data-[footer-divider=true]/utmr-drawer:border-t group-data-[footer-divider=true]/utmr-drawer:border-gray-200 dark:group-data-[footer-divider=true]/utmr-drawer:border-gray-600"
|
|
106
|
+
DRAWER_CLOSE_CLASSES = "ml-3 flex h-7 items-center group-data-[close-button=false]/utmr-drawer:hidden"
|
|
102
107
|
DRAWER_CLOSE_BUTTON_CLASSES = "relative rounded-md text-gray-400 hover:text-gray-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
|
103
108
|
DRAWER_CLOSE_SR_CLASSES = MODAL_CLOSE_SR_CLASSES
|
|
104
109
|
DRAWER_CLOSE_ICON_CLASSES = "size-6"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# Set STYLES to a CSS string if you need additional inline styles.
|
|
6
6
|
module UltimateTurboModal::Flavors
|
|
7
7
|
class Vanilla < UltimateTurboModal::Base
|
|
8
|
-
STYLES = "html:has(dialog#modal-container[open]) { overflow: hidden; }"
|
|
8
|
+
STYLES = "html:has(dialog#modal-container[open]), html:has(dialog#modal-container-stacked[open]) { overflow: hidden; }"
|
|
9
9
|
|
|
10
10
|
MODAL_DIALOG_CLASSES = "modal-container"
|
|
11
11
|
MODAL_INNER_CLASSES = "modal-inner"
|
|
@@ -39,6 +39,9 @@ class UltimateTurboModal::Base < Phlex::HTML
|
|
|
39
39
|
request: nil, title: nil
|
|
40
40
|
)
|
|
41
41
|
@drawer = drawer_position
|
|
42
|
+
@request = request
|
|
43
|
+
|
|
44
|
+
raise ArgumentError, "Cannot render a drawer into the drawer-modal frame (drawers cannot be opened from inside another drawer or modal)" if drawer? && stacked?
|
|
42
45
|
|
|
43
46
|
if drawer?
|
|
44
47
|
cfg = UltimateTurboModal.configuration.drawer_config
|
|
@@ -60,11 +63,15 @@ class UltimateTurboModal::Base < Phlex::HTML
|
|
|
60
63
|
@overlay = overlay.nil? ? cfg.overlay : overlay
|
|
61
64
|
@padding = padding.nil? ? cfg.padding : padding
|
|
62
65
|
|
|
66
|
+
if stacked?
|
|
67
|
+
@advance = false
|
|
68
|
+
@advance_url = nil
|
|
69
|
+
end
|
|
70
|
+
|
|
63
71
|
@allowed_click_outside_selector = allowed_click_outside_selector
|
|
64
72
|
@close_button_data_action = close_button_data_action
|
|
65
73
|
@close_button_sr_label = close_button_sr_label
|
|
66
74
|
@content_div_data = content_div_data
|
|
67
|
-
@request = request
|
|
68
75
|
@title = title
|
|
69
76
|
|
|
70
77
|
self.class.include_turbo_helpers
|
|
@@ -84,7 +91,7 @@ class UltimateTurboModal::Base < Phlex::HTML
|
|
|
84
91
|
|
|
85
92
|
def view_template(&block)
|
|
86
93
|
if turbo_frame?
|
|
87
|
-
turbo_frame_tag(
|
|
94
|
+
turbo_frame_tag(turbo_frame_name) do
|
|
88
95
|
drawer? ? render_drawer(&block) : render_modal(&block)
|
|
89
96
|
end
|
|
90
97
|
else
|
|
@@ -92,6 +99,10 @@ class UltimateTurboModal::Base < Phlex::HTML
|
|
|
92
99
|
end
|
|
93
100
|
end
|
|
94
101
|
|
|
102
|
+
def turbo_frame_name
|
|
103
|
+
stacked? ? "drawer-modal" : "modal"
|
|
104
|
+
end
|
|
105
|
+
|
|
95
106
|
def title(&block)
|
|
96
107
|
@title_block = block
|
|
97
108
|
end
|
|
@@ -145,6 +156,16 @@ class UltimateTurboModal::Base < Phlex::HTML
|
|
|
145
156
|
|
|
146
157
|
def drawer? = !!@drawer
|
|
147
158
|
|
|
159
|
+
def stacked? = request&.headers&.[]("Turbo-Frame") == "drawer-modal"
|
|
160
|
+
|
|
161
|
+
def dialog_id = stacked? ? "modal-container-stacked" : "modal-container"
|
|
162
|
+
|
|
163
|
+
def inner_id = stacked? ? "modal-inner-stacked" : "modal-inner"
|
|
164
|
+
|
|
165
|
+
# Suffix inner ids so they don't collide with the drawer's ids when the
|
|
166
|
+
# stacked modal is rendered inside the drawer's DOM.
|
|
167
|
+
def scoped_id(name) = stacked? ? "#{name}-stacked" : name
|
|
168
|
+
|
|
148
169
|
def drawer_position = @drawer || :right
|
|
149
170
|
|
|
150
171
|
def overlay? = !!@overlay
|
|
@@ -236,7 +257,7 @@ class UltimateTurboModal::Base < Phlex::HTML
|
|
|
236
257
|
modal_target: "container",
|
|
237
258
|
modal_advance_url_value: advance_url,
|
|
238
259
|
modal_allowed_click_outside_selector_value: allowed_click_outside_selector,
|
|
239
|
-
action: "turbo:submit-end->modal#submitEnd cancel->modal#cancelEvent click->modal#dialogClicked",
|
|
260
|
+
action: "turbo:submit-end->modal#submitEnd cancel->modal#cancelEvent mousedown->modal#dialogMousedown click->modal#dialogClicked",
|
|
240
261
|
padding: padding?.to_s,
|
|
241
262
|
title: title?.to_s,
|
|
242
263
|
header: header?.to_s,
|
|
@@ -262,11 +283,11 @@ class UltimateTurboModal::Base < Phlex::HTML
|
|
|
262
283
|
inline_style = "--utmr-w: #{@drawer_size}"
|
|
263
284
|
end
|
|
264
285
|
|
|
265
|
-
dialog(id:
|
|
286
|
+
dialog(id: dialog_id,
|
|
266
287
|
class: dialog_classes,
|
|
267
288
|
style: inline_style,
|
|
268
289
|
aria: {
|
|
269
|
-
labelledby: "modal-title-h"
|
|
290
|
+
labelledby: scoped_id("modal-title-h")
|
|
270
291
|
},
|
|
271
292
|
data: data_attributes, &block)
|
|
272
293
|
end
|
|
@@ -274,14 +295,14 @@ class UltimateTurboModal::Base < Phlex::HTML
|
|
|
274
295
|
## Modal-specific elements
|
|
275
296
|
|
|
276
297
|
def modal_inner(&block)
|
|
277
|
-
maybe_turbo_frame(
|
|
278
|
-
div(id:
|
|
298
|
+
maybe_turbo_frame(inner_id) do
|
|
299
|
+
div(id: inner_id, class: self.class::MODAL_INNER_CLASSES, &block)
|
|
279
300
|
end
|
|
280
301
|
end
|
|
281
302
|
|
|
282
303
|
def modal_content(&block)
|
|
283
304
|
data = (content_div_data || {}).merge({modal_target: "content"})
|
|
284
|
-
div(id: "modal-content", class: self.class::MODAL_CONTENT_CLASSES, data: data, &block)
|
|
305
|
+
div(id: scoped_id("modal-content"), class: self.class::MODAL_CONTENT_CLASSES, data: data, &block)
|
|
285
306
|
end
|
|
286
307
|
|
|
287
308
|
def modal_main(&block) = render_main(&block)
|
|
@@ -294,7 +315,12 @@ class UltimateTurboModal::Base < Phlex::HTML
|
|
|
294
315
|
|
|
295
316
|
def drawer_wrapper(&block)
|
|
296
317
|
maybe_turbo_frame("modal-inner") do
|
|
297
|
-
div(id: "drawer-wrapper", class: self.class::DRAWER_WRAPPER_CLASSES
|
|
318
|
+
div(id: "drawer-wrapper", class: self.class::DRAWER_WRAPPER_CLASSES) do
|
|
319
|
+
yield
|
|
320
|
+
# Empty frame so links inside the drawer can target a stacked modal
|
|
321
|
+
# via data-turbo-frame="drawer-modal". Always rendered.
|
|
322
|
+
turbo_frame_tag("drawer-modal")
|
|
323
|
+
end
|
|
298
324
|
end
|
|
299
325
|
end
|
|
300
326
|
|
|
@@ -320,34 +346,34 @@ class UltimateTurboModal::Base < Phlex::HTML
|
|
|
320
346
|
## Shared rendering
|
|
321
347
|
|
|
322
348
|
def render_main(&block)
|
|
323
|
-
div(id: "modal-main", class: classes_for("MAIN_CLASSES"), &block)
|
|
349
|
+
div(id: scoped_id("modal-main"), class: classes_for("MAIN_CLASSES"), &block)
|
|
324
350
|
end
|
|
325
351
|
|
|
326
352
|
def render_header
|
|
327
|
-
div(id: "modal-header", class: classes_for("HEADER_CLASSES")) do
|
|
353
|
+
div(id: scoped_id("modal-header"), class: classes_for("HEADER_CLASSES")) do
|
|
328
354
|
render_title
|
|
329
355
|
drawer? ? drawer_close : modal_close
|
|
330
356
|
end
|
|
331
357
|
end
|
|
332
358
|
|
|
333
359
|
def render_title
|
|
334
|
-
div(id: "modal-title", class: classes_for("TITLE_CLASSES")) do
|
|
360
|
+
div(id: scoped_id("modal-title"), class: classes_for("TITLE_CLASSES")) do
|
|
335
361
|
if @title_block.present?
|
|
336
362
|
render @title_block
|
|
337
363
|
else
|
|
338
|
-
h3(id: "modal-title-h", class: classes_for("TITLE_H_CLASSES")) { @title }
|
|
364
|
+
h3(id: scoped_id("modal-title-h"), class: classes_for("TITLE_H_CLASSES")) { @title }
|
|
339
365
|
end
|
|
340
366
|
end
|
|
341
367
|
end
|
|
342
368
|
|
|
343
369
|
def render_footer
|
|
344
|
-
div(id: "modal-footer", class: classes_for("FOOTER_CLASSES")) do
|
|
370
|
+
div(id: scoped_id("modal-footer"), class: classes_for("FOOTER_CLASSES")) do
|
|
345
371
|
render @footer
|
|
346
372
|
end
|
|
347
373
|
end
|
|
348
374
|
|
|
349
375
|
def render_close
|
|
350
|
-
div(id: "modal-close", class: classes_for("CLOSE_CLASSES")) do
|
|
376
|
+
div(id: scoped_id("modal-close"), class: classes_for("CLOSE_CLASSES")) do
|
|
351
377
|
close_button_tag(classes_for("CLOSE_BUTTON_CLASSES")) do
|
|
352
378
|
yield if block_given?
|
|
353
379
|
close_icon_svg(classes_for("CLOSE_ICON_CLASSES"))
|