1
+ let minimizedModalsByType = { } ;
2
+ let modalCounter = 0 ;
3
+
1
4
export default {
2
- show ( { id = "custom-modal" , title = "" , content = "" , buttons = [ ] } ) {
5
+ show ( {
6
+ id = "custom-modal" ,
7
+ title = "" ,
8
+ content = "" ,
9
+ buttons = [ ] ,
10
+ type = "default" ,
11
+ } ) {
3
12
this . setNoSelect ( ) ;
4
-
5
- let modal = document . getElementById ( id ) ;
6
- if ( ! modal ) {
7
- modal = document . createElement ( "div" ) ;
8
- modal . id = id ;
9
- modal . className =
10
- "fixed bottom-0 left-0 w-full bg-white shadow-xl rounded-t-xl transform translate-y-full transition-transform duration-300 z-50" ;
11
- modal . innerHTML = `
13
+ if ( document . getElementById ( id ) ) {
14
+ id = id + "-" + ++ modalCounter ;
15
+ }
16
+ let modal = document . createElement ( "div" ) ;
17
+ modal . id = id ;
18
+ modal . dataset . type = type ;
19
+ modal . className =
20
+ "fixed bottom-0 left-0 w-full bg-white shadow-xl rounded-t-xl transform translate-y-full transition-transform duration-300 z-50" ;
21
+ modal . innerHTML = `
12
22
<div class="cursor-pointer w-full bg-gray-300 h-1.5 rounded-full mt-2 mx-auto max-w-16"></div>
13
23
<div class="mx-auto p-4 border-b">
14
24
<div class="max-w-7xl mx-auto flex items-center justify-between">
@@ -20,25 +30,21 @@ export default {
20
30
${ content }
21
31
</div>
22
32
` ;
23
- document . body . appendChild ( modal ) ;
24
-
25
- this . updateButtons ( id , buttons ) ;
26
-
27
- this . enableDrag ( id ) ;
28
-
29
- setTimeout ( ( ) => {
30
- modal . style . transform = "translateY(0)" ;
31
- } , 10 ) ;
32
- }
33
+ document . body . appendChild ( modal ) ;
34
+ this . updateButtons ( id , buttons ) ;
35
+ this . enableDrag ( id ) ;
36
+ setTimeout ( ( ) => {
37
+ modal . style . transform = "translateY(0px)" ;
38
+ } , 10 ) ;
33
39
} ,
34
40
35
41
hide ( id = "custom-modal" ) {
36
42
this . removeNoSelect ( ) ;
37
-
38
43
const modal = document . getElementById ( id ) ;
39
44
if ( modal ) {
40
45
modal . style . transform = "translateY(100%)" ;
41
46
setTimeout ( ( ) => modal . remove ( ) , 300 ) ;
47
+ this . _removeMinimized ( modal ) ;
42
48
}
43
49
} ,
44
50
@@ -50,15 +56,21 @@ export default {
50
56
updateButtons ( id , buttons ) {
51
57
const buttonsContainer = document . getElementById ( `${ id } -buttons` ) ;
52
58
if ( ! buttonsContainer ) return ;
53
-
54
59
buttonsContainer . innerHTML = "" ;
55
- buttons . forEach ( ( { id, text, onClick, hidden = false } ) => {
60
+ buttons . forEach ( ( { id, text, icon , onClick, hidden = false } ) => {
56
61
const btn = document . createElement ( "button" ) ;
57
62
btn . id = `${ id } ` ;
58
- btn . textContent = text ;
59
- btn . className = `px-4 py-1 rounded ${
60
- hidden ? "hidden" : ""
61
- } bg-blue-500 text-white hover:bg-blue-600`;
63
+ if ( icon ) {
64
+ btn . innerHTML = `<span class="material-symbols-outlined" aria-label="${ text } ">${ icon } </span>` ;
65
+ btn . className = `px-4 py-1 rounded ${
66
+ hidden ? "hidden" : ""
67
+ } text-gray-700`;
68
+ } else {
69
+ btn . textContent = text ;
70
+ btn . className = `px-4 py-1 rounded ${
71
+ hidden ? "hidden" : ""
72
+ } bg-blue-500 text-white hover:bg-blue-600`;
73
+ }
62
74
btn . addEventListener ( "click" , onClick ) ;
63
75
buttonsContainer . appendChild ( btn ) ;
64
76
} ) ;
@@ -74,39 +86,99 @@ export default {
74
86
isDragging = false ;
75
87
let modal = document . getElementById ( id ) ;
76
88
let pill = modal . querySelector ( ".cursor-pointer" ) ;
77
-
78
- let initialTransform = 100 ;
79
- let currentTransform = initialTransform ;
80
-
89
+ let initialTransform = 0 ;
90
+ let currentTransform = 0 ;
91
+ const anchorThreshold = 150 ;
81
92
pill . addEventListener ( "mousedown" , ( e ) => {
82
93
isDragging = true ;
83
94
startY = e . clientY ;
84
- initialTransform =
85
- parseFloat ( getComputedStyle ( modal ) . transform . split ( "," ) [ 5 ] ) || 0 ;
95
+ const transform = getComputedStyle ( modal ) . transform ;
96
+ if ( transform !== "none" ) {
97
+ const matrix = transform . match ( / m a t r i x .* \( ( .+ ) \) / ) [ 1 ] . split ( ", " ) ;
98
+ initialTransform = parseFloat ( matrix [ 5 ] ) ;
99
+ } else {
100
+ initialTransform = 0 ;
101
+ }
86
102
modal . style . transition = "none" ;
87
103
} ) ;
88
-
89
104
document . addEventListener ( "mousemove" , ( e ) => {
90
105
if ( ! isDragging ) return ;
91
106
let diff = e . clientY - startY ;
92
- let newTransform = Math . min ( Math . max ( initialTransform + diff , 0 ) , 100 ) ;
93
- modal . style . transform = `translateY(${ newTransform } %)` ;
107
+ let newTransform = initialTransform + diff ;
108
+ newTransform = Math . max ( newTransform , 0 ) ;
109
+ modal . style . transform = `translateY(${ newTransform } px)` ;
94
110
currentTransform = newTransform ;
95
111
} ) ;
96
-
97
112
document . addEventListener ( "mouseup" , ( ) => {
98
113
if ( ! isDragging ) return ;
99
114
isDragging = false ;
100
- modal . style . transition = "transform 0.3s ease" ;
101
- if ( currentTransform > 70 ) {
102
- modal . style . transform = "translateY(100%)" ;
103
- setTimeout ( ( ) => modal . remove ( ) , 300 ) ;
115
+ modal . style . transition = "all 0.3s ease" ;
116
+ if ( currentTransform >= anchorThreshold ) {
117
+ this . minimizeModal ( modal ) ;
104
118
} else {
105
- modal . style . transform = "translateY(0 )" ;
119
+ modal . style . transform = "translateY(0px )" ;
106
120
}
107
121
} ) ;
108
122
} ,
109
123
124
+ minimizeModal ( modal ) {
125
+ modal . style . transform = "translateY(0px)" ;
126
+ modal . style . height = "74px" ;
127
+ modal . style . width = "200px" ;
128
+ modal . style . bottom = "10px" ;
129
+ modal . style . zIndex = "0" ;
130
+ let type = modal . dataset . type || "default" ;
131
+ if ( ! minimizedModalsByType [ type ] ) {
132
+ minimizedModalsByType [ type ] = [ ] ;
133
+ }
134
+ const arr = minimizedModalsByType [ type ] ;
135
+ const index = arr . length ;
136
+ arr . push ( modal ) ;
137
+ const spacing = 10 ;
138
+ const modalWidth = 200 ;
139
+ let left = 10 + index * ( modalWidth + spacing ) ;
140
+ const maxLeft = window . innerWidth - modalWidth - 10 ;
141
+ if ( left > maxLeft ) left = maxLeft ;
142
+ modal . style . left = `${ left } px` ;
143
+ modal . classList . add ( "rounded-xl" ) ;
144
+ const title = modal . querySelector ( ".font-semibold" ) ;
145
+ if ( title ) {
146
+ title . classList . add ( "truncate" , "text-sm" ) ;
147
+ }
148
+ const restoreHandler = ( e ) => {
149
+ modal . style . transition = "all 0.3s ease" ;
150
+ modal . style . height = "" ;
151
+ modal . style . width = "100%" ;
152
+ modal . style . left = "0" ;
153
+ modal . style . zIndex = "50" ;
154
+ modal . classList . remove ( "rounded-xl" ) ;
155
+ if ( title ) {
156
+ title . classList . remove ( "truncate" , "text-sm" ) ;
157
+ }
158
+ modal . removeEventListener ( "click" , restoreHandler ) ;
159
+ this . _removeMinimized ( modal ) ;
160
+ } ;
161
+ modal . addEventListener ( "click" , restoreHandler ) ;
162
+ modal . _restoreHandler = restoreHandler ;
163
+ } ,
164
+
165
+ _removeMinimized ( modal ) {
166
+ let type = modal . dataset . type || "default" ;
167
+ if ( minimizedModalsByType [ type ] ) {
168
+ minimizedModalsByType [ type ] = minimizedModalsByType [ type ] . filter (
169
+ ( m ) => m !== modal
170
+ ) ;
171
+ minimizedModalsByType [ type ] . forEach ( ( m , i ) => {
172
+ const spacing = 10 ;
173
+ const modalWidth = 200 ;
174
+ let left = 10 + i * ( modalWidth + spacing ) ;
175
+ const maxLeft = window . innerWidth - modalWidth - 10 ;
176
+ if ( left > maxLeft ) left = maxLeft ;
177
+ m . style . left = `${ left } px` ;
178
+ } ) ;
179
+ }
180
+ } ,
181
+
110
182
setNoSelect ( ) {
111
183
document . body . style . userSelect = "none" ;
112
184
} ,
0 commit comments