rails_modal_manager 1.0.34 → 1.0.36
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/app/assets/stylesheets/rails_modal_manager.css +232 -7
- data/app/javascript/rails_modal_manager/controllers/rmm_modal_controller.js +87 -11
- data/app/javascript/rails_modal_manager/modal_store.js +23 -7
- data/app/views/rails_modal_manager/_modal.html.erb +9 -0
- data/lib/rails_modal_manager/version.rb +1 -1
- 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: 1e33e1757e9bc78d6b4e0b801af55ce709813f195254eb3196b8f8394a6afcce
|
|
4
|
+
data.tar.gz: 81818160c6846429dccc642893a95d2c98f0274406aaae378eff2a6e73ff2163
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b826a9937d346a5163902fd6d6a47ba22816c99ffdcf52710cbe09f3feca7b2bc571b818eb0229f7092fd2615cda1772f7dd01a8a49bf817e454487179b5790
|
|
7
|
+
data.tar.gz: 05bf7a6f7cdbe61b203a44735b597bcd9a57b5fe9eff65b3779eb332cb63ead249118b4175ef52c1f363e0d68726434cc79225e1a540c5d95e0552c1e1917cbc
|
|
@@ -23,6 +23,16 @@
|
|
|
23
23
|
--rmm-header-text: #1e293b;
|
|
24
24
|
--rmm-header-height: 56px;
|
|
25
25
|
|
|
26
|
+
/* Header - Depth Colors (Parent/Child/Grandchild) */
|
|
27
|
+
--rmm-header-bg-depth-0: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
28
|
+
--rmm-header-text-depth-0: #ffffff;
|
|
29
|
+
--rmm-header-bg-depth-1: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
|
30
|
+
--rmm-header-text-depth-1: #ffffff;
|
|
31
|
+
--rmm-header-bg-depth-2: linear-gradient(135deg, #fc4a1a 0%, #f7b733 100%);
|
|
32
|
+
--rmm-header-text-depth-2: #ffffff;
|
|
33
|
+
--rmm-header-bg-depth-3: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%);
|
|
34
|
+
--rmm-header-text-depth-3: #ffffff;
|
|
35
|
+
|
|
26
36
|
/* Footer */
|
|
27
37
|
--rmm-footer-bg: #f8fafc;
|
|
28
38
|
--rmm-footer-border: rgba(0, 0, 0, 0.1);
|
|
@@ -110,6 +120,16 @@
|
|
|
110
120
|
--rmm-header-border: rgba(255, 255, 255, 0.1);
|
|
111
121
|
--rmm-header-text: #f1f5f9;
|
|
112
122
|
|
|
123
|
+
/* Header - Depth Colors (Dark Theme) */
|
|
124
|
+
--rmm-header-bg-depth-0: linear-gradient(135deg, #4c51bf 0%, #553c9a 100%);
|
|
125
|
+
--rmm-header-text-depth-0: #ffffff;
|
|
126
|
+
--rmm-header-bg-depth-1: linear-gradient(135deg, #0d7377 0%, #14a085 100%);
|
|
127
|
+
--rmm-header-text-depth-1: #ffffff;
|
|
128
|
+
--rmm-header-bg-depth-2: linear-gradient(135deg, #c0392b 0%, #d68910 100%);
|
|
129
|
+
--rmm-header-text-depth-2: #ffffff;
|
|
130
|
+
--rmm-header-bg-depth-3: linear-gradient(135deg, #a0185e 0%, #c0392b 100%);
|
|
131
|
+
--rmm-header-text-depth-3: #ffffff;
|
|
132
|
+
|
|
113
133
|
--rmm-footer-bg: #0f172a;
|
|
114
134
|
--rmm-footer-border: rgba(255, 255, 255, 0.1);
|
|
115
135
|
--rmm-footer-text: #e2e8f0;
|
|
@@ -219,13 +239,18 @@
|
|
|
219
239
|
}
|
|
220
240
|
|
|
221
241
|
/* Size variants */
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
.rmm-modal.rmm-size-
|
|
228
|
-
.rmm-modal.rmm-size-
|
|
242
|
+
/* Modal Size Options (PC)
|
|
243
|
+
- width: 브라우저 크기 대비 비율
|
|
244
|
+
- min-width: 1920px 기준 비율, 단 브라우저보다 커지지 않도록 min() 사용
|
|
245
|
+
- max-width: 브라우저보다 커지지 않도록 제한 (20px 여백 확보)
|
|
246
|
+
*/
|
|
247
|
+
.rmm-modal.rmm-size-fit { width: auto; min-width: min(200px, calc(100vw - 40px)); max-width: calc(100vw - 40px); }
|
|
248
|
+
.rmm-modal.rmm-size-xss { width: 20%; min-width: min(384px, calc(100vw - 40px)); max-width: calc(100vw - 40px); } /* 1920 * 20% */
|
|
249
|
+
.rmm-modal.rmm-size-xs { width: 30%; min-width: min(576px, calc(100vw - 40px)); max-width: calc(100vw - 40px); } /* 1920 * 30% */
|
|
250
|
+
.rmm-modal.rmm-size-sm { width: 40%; min-width: min(768px, calc(100vw - 40px)); max-width: calc(100vw - 40px); } /* 1920 * 40% */
|
|
251
|
+
.rmm-modal.rmm-size-md { width: 50%; min-width: min(960px, calc(100vw - 40px)); max-width: calc(100vw - 40px); } /* 1920 * 50% */
|
|
252
|
+
.rmm-modal.rmm-size-lg { width: 60%; min-width: min(1152px, calc(100vw - 40px)); max-width: calc(100vw - 40px); } /* 1920 * 60% */
|
|
253
|
+
.rmm-modal.rmm-size-xl { width: 70%; min-width: min(1344px, calc(100vw - 40px)); max-width: calc(100vw - 40px); } /* 1920 * 70% */
|
|
229
254
|
.rmm-modal.rmm-size-full,
|
|
230
255
|
.rmm-modal.rmm-size-full.rmm-active,
|
|
231
256
|
.rmm-modal.rmm-size-full.rmm-position-center,
|
|
@@ -470,6 +495,154 @@
|
|
|
470
495
|
white-space: nowrap;
|
|
471
496
|
}
|
|
472
497
|
|
|
498
|
+
/* ============================================
|
|
499
|
+
Modal Depth-based Header Colors
|
|
500
|
+
============================================ */
|
|
501
|
+
/* Depth 0 - Parent Modal (Purple gradient) */
|
|
502
|
+
.rmm-modal.rmm-depth-0 .rmm-header {
|
|
503
|
+
background: var(--rmm-header-bg-depth-0);
|
|
504
|
+
border-bottom-color: rgba(255, 255, 255, 0.2);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.rmm-modal.rmm-depth-0 .rmm-header-title {
|
|
508
|
+
color: var(--rmm-header-text-depth-0);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.rmm-modal.rmm-depth-0 .rmm-header-btn {
|
|
512
|
+
color: var(--rmm-header-text-depth-0);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.rmm-modal.rmm-depth-0 .rmm-header-btn:hover {
|
|
516
|
+
background: rgba(255, 255, 255, 0.15);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.rmm-modal.rmm-depth-0 .rmm-header-btn.rmm-close-btn:hover {
|
|
520
|
+
background: rgba(239, 68, 68, 0.9);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.rmm-modal.rmm-depth-0 .rmm-drag-handle {
|
|
524
|
+
color: var(--rmm-header-text-depth-0);
|
|
525
|
+
opacity: 0.6;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/* Depth 1 - Child Modal (Green gradient) */
|
|
529
|
+
.rmm-modal.rmm-depth-1 .rmm-header {
|
|
530
|
+
background: var(--rmm-header-bg-depth-1);
|
|
531
|
+
border-bottom-color: rgba(255, 255, 255, 0.2);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.rmm-modal.rmm-depth-1 .rmm-header-title {
|
|
535
|
+
color: var(--rmm-header-text-depth-1);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.rmm-modal.rmm-depth-1 .rmm-header-btn {
|
|
539
|
+
color: var(--rmm-header-text-depth-1);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.rmm-modal.rmm-depth-1 .rmm-header-btn:hover {
|
|
543
|
+
background: rgba(255, 255, 255, 0.15);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.rmm-modal.rmm-depth-1 .rmm-header-btn.rmm-close-btn:hover {
|
|
547
|
+
background: rgba(239, 68, 68, 0.9);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.rmm-modal.rmm-depth-1 .rmm-drag-handle {
|
|
551
|
+
color: var(--rmm-header-text-depth-1);
|
|
552
|
+
opacity: 0.6;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/* Depth 2 - Grandchild Modal (Orange gradient) */
|
|
556
|
+
.rmm-modal.rmm-depth-2 .rmm-header {
|
|
557
|
+
background: var(--rmm-header-bg-depth-2);
|
|
558
|
+
border-bottom-color: rgba(255, 255, 255, 0.2);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.rmm-modal.rmm-depth-2 .rmm-header-title {
|
|
562
|
+
color: var(--rmm-header-text-depth-2);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.rmm-modal.rmm-depth-2 .rmm-header-btn {
|
|
566
|
+
color: var(--rmm-header-text-depth-2);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.rmm-modal.rmm-depth-2 .rmm-header-btn:hover {
|
|
570
|
+
background: rgba(255, 255, 255, 0.15);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.rmm-modal.rmm-depth-2 .rmm-header-btn.rmm-close-btn:hover {
|
|
574
|
+
background: rgba(239, 68, 68, 0.9);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.rmm-modal.rmm-depth-2 .rmm-drag-handle {
|
|
578
|
+
color: var(--rmm-header-text-depth-2);
|
|
579
|
+
opacity: 0.6;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* Depth 3+ - Deep nested modals (Pink/Red gradient) */
|
|
583
|
+
.rmm-modal.rmm-depth-3 .rmm-header,
|
|
584
|
+
.rmm-modal.rmm-depth-4 .rmm-header,
|
|
585
|
+
.rmm-modal.rmm-depth-5 .rmm-header {
|
|
586
|
+
background: var(--rmm-header-bg-depth-3);
|
|
587
|
+
border-bottom-color: rgba(255, 255, 255, 0.2);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.rmm-modal.rmm-depth-3 .rmm-header-title,
|
|
591
|
+
.rmm-modal.rmm-depth-4 .rmm-header-title,
|
|
592
|
+
.rmm-modal.rmm-depth-5 .rmm-header-title {
|
|
593
|
+
color: var(--rmm-header-text-depth-3);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.rmm-modal.rmm-depth-3 .rmm-header-btn,
|
|
597
|
+
.rmm-modal.rmm-depth-4 .rmm-header-btn,
|
|
598
|
+
.rmm-modal.rmm-depth-5 .rmm-header-btn {
|
|
599
|
+
color: var(--rmm-header-text-depth-3);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
.rmm-modal.rmm-depth-3 .rmm-header-btn:hover,
|
|
603
|
+
.rmm-modal.rmm-depth-4 .rmm-header-btn:hover,
|
|
604
|
+
.rmm-modal.rmm-depth-5 .rmm-header-btn:hover {
|
|
605
|
+
background: rgba(255, 255, 255, 0.15);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.rmm-modal.rmm-depth-3 .rmm-header-btn.rmm-close-btn:hover,
|
|
609
|
+
.rmm-modal.rmm-depth-4 .rmm-header-btn.rmm-close-btn:hover,
|
|
610
|
+
.rmm-modal.rmm-depth-5 .rmm-header-btn.rmm-close-btn:hover {
|
|
611
|
+
background: rgba(239, 68, 68, 0.9);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.rmm-modal.rmm-depth-3 .rmm-drag-handle,
|
|
615
|
+
.rmm-modal.rmm-depth-4 .rmm-drag-handle,
|
|
616
|
+
.rmm-modal.rmm-depth-5 .rmm-drag-handle {
|
|
617
|
+
color: var(--rmm-header-text-depth-3);
|
|
618
|
+
opacity: 0.6;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/* ============================================
|
|
622
|
+
Custom Header Colors (via CSS variables)
|
|
623
|
+
Usage: style="--rmm-custom-header-bg: #3b82f6; --rmm-custom-header-text: #ffffff;"
|
|
624
|
+
============================================ */
|
|
625
|
+
.rmm-modal.rmm-custom-header .rmm-header {
|
|
626
|
+
background: var(--rmm-custom-header-bg, var(--rmm-header-bg));
|
|
627
|
+
border-bottom-color: var(--rmm-custom-header-border, var(--rmm-header-border));
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
.rmm-modal.rmm-custom-header .rmm-header-title {
|
|
631
|
+
color: var(--rmm-custom-header-text, var(--rmm-header-text));
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.rmm-modal.rmm-custom-header .rmm-header-btn {
|
|
635
|
+
color: var(--rmm-custom-header-text, var(--rmm-header-text));
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.rmm-modal.rmm-custom-header .rmm-header-btn:hover {
|
|
639
|
+
background: var(--rmm-custom-header-btn-hover, rgba(255, 255, 255, 0.15));
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.rmm-modal.rmm-custom-header .rmm-drag-handle {
|
|
643
|
+
color: var(--rmm-custom-header-text, var(--rmm-header-text));
|
|
644
|
+
}
|
|
645
|
+
|
|
473
646
|
/* Size controls dropdown */
|
|
474
647
|
.rmm-size-controls {
|
|
475
648
|
position: relative;
|
|
@@ -1507,3 +1680,55 @@
|
|
|
1507
1680
|
opacity: 0.5;
|
|
1508
1681
|
transition: opacity 0.2s ease;
|
|
1509
1682
|
}
|
|
1683
|
+
|
|
1684
|
+
/* ============================================
|
|
1685
|
+
PC Size Fixes (min-width: 769px)
|
|
1686
|
+
- 중간 크기 브라우저에서도 min-width 적용
|
|
1687
|
+
- :not(.rmm-dragging):not(.rmm-custom-position)으로 드래그 중/후 제외
|
|
1688
|
+
============================================ */
|
|
1689
|
+
@media (min-width: 769px) {
|
|
1690
|
+
/* MD: 769px ~ 960px 범위에서도 min-width 적용 */
|
|
1691
|
+
.rmm-modal.rmm-size-md:not(.rmm-dragging):not(.rmm-custom-position) {
|
|
1692
|
+
width: 50% !important;
|
|
1693
|
+
min-width: min(960px, calc(100vw - 40px)) !important;
|
|
1694
|
+
max-width: calc(100vw - 40px) !important;
|
|
1695
|
+
left: 50% !important;
|
|
1696
|
+
right: auto !important;
|
|
1697
|
+
}
|
|
1698
|
+
.rmm-modal.rmm-size-md.rmm-position-center:not(.rmm-dragging):not(.rmm-custom-position) {
|
|
1699
|
+
transform: translate(-50%, -50%) !important;
|
|
1700
|
+
}
|
|
1701
|
+
.rmm-modal.rmm-size-md.rmm-position-center.rmm-active:not(.rmm-dragging):not(.rmm-custom-position) {
|
|
1702
|
+
transform: translate(-50%, -50%) scale(1) !important;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
/* LG: 769px ~ 1152px 범위에서도 min-width 적용 */
|
|
1706
|
+
.rmm-modal.rmm-size-lg:not(.rmm-dragging):not(.rmm-custom-position) {
|
|
1707
|
+
width: 60% !important;
|
|
1708
|
+
min-width: min(1152px, calc(100vw - 40px)) !important;
|
|
1709
|
+
max-width: calc(100vw - 40px) !important;
|
|
1710
|
+
left: 50% !important;
|
|
1711
|
+
right: auto !important;
|
|
1712
|
+
}
|
|
1713
|
+
.rmm-modal.rmm-size-lg.rmm-position-center:not(.rmm-dragging):not(.rmm-custom-position) {
|
|
1714
|
+
transform: translate(-50%, -50%) !important;
|
|
1715
|
+
}
|
|
1716
|
+
.rmm-modal.rmm-size-lg.rmm-position-center.rmm-active:not(.rmm-dragging):not(.rmm-custom-position) {
|
|
1717
|
+
transform: translate(-50%, -50%) scale(1) !important;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
/* XL: 769px ~ 1344px 범위에서도 min-width 적용 */
|
|
1721
|
+
.rmm-modal.rmm-size-xl:not(.rmm-dragging):not(.rmm-custom-position) {
|
|
1722
|
+
width: 70% !important;
|
|
1723
|
+
min-width: min(1344px, calc(100vw - 40px)) !important;
|
|
1724
|
+
max-width: calc(100vw - 40px) !important;
|
|
1725
|
+
left: 50% !important;
|
|
1726
|
+
right: auto !important;
|
|
1727
|
+
}
|
|
1728
|
+
.rmm-modal.rmm-size-xl.rmm-position-center:not(.rmm-dragging):not(.rmm-custom-position) {
|
|
1729
|
+
transform: translate(-50%, -50%) !important;
|
|
1730
|
+
}
|
|
1731
|
+
.rmm-modal.rmm-size-xl.rmm-position-center.rmm-active:not(.rmm-dragging):not(.rmm-custom-position) {
|
|
1732
|
+
transform: translate(-50%, -50%) scale(1) !important;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
@@ -37,6 +37,10 @@ export default class extends Controller {
|
|
|
37
37
|
confirmClose: { type: Boolean, default: false },
|
|
38
38
|
confirmCloseMessage: { type: String, default: "정말 닫으시겠습니까?" },
|
|
39
39
|
mobileDefaultMaximized: { type: Boolean, default: false },
|
|
40
|
+
// Custom header colors
|
|
41
|
+
headerBg: String, // Custom header background color (e.g., "#3b82f6" or "linear-gradient(135deg, #667eea, #764ba2)")
|
|
42
|
+
headerText: String, // Custom header text color (e.g., "#ffffff")
|
|
43
|
+
headerBorder: String, // Custom header border color (e.g., "#2563eb")
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
connect() {
|
|
@@ -328,6 +332,9 @@ export default class extends Controller {
|
|
|
328
332
|
// Apply initial size and position
|
|
329
333
|
this.applyStyles()
|
|
330
334
|
|
|
335
|
+
// Apply depth class for visual hierarchy (parent/child/grandchild)
|
|
336
|
+
this.applyDepthClass()
|
|
337
|
+
|
|
331
338
|
// Start animation
|
|
332
339
|
requestAnimationFrame(() => {
|
|
333
340
|
modal.classList.add('rmm-active')
|
|
@@ -340,6 +347,46 @@ export default class extends Controller {
|
|
|
340
347
|
})
|
|
341
348
|
}
|
|
342
349
|
|
|
350
|
+
/**
|
|
351
|
+
* Apply depth-based CSS class for visual hierarchy
|
|
352
|
+
* Depth 0 (root): rmm-depth-0
|
|
353
|
+
* Depth 1 (child): rmm-depth-1
|
|
354
|
+
* Depth 2 (grandchild): rmm-depth-2
|
|
355
|
+
* Depth 3+ (great-grandchild): rmm-depth-3
|
|
356
|
+
*
|
|
357
|
+
* Also applies custom header colors if specified via data attributes
|
|
358
|
+
*/
|
|
359
|
+
applyDepthClass() {
|
|
360
|
+
const modal = this.element
|
|
361
|
+
const modalId = this.effectiveModalId
|
|
362
|
+
const depth = modalStore.getModalDepth(modalId)
|
|
363
|
+
|
|
364
|
+
// Remove existing depth classes
|
|
365
|
+
modal.classList.remove('rmm-depth-0', 'rmm-depth-1', 'rmm-depth-2', 'rmm-depth-3')
|
|
366
|
+
|
|
367
|
+
// Check for custom header colors
|
|
368
|
+
const hasCustomHeader = this.hasHeaderBgValue || this.hasHeaderTextValue || this.hasHeaderBorderValue
|
|
369
|
+
|
|
370
|
+
if (hasCustomHeader) {
|
|
371
|
+
// Apply custom header colors via CSS variables
|
|
372
|
+
modal.classList.add('rmm-custom-header')
|
|
373
|
+
|
|
374
|
+
if (this.hasHeaderBgValue && this.headerBgValue) {
|
|
375
|
+
modal.style.setProperty('--rmm-custom-header-bg', this.headerBgValue)
|
|
376
|
+
}
|
|
377
|
+
if (this.hasHeaderTextValue && this.headerTextValue) {
|
|
378
|
+
modal.style.setProperty('--rmm-custom-header-text', this.headerTextValue)
|
|
379
|
+
}
|
|
380
|
+
if (this.hasHeaderBorderValue && this.headerBorderValue) {
|
|
381
|
+
modal.style.setProperty('--rmm-custom-header-border', this.headerBorderValue)
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
// Apply depth class (only if no custom header colors)
|
|
385
|
+
const depthClass = `rmm-depth-${Math.min(depth, 3)}`
|
|
386
|
+
modal.classList.add(depthClass)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
343
390
|
hideModal(callback) {
|
|
344
391
|
const modal = this.element
|
|
345
392
|
const overlay = this.getOverlay()
|
|
@@ -347,10 +394,18 @@ export default class extends Controller {
|
|
|
347
394
|
// Reset sidebar and submenu to first item before hiding
|
|
348
395
|
this.resetSidebarAndSubmenu()
|
|
349
396
|
|
|
397
|
+
// 닫기 애니메이션 시작 (현재 위치에서 닫힘)
|
|
350
398
|
modal.classList.remove('rmm-active')
|
|
351
399
|
if (overlay) overlay.classList.remove('rmm-active')
|
|
352
400
|
|
|
353
401
|
setTimeout(() => {
|
|
402
|
+
// 애니메이션 완료 후 드래그로 인한 커스텀 위치 클래스 및 인라인 스타일 제거
|
|
403
|
+
modal.classList.remove('rmm-custom-position')
|
|
404
|
+
modal.classList.remove('rmm-dragging')
|
|
405
|
+
modal.style.top = ''
|
|
406
|
+
modal.style.left = ''
|
|
407
|
+
modal.style.transform = ''
|
|
408
|
+
|
|
354
409
|
// Reset openValue to false so modal can be reopened
|
|
355
410
|
// Use a flag to prevent openValueChanged from triggering close again
|
|
356
411
|
this._closingProgrammatically = true
|
|
@@ -417,17 +472,34 @@ export default class extends Controller {
|
|
|
417
472
|
|
|
418
473
|
// For full size, explicitly set full screen styles
|
|
419
474
|
if (config.size === 'full' || config.isMaximized) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
modal.style.
|
|
430
|
-
modal.style.
|
|
475
|
+
// 최소화된 모달이 있는지 store에서 확인하여 taskbar 높이 계산
|
|
476
|
+
const minimizedGroups = modalStore.getMinimizedModalGroups()
|
|
477
|
+
const hasMinimizedModals = minimizedGroups.length > 0
|
|
478
|
+
// CSS 변수에서 taskbar 높이 가져오기 (기본값 48px)
|
|
479
|
+
const taskbarHeight = hasMinimizedModals
|
|
480
|
+
? parseInt(getComputedStyle(document.documentElement).getPropertyValue('--rmm-taskbar-height') || '48', 10)
|
|
481
|
+
: 0
|
|
482
|
+
|
|
483
|
+
// CSS !important를 덮어쓰기 위해 setProperty 사용
|
|
484
|
+
modal.style.setProperty('width', '100%', 'important')
|
|
485
|
+
modal.style.setProperty('min-width', '100%', 'important')
|
|
486
|
+
modal.style.setProperty('top', '0', 'important')
|
|
487
|
+
modal.style.setProperty('left', '0', 'important')
|
|
488
|
+
modal.style.setProperty('right', '0', 'important')
|
|
489
|
+
modal.style.setProperty('transform', 'none', 'important')
|
|
490
|
+
modal.style.setProperty('border-radius', '0', 'important')
|
|
491
|
+
modal.style.setProperty('margin', '0', 'important')
|
|
492
|
+
|
|
493
|
+
if (taskbarHeight > 0) {
|
|
494
|
+
// taskbar가 있으면 그 위까지만 표시
|
|
495
|
+
modal.style.setProperty('height', `calc(100% - ${taskbarHeight}px)`, 'important')
|
|
496
|
+
modal.style.setProperty('max-height', `calc(100% - ${taskbarHeight}px)`, 'important')
|
|
497
|
+
modal.style.setProperty('bottom', `${taskbarHeight}px`, 'important')
|
|
498
|
+
} else {
|
|
499
|
+
modal.style.setProperty('height', '100%', 'important')
|
|
500
|
+
modal.style.setProperty('max-height', '100%', 'important')
|
|
501
|
+
modal.style.setProperty('bottom', '0', 'important')
|
|
502
|
+
}
|
|
431
503
|
} else {
|
|
432
504
|
// Reset full-size specific styles
|
|
433
505
|
modal.style.borderRadius = ''
|
|
@@ -548,6 +620,7 @@ export default class extends Controller {
|
|
|
548
620
|
historyStackManager.moveToTop('modal', this.effectiveModalId)
|
|
549
621
|
}
|
|
550
622
|
this.isDragging = true
|
|
623
|
+
this.element.classList.add('rmm-dragging')
|
|
551
624
|
|
|
552
625
|
const clientX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX
|
|
553
626
|
const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY
|
|
@@ -590,6 +663,9 @@ export default class extends Controller {
|
|
|
590
663
|
|
|
591
664
|
handleDragEnd() {
|
|
592
665
|
this.isDragging = false
|
|
666
|
+
this.element.classList.remove('rmm-dragging')
|
|
667
|
+
// 드래그 후 위치 유지를 위해 custom-position 클래스 추가
|
|
668
|
+
this.element.classList.add('rmm-custom-position')
|
|
593
669
|
|
|
594
670
|
this.element.style.transition = ''
|
|
595
671
|
|
|
@@ -23,14 +23,16 @@ export const MODAL_CONSTANTS = {
|
|
|
23
23
|
export const CASCADE_OFFSET = { x: 30, y: 30 };
|
|
24
24
|
|
|
25
25
|
// Size configurations (matching React version)
|
|
26
|
+
// min-width: 1920px 기준 비율 적용 (브라우저가 작아져도 이 이하로 줄어들지 않음)
|
|
27
|
+
// max-width는 CSS에서 calc(100vw - 40px)로 처리하여 브라우저 벗어남 방지
|
|
26
28
|
export const SIZE_CONFIG = {
|
|
27
|
-
fit: { width: 'auto', minWidth: '
|
|
28
|
-
xss: { width: '20%', minWidth: '
|
|
29
|
-
xs: { width: '30%', minWidth: '
|
|
30
|
-
sm: { width: '40%', minWidth: '
|
|
31
|
-
md: { width: '50%', minWidth: '
|
|
32
|
-
lg: { width: '60%', minWidth: '
|
|
33
|
-
xl: { width: '70%', minWidth: '
|
|
29
|
+
fit: { width: 'auto', minWidth: '200px', height: 'auto' },
|
|
30
|
+
xss: { width: '20%', minWidth: '384px', height: 'auto' }, // 1920 * 20%
|
|
31
|
+
xs: { width: '30%', minWidth: '576px', height: 'auto' }, // 1920 * 30%
|
|
32
|
+
sm: { width: '40%', minWidth: '768px', height: 'auto' }, // 1920 * 40%
|
|
33
|
+
md: { width: '50%', minWidth: '960px', height: 'auto' }, // 1920 * 50%
|
|
34
|
+
lg: { width: '60%', minWidth: '1152px', height: 'auto' }, // 1920 * 60%
|
|
35
|
+
xl: { width: '70%', minWidth: '1344px', height: 'auto' }, // 1920 * 70%
|
|
34
36
|
full: { width: '100%', minWidth: '100%', height: '100%' },
|
|
35
37
|
};
|
|
36
38
|
|
|
@@ -490,6 +492,20 @@ class ModalStore {
|
|
|
490
492
|
return [...this.getModalChain(config.parentModalId), modalId];
|
|
491
493
|
}
|
|
492
494
|
|
|
495
|
+
/**
|
|
496
|
+
* Get the depth of a modal in the parent-child hierarchy
|
|
497
|
+
* Root modals (no parent) have depth 0, children have depth 1, grandchildren depth 2, etc.
|
|
498
|
+
* @param {string} modalId - The modal ID to get depth for
|
|
499
|
+
* @returns {number} The depth level (0 for root, 1 for child, 2 for grandchild, etc.)
|
|
500
|
+
*/
|
|
501
|
+
getModalDepth(modalId) {
|
|
502
|
+
const config = this.activeModals[modalId];
|
|
503
|
+
if (!config || !config.parentModalId) {
|
|
504
|
+
return 0;
|
|
505
|
+
}
|
|
506
|
+
return 1 + this.getModalDepth(config.parentModalId);
|
|
507
|
+
}
|
|
508
|
+
|
|
493
509
|
getModalGroupSize(modalId) {
|
|
494
510
|
const rootId = this.getRootModal(modalId);
|
|
495
511
|
const descendants = this.getAllDescendants(rootId);
|
|
@@ -42,6 +42,9 @@
|
|
|
42
42
|
footer_buttons: Array - Footer buttons (default: [])
|
|
43
43
|
extra_class: String - Additional CSS classes (default: "")
|
|
44
44
|
extra_data: Hash - Additional data attributes (default: {})
|
|
45
|
+
header_bg: String - Custom header background (e.g., "#3b82f6" or "linear-gradient(135deg, #667eea, #764ba2)")
|
|
46
|
+
header_text: String - Custom header text color (e.g., "#ffffff")
|
|
47
|
+
header_border: String - Custom header border color (e.g., "#2563eb")
|
|
45
48
|
%>
|
|
46
49
|
<%
|
|
47
50
|
# Default values
|
|
@@ -86,6 +89,9 @@
|
|
|
86
89
|
extra_class ||= ""
|
|
87
90
|
extra_data ||= {}
|
|
88
91
|
mobile_default_maximized ||= false
|
|
92
|
+
header_bg ||= nil
|
|
93
|
+
header_text ||= nil
|
|
94
|
+
header_border ||= nil
|
|
89
95
|
|
|
90
96
|
# Build CSS classes
|
|
91
97
|
modal_classes = ["rmm-modal", "rmm-size-#{size}", "rmm-position-#{position}"]
|
|
@@ -122,6 +128,9 @@
|
|
|
122
128
|
data_attrs[:rmm_modal_persistent_id_value] = persistent_id if persistent_id
|
|
123
129
|
data_attrs[:rmm_modal_max_width_value] = max_width if max_width
|
|
124
130
|
data_attrs[:rmm_modal_max_height_value] = max_height if max_height
|
|
131
|
+
data_attrs[:rmm_modal_header_bg_value] = header_bg if header_bg
|
|
132
|
+
data_attrs[:rmm_modal_header_text_value] = header_text if header_text
|
|
133
|
+
data_attrs[:rmm_modal_header_border_value] = header_border if header_border
|
|
125
134
|
data_attrs.merge!(extra_data)
|
|
126
135
|
%>
|
|
127
136
|
|