ultimate_turbo_modal 3.2.1 → 3.4.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 +13 -0
- data/Gemfile.lock +1 -1
- data/README.md +248 -0
- data/VERSION +1 -1
- data/app/assets/stylesheets/ultimate_turbo_modal.css +610 -0
- data/lib/generators/ultimate_turbo_modal/base.rb +44 -0
- data/lib/generators/ultimate_turbo_modal/install_generator.rb +8 -0
- data/lib/generators/ultimate_turbo_modal/templates/flavors/custom.rb +10 -1
- data/lib/generators/ultimate_turbo_modal/templates/flavors/tailwind.rb +78 -4
- data/lib/generators/ultimate_turbo_modal/templates/flavors/vanilla.rb +14 -1
- data/lib/generators/ultimate_turbo_modal/templates/ultimate_turbo_modal.rb +18 -0
- data/lib/generators/ultimate_turbo_modal/update_generator.rb +2 -0
- data/lib/ultimate_turbo_modal/base.rb +172 -26
- data/lib/ultimate_turbo_modal/configuration.rb +45 -5
- data/lib/ultimate_turbo_modal/helpers/view_helper.rb +31 -0
- data/lib/ultimate_turbo_modal/railtie.rb +23 -0
- data/lib/ultimate_turbo_modal.rb +4 -0
- metadata +2 -1
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
/* ========================================
|
|
2
|
+
All styles scoped to dialog.utmr to
|
|
3
|
+
prevent conflicts with end-user CSS.
|
|
4
|
+
======================================== */
|
|
5
|
+
|
|
6
|
+
dialog.utmr {
|
|
7
|
+
.hidden {
|
|
8
|
+
display: none;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.sr-only {
|
|
12
|
+
position: absolute;
|
|
13
|
+
width: 1px;
|
|
14
|
+
height: 1px;
|
|
15
|
+
padding: 0;
|
|
16
|
+
margin: -1px;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
clip: rect(0, 0, 0, 0);
|
|
19
|
+
white-space: nowrap;
|
|
20
|
+
border-width: 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* ========================================
|
|
24
|
+
Dialog reset (shared modal + drawer)
|
|
25
|
+
======================================== */
|
|
26
|
+
|
|
27
|
+
&.modal-container,
|
|
28
|
+
&.drawer-container {
|
|
29
|
+
position: fixed;
|
|
30
|
+
inset: 0;
|
|
31
|
+
padding: 0;
|
|
32
|
+
margin: 0;
|
|
33
|
+
border: none;
|
|
34
|
+
background: transparent;
|
|
35
|
+
max-width: 100vw;
|
|
36
|
+
max-height: 100dvh;
|
|
37
|
+
width: 100%;
|
|
38
|
+
height: 100%;
|
|
39
|
+
overflow-y: auto;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ========================================
|
|
43
|
+
Modal styles
|
|
44
|
+
======================================== */
|
|
45
|
+
|
|
46
|
+
/* Backdrop transitions */
|
|
47
|
+
&.modal-container::backdrop {
|
|
48
|
+
opacity: 0;
|
|
49
|
+
transition: opacity 300ms ease-out;
|
|
50
|
+
}
|
|
51
|
+
&.modal-container[data-overlay="true"]::backdrop {
|
|
52
|
+
background-color: rgba(17, 24, 39, 0.7);
|
|
53
|
+
}
|
|
54
|
+
&.modal-container[data-overlay="false"]::backdrop {
|
|
55
|
+
background-color: transparent;
|
|
56
|
+
}
|
|
57
|
+
&.modal-container[data-overlay="true"][data-entered]::backdrop {
|
|
58
|
+
opacity: 1;
|
|
59
|
+
}
|
|
60
|
+
&.modal-container[data-closing]::backdrop {
|
|
61
|
+
transition-duration: 200ms;
|
|
62
|
+
transition-timing-function: ease-in;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Modal enter/leave transitions */
|
|
66
|
+
&.modal-container:not([data-enter-ready]):not([data-entered]) .modal-inner {
|
|
67
|
+
visibility: hidden;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.modal-inner {
|
|
71
|
+
display: flex;
|
|
72
|
+
padding: 0.25rem;
|
|
73
|
+
padding-top: 10vh;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
align-items: flex-start;
|
|
76
|
+
min-height: 100%;
|
|
77
|
+
opacity: 0;
|
|
78
|
+
translate: 0 1rem;
|
|
79
|
+
transition: opacity 300ms ease-out, translate 300ms ease-out, scale 300ms ease-out;
|
|
80
|
+
|
|
81
|
+
@media (min-width: 640px) {
|
|
82
|
+
padding: 1rem;
|
|
83
|
+
padding-bottom: 10vh;
|
|
84
|
+
align-items: center;
|
|
85
|
+
translate: none;
|
|
86
|
+
scale: 0.95;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
&.modal-container[data-entered] .modal-inner {
|
|
91
|
+
opacity: 1;
|
|
92
|
+
translate: none;
|
|
93
|
+
scale: 1;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&.modal-container[data-closing] .modal-inner {
|
|
97
|
+
transition-duration: 200ms;
|
|
98
|
+
transition-timing-function: ease-in;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Modal conditional styling via data attributes */
|
|
102
|
+
&.modal-container {
|
|
103
|
+
&[data-header="false"] .modal-header {
|
|
104
|
+
position: absolute;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
&[data-header-divider="true"] .modal-header {
|
|
108
|
+
border-bottom-width: 1px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
&[data-footer-divider="true"] .modal-footer {
|
|
112
|
+
border-top-width: 1px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
&[data-padding="true"] .modal-main {
|
|
116
|
+
padding: 1rem;
|
|
117
|
+
padding-top: 0.5rem;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.modal-main {
|
|
121
|
+
overflow-y: auto;
|
|
122
|
+
max-height: 75vh;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&[data-title="false"] .modal-title-h {
|
|
126
|
+
display: none;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&[data-close-button="false"] .modal-close {
|
|
130
|
+
display: none;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.modal-content {
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
position: relative;
|
|
137
|
+
background-color: #ffffff;
|
|
138
|
+
transition-property: all;
|
|
139
|
+
text-align: left;
|
|
140
|
+
border-radius: 0.5rem;
|
|
141
|
+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
|
142
|
+
max-height: 100vh;
|
|
143
|
+
|
|
144
|
+
@media (min-width: 640px) {
|
|
145
|
+
max-width: 48rem;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.modal-header {
|
|
150
|
+
display: flex;
|
|
151
|
+
padding-top: 1rem;
|
|
152
|
+
padding-bottom: 1rem;
|
|
153
|
+
justify-content: space-between;
|
|
154
|
+
align-items: center;
|
|
155
|
+
width: 100%;
|
|
156
|
+
border-top-left-radius: 0.25rem;
|
|
157
|
+
border-top-right-radius: 0.25rem;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.modal-title {
|
|
161
|
+
line-height: 1.75rem;
|
|
162
|
+
font-weight: 600;
|
|
163
|
+
padding-left: 1rem;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.modal-footer {
|
|
167
|
+
display: flex;
|
|
168
|
+
padding: 1rem;
|
|
169
|
+
border-bottom-right-radius: 0.25rem;
|
|
170
|
+
border-bottom-left-radius: 0.25rem;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.modal-close {
|
|
174
|
+
margin-right: 1rem;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.modal-close-button {
|
|
178
|
+
display: inline-flex;
|
|
179
|
+
padding: 0.375rem;
|
|
180
|
+
margin-left: auto;
|
|
181
|
+
background-color: transparent;
|
|
182
|
+
font-size: 0.875rem;
|
|
183
|
+
line-height: 1.25rem;
|
|
184
|
+
align-items: center;
|
|
185
|
+
border-radius: 0.5rem;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.modal-close-icon {
|
|
189
|
+
width: 1.25rem;
|
|
190
|
+
height: 1.25rem;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* ========================================
|
|
194
|
+
Confirm dialog styles
|
|
195
|
+
Scoped to [data-utmr-confirm] so it can reuse the modal container,
|
|
196
|
+
backdrop and enter/leave transitions while overriding the layout.
|
|
197
|
+
======================================== */
|
|
198
|
+
|
|
199
|
+
&[data-utmr-confirm] {
|
|
200
|
+
/* Real gutter on mobile: the card below is full width. */
|
|
201
|
+
.modal-inner {
|
|
202
|
+
padding: 1rem;
|
|
203
|
+
padding-top: 10vh;
|
|
204
|
+
|
|
205
|
+
@media (min-width: 640px) {
|
|
206
|
+
padding: 1rem;
|
|
207
|
+
padding-bottom: 10vh;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* Sized rather than shrink-to-fit, so a one-word prompt and a three-line
|
|
212
|
+
one come out the same. The floor is written as min(20rem, 100%) so it can
|
|
213
|
+
never push the card wider than the space it has on a narrow screen. */
|
|
214
|
+
.confirm-content {
|
|
215
|
+
position: relative;
|
|
216
|
+
overflow: hidden;
|
|
217
|
+
width: 100%;
|
|
218
|
+
min-width: min(20rem, 100%);
|
|
219
|
+
background-color: #ffffff;
|
|
220
|
+
text-align: left;
|
|
221
|
+
border-radius: 0.5rem;
|
|
222
|
+
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
223
|
+
|
|
224
|
+
@media (min-width: 640px) {
|
|
225
|
+
max-width: 28rem;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.confirm-header {
|
|
230
|
+
display: flex;
|
|
231
|
+
align-items: flex-start;
|
|
232
|
+
justify-content: space-between;
|
|
233
|
+
gap: 1rem;
|
|
234
|
+
width: 100%;
|
|
235
|
+
padding: 1.5rem 1.5rem 0;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.confirm-title-h {
|
|
239
|
+
font-size: 1rem;
|
|
240
|
+
line-height: 1.5rem;
|
|
241
|
+
font-weight: 600;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.confirm-main {
|
|
245
|
+
padding: 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.confirm-body {
|
|
249
|
+
font-size: 0.875rem;
|
|
250
|
+
line-height: 1.5rem;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.confirm-footer {
|
|
254
|
+
padding: 1.5rem;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* Stacked and full width on mobile, inline and right aligned from 640px.
|
|
258
|
+
Reversed so the primary action sits on top. */
|
|
259
|
+
.confirm-actions {
|
|
260
|
+
display: flex;
|
|
261
|
+
width: 100%;
|
|
262
|
+
flex-direction: column-reverse;
|
|
263
|
+
gap: 0.5rem;
|
|
264
|
+
|
|
265
|
+
@media (min-width: 640px) {
|
|
266
|
+
flex-direction: row;
|
|
267
|
+
justify-content: flex-end;
|
|
268
|
+
gap: 0.75rem;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.confirm-cancel,
|
|
273
|
+
.confirm-accept {
|
|
274
|
+
display: inline-flex;
|
|
275
|
+
width: 100%;
|
|
276
|
+
justify-content: center;
|
|
277
|
+
align-items: center;
|
|
278
|
+
padding: 0.625rem 1rem;
|
|
279
|
+
font-size: 0.875rem;
|
|
280
|
+
line-height: 1.25rem;
|
|
281
|
+
font-weight: 600;
|
|
282
|
+
border-radius: 0.375rem;
|
|
283
|
+
cursor: pointer;
|
|
284
|
+
transition: background-color 150ms ease-in-out;
|
|
285
|
+
|
|
286
|
+
@media (min-width: 640px) {
|
|
287
|
+
width: auto;
|
|
288
|
+
padding: 0.5rem 1rem;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.confirm-cancel {
|
|
293
|
+
background-color: #ffffff;
|
|
294
|
+
color: #111827;
|
|
295
|
+
border: 1px solid #D1D5DB;
|
|
296
|
+
|
|
297
|
+
&:hover {
|
|
298
|
+
background-color: #F9FAFB;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.confirm-accept {
|
|
303
|
+
background-color: #4F46E5;
|
|
304
|
+
color: #ffffff;
|
|
305
|
+
border: 1px solid transparent;
|
|
306
|
+
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
307
|
+
|
|
308
|
+
&:hover {
|
|
309
|
+
background-color: #6366F1;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Destructive variant, set from `variant: :danger` */
|
|
314
|
+
&[data-utmr-confirm-variant="danger"] .confirm-accept {
|
|
315
|
+
background-color: #DC2626;
|
|
316
|
+
|
|
317
|
+
&:hover {
|
|
318
|
+
background-color: #EF4444;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* Options, matching the modal's data-attribute switches */
|
|
323
|
+
&[data-header="false"] .confirm-header {
|
|
324
|
+
display: none;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
&[data-title="false"] .confirm-title-h {
|
|
328
|
+
display: none;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
&[data-padding="true"] .confirm-main {
|
|
332
|
+
padding: 0.5rem 1.5rem 0;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
&[data-header-divider="true"] .confirm-header {
|
|
336
|
+
padding-bottom: 1rem;
|
|
337
|
+
border-bottom-width: 1px;
|
|
338
|
+
border-bottom-style: solid;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
&[data-footer-divider="true"] .confirm-footer {
|
|
342
|
+
border-top-width: 1px;
|
|
343
|
+
border-top-style: solid;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* ========================================
|
|
348
|
+
Drawer styles
|
|
349
|
+
======================================== */
|
|
350
|
+
|
|
351
|
+
/* Drawer backdrop transitions */
|
|
352
|
+
&.drawer-container::backdrop {
|
|
353
|
+
background: transparent;
|
|
354
|
+
opacity: 0;
|
|
355
|
+
transition: opacity 300ms ease-out;
|
|
356
|
+
}
|
|
357
|
+
&.drawer-container[data-overlay="true"]::backdrop {
|
|
358
|
+
background-color: rgba(17, 24, 39, 0.7);
|
|
359
|
+
}
|
|
360
|
+
&.drawer-container[data-overlay="true"][data-entered]::backdrop {
|
|
361
|
+
opacity: 1;
|
|
362
|
+
}
|
|
363
|
+
&.drawer-container[data-closing]::backdrop {
|
|
364
|
+
transition-duration: 200ms;
|
|
365
|
+
transition-timing-function: ease-in;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* Drawer sizing via data attributes */
|
|
369
|
+
&.drawer-container {
|
|
370
|
+
--utmr-gutter: 2.5rem;
|
|
371
|
+
|
|
372
|
+
@media (min-width: 640px) {
|
|
373
|
+
--utmr-gutter: 4rem;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
&.drawer-container[data-drawer-size="xs"] { --utmr-w: 16rem; }
|
|
377
|
+
&.drawer-container[data-drawer-size="sm"] { --utmr-w: 20rem; }
|
|
378
|
+
&.drawer-container[data-drawer-size="md"],
|
|
379
|
+
&.drawer-container[data-drawer-size=""] { --utmr-w: 24rem; }
|
|
380
|
+
&.drawer-container[data-drawer-size="lg"] { --utmr-w: 28rem; }
|
|
381
|
+
&.drawer-container[data-drawer-size="xl"] { --utmr-w: 42rem; }
|
|
382
|
+
&.drawer-container[data-drawer-size="2xl"] { --utmr-w: 56rem; }
|
|
383
|
+
&.drawer-container[data-drawer-size="full"] { --utmr-w: 100vw; }
|
|
384
|
+
|
|
385
|
+
/* Drawer direction */
|
|
386
|
+
&.drawer-container[data-drawer="left"] { --utmr-hide: -100% 0; }
|
|
387
|
+
&.drawer-container[data-drawer="left"] .drawer-panel { left: 0; }
|
|
388
|
+
&.drawer-container[data-drawer="right"] { --utmr-hide: 100% 0; }
|
|
389
|
+
&.drawer-container[data-drawer="right"] .drawer-panel { right: 0; }
|
|
390
|
+
|
|
391
|
+
/* Drawer panel transitions */
|
|
392
|
+
.drawer-panel {
|
|
393
|
+
position: absolute;
|
|
394
|
+
top: 0;
|
|
395
|
+
bottom: 0;
|
|
396
|
+
height: 100%;
|
|
397
|
+
width: min(var(--utmr-w), calc(100vw - var(--utmr-gutter)));
|
|
398
|
+
translate: var(--utmr-hide);
|
|
399
|
+
transition: translate 250ms ease-in-out;
|
|
400
|
+
will-change: translate;
|
|
401
|
+
|
|
402
|
+
@media (min-width: 640px) {
|
|
403
|
+
transition-duration: 400ms;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
&.drawer-container:not([data-enter-ready]):not([data-entered]) .drawer-panel {
|
|
407
|
+
visibility: hidden;
|
|
408
|
+
}
|
|
409
|
+
&.drawer-container[data-entered] .drawer-panel {
|
|
410
|
+
translate: 0;
|
|
411
|
+
}
|
|
412
|
+
&.drawer-container[data-closing] .drawer-panel {
|
|
413
|
+
translate: var(--utmr-hide);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/* Drawer conditional styling via data attributes */
|
|
417
|
+
&.drawer-container {
|
|
418
|
+
&[data-header="false"] .drawer-header {
|
|
419
|
+
display: none;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
&[data-header-divider="true"] .drawer-header {
|
|
423
|
+
padding-bottom: 1rem;
|
|
424
|
+
border-bottom-width: 1px;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
&[data-footer-divider="true"] .drawer-footer {
|
|
428
|
+
border-top-width: 1px;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
&[data-padding="true"] .drawer-main {
|
|
432
|
+
padding: 1rem;
|
|
433
|
+
padding-top: 0.5rem;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
&[data-title="false"] .drawer-title-h {
|
|
437
|
+
display: none;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
&[data-close-button="false"] .drawer-close {
|
|
441
|
+
display: none;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.drawer-wrapper {
|
|
446
|
+
position: absolute;
|
|
447
|
+
inset: 0;
|
|
448
|
+
overflow: hidden;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.drawer-content {
|
|
452
|
+
position: relative;
|
|
453
|
+
display: flex;
|
|
454
|
+
flex-direction: column;
|
|
455
|
+
height: 100%;
|
|
456
|
+
width: 100%;
|
|
457
|
+
background-color: #ffffff;
|
|
458
|
+
box-shadow:
|
|
459
|
+
0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
|
460
|
+
0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
461
|
+
}
|
|
462
|
+
&.drawer-container[data-padding="true"] .drawer-content {
|
|
463
|
+
padding-top: 1.5rem;
|
|
464
|
+
padding-bottom: 1.5rem;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.drawer-header {
|
|
468
|
+
display: flex;
|
|
469
|
+
padding-left: 1rem;
|
|
470
|
+
padding-right: 1rem;
|
|
471
|
+
align-items: flex-start;
|
|
472
|
+
justify-content: space-between;
|
|
473
|
+
width: 100%;
|
|
474
|
+
|
|
475
|
+
@media (min-width: 640px) {
|
|
476
|
+
padding-left: 1.5rem;
|
|
477
|
+
padding-right: 1.5rem;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.drawer-title {
|
|
482
|
+
line-height: 1.75rem;
|
|
483
|
+
font-weight: 600;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.drawer-main {
|
|
487
|
+
position: relative;
|
|
488
|
+
flex: 1;
|
|
489
|
+
overflow-y: auto;
|
|
490
|
+
}
|
|
491
|
+
&.drawer-container[data-padding="true"] .drawer-main {
|
|
492
|
+
margin-top: 1.5rem;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.drawer-footer {
|
|
496
|
+
display: flex;
|
|
497
|
+
flex-shrink: 0;
|
|
498
|
+
padding: 1rem;
|
|
499
|
+
|
|
500
|
+
@media (min-width: 640px) {
|
|
501
|
+
padding-left: 1.5rem;
|
|
502
|
+
padding-right: 1.5rem;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.drawer-close {
|
|
507
|
+
margin-left: 0.75rem;
|
|
508
|
+
display: flex;
|
|
509
|
+
height: 1.75rem;
|
|
510
|
+
align-items: center;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.drawer-close-button {
|
|
514
|
+
position: relative;
|
|
515
|
+
display: inline-flex;
|
|
516
|
+
padding: 0.375rem;
|
|
517
|
+
background-color: transparent;
|
|
518
|
+
border-radius: 0.375rem;
|
|
519
|
+
align-items: center;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.drawer-close-hit-area {
|
|
523
|
+
position: absolute;
|
|
524
|
+
inset: -0.625rem;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.drawer-close-icon {
|
|
528
|
+
width: 1.5rem;
|
|
529
|
+
height: 1.5rem;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/* ========================================
|
|
534
|
+
Dark / light mode (requires ancestor
|
|
535
|
+
context so must stay outside dialog.utmr)
|
|
536
|
+
======================================== */
|
|
537
|
+
|
|
538
|
+
.dark dialog.utmr {
|
|
539
|
+
&::backdrop {
|
|
540
|
+
background-color: rgba(17, 24, 39, 0.8);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.modal-header,
|
|
544
|
+
.modal-footer,
|
|
545
|
+
.drawer-header,
|
|
546
|
+
.drawer-footer {
|
|
547
|
+
border-color: #4B5563;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.modal-content,
|
|
551
|
+
.drawer-content {
|
|
552
|
+
background-color: #1F2937;
|
|
553
|
+
color: #ffffff;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.confirm-content {
|
|
557
|
+
background-color: #1F2937;
|
|
558
|
+
color: #ffffff;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.confirm-body {
|
|
562
|
+
color: #9CA3AF;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.confirm-header,
|
|
566
|
+
.confirm-footer {
|
|
567
|
+
border-color: #4B5563;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
.confirm-cancel {
|
|
571
|
+
background-color: #374151;
|
|
572
|
+
color: #F3F4F6;
|
|
573
|
+
border-color: #4B5563;
|
|
574
|
+
|
|
575
|
+
&:hover {
|
|
576
|
+
background-color: #4B5563;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.modal-close-button:hover,
|
|
581
|
+
.modal-close-icon:hover,
|
|
582
|
+
.drawer-close-button:hover,
|
|
583
|
+
.drawer-close-icon:hover {
|
|
584
|
+
background-color: #6B7280;
|
|
585
|
+
color: #ffffff;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
:not(.dark) dialog.utmr {
|
|
590
|
+
.confirm-body {
|
|
591
|
+
color: #6B7280;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
.confirm-header,
|
|
595
|
+
.confirm-footer {
|
|
596
|
+
border-color: #E5E7EB;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.modal-close-button,
|
|
600
|
+
.modal-close-icon,
|
|
601
|
+
.drawer-close-button,
|
|
602
|
+
.drawer-close-icon {
|
|
603
|
+
color: #9CA3AF;
|
|
604
|
+
|
|
605
|
+
&:hover {
|
|
606
|
+
color: #111827;
|
|
607
|
+
background-color: #E5E7EB;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
@@ -86,6 +86,50 @@ module UltimateTurboModal
|
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
# The vanilla flavor needs a stylesheet; the other flavors are styled
|
|
90
|
+
# entirely by the classes defined in their flavor file.
|
|
91
|
+
def add_vanilla_stylesheet
|
|
92
|
+
layout_path = rails_root_join("app", "views", "layouts", "application.html.erb")
|
|
93
|
+
link_tag = "<%= stylesheet_link_tag \"#{package_name}\", \"data-turbo-track\": \"reload\" %>\n"
|
|
94
|
+
|
|
95
|
+
say "\nAttempting to add the vanilla stylesheet to #{layout_path}...", :yellow
|
|
96
|
+
|
|
97
|
+
unless File.exist?(layout_path)
|
|
98
|
+
say "❌ Layout file not found at #{layout_path}.", :red
|
|
99
|
+
say " Please manually add the following line inside the <head> of your main layout:", :yellow
|
|
100
|
+
say " #{link_tag.strip}\n", :cyan
|
|
101
|
+
return
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
file_content = File.read(layout_path)
|
|
105
|
+
|
|
106
|
+
if file_content.match?(/stylesheet_link_tag\s+["']#{Regexp.escape(package_name)}["']/)
|
|
107
|
+
say "⏩ Stylesheet tag already exists.", :blue
|
|
108
|
+
return
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Insert before the first existing stylesheet_link_tag. Where the two
|
|
112
|
+
# stylesheets collide at equal specificity the later one wins, so the
|
|
113
|
+
# defaults have to load first for the app's own styles to override them.
|
|
114
|
+
existing_tag = file_content.scan(/^.*stylesheet_link_tag.*\n/).first
|
|
115
|
+
|
|
116
|
+
if existing_tag
|
|
117
|
+
insert_into_file layout_path, "#{indentation_of(existing_tag)}#{link_tag}", before: existing_tag
|
|
118
|
+
say "✅ Added stylesheet tag to the layout.", :green
|
|
119
|
+
elsif file_content.match?(%r{</head>})
|
|
120
|
+
insert_into_file layout_path, " #{link_tag}", before: %r{^\s*</head>}
|
|
121
|
+
say "✅ Added stylesheet tag before </head>.", :green
|
|
122
|
+
else
|
|
123
|
+
say "❌ Could not find a <head> section in #{layout_path}.", :red
|
|
124
|
+
say " Please manually add the following line inside the <head> of your main layout:", :yellow
|
|
125
|
+
say " #{link_tag.strip}\n", :cyan
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def indentation_of(line)
|
|
130
|
+
line[/\A[ \t]*/]
|
|
131
|
+
end
|
|
132
|
+
|
|
89
133
|
def uses_importmaps?
|
|
90
134
|
File.exist?(rails_root_join("config", "importmap.rb"))
|
|
91
135
|
end
|
|
@@ -108,6 +108,11 @@ module UltimateTurboModal
|
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
+
# Step 5: Add the stylesheet to the layout (vanilla flavor only)
|
|
112
|
+
def setup_stylesheet
|
|
113
|
+
add_vanilla_stylesheet if @framework == "vanilla"
|
|
114
|
+
end
|
|
115
|
+
|
|
111
116
|
def copy_initializer_and_flavor
|
|
112
117
|
say "\nCreating initializer for `#{@framework}` flavor...", :green
|
|
113
118
|
copy_file "ultimate_turbo_modal.rb", "config/initializers/ultimate_turbo_modal.rb"
|
|
@@ -122,6 +127,9 @@ module UltimateTurboModal
|
|
|
122
127
|
def show_readme
|
|
123
128
|
say "\nUltimateTurboModal installation complete!\n", :magenta
|
|
124
129
|
say "Please review the initializer files, ensure JS is set up, and check your layout file.", :magenta
|
|
130
|
+
say "\nOptional: to render `data-turbo-confirm` prompts as modals instead of", :magenta
|
|
131
|
+
say "the browser's confirm dialog, add this to your layout:", :magenta
|
|
132
|
+
say " <%= modal_confirm_template %>", :cyan
|
|
125
133
|
say "Don't forget to restart your Rails server!", :yellow
|
|
126
134
|
end
|
|
127
135
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# Set to "" or omit entirely if you handle all styling via classes and external CSS.
|
|
9
9
|
module UltimateTurboModal::Flavors
|
|
10
10
|
class Custom < UltimateTurboModal::Base
|
|
11
|
-
STYLES = "html:has(dialog
|
|
11
|
+
STYLES = "html:has(dialog.utmr[open]) { overflow: hidden; }"
|
|
12
12
|
|
|
13
13
|
MODAL_DIALOG_CLASSES = ""
|
|
14
14
|
MODAL_INNER_CLASSES = ""
|
|
@@ -23,6 +23,15 @@ module UltimateTurboModal::Flavors
|
|
|
23
23
|
MODAL_CLOSE_SR_CLASSES = ""
|
|
24
24
|
MODAL_CLOSE_ICON_CLASSES = ""
|
|
25
25
|
|
|
26
|
+
# Confirm dialog constants
|
|
27
|
+
# Only the slots that have no modal equivalent are listed. Every other slot
|
|
28
|
+
# (dialog, inner, content, header, title, main, footer) falls back to the
|
|
29
|
+
# matching MODAL_* class above; define a CONFIRM_* one to override it.
|
|
30
|
+
CONFIRM_BODY_CLASSES = ""
|
|
31
|
+
CONFIRM_ACTIONS_CLASSES = ""
|
|
32
|
+
CONFIRM_CANCEL_CLASSES = ""
|
|
33
|
+
CONFIRM_ACCEPT_CLASSES = ""
|
|
34
|
+
|
|
26
35
|
# Drawer constants
|
|
27
36
|
DRAWER_DIALOG_CLASSES = MODAL_DIALOG_CLASSES
|
|
28
37
|
DRAWER_WRAPPER_CLASSES = ""
|