bitunix-automated-crypto-trading 2.6.7__py3-none-any.whl → 2.6.8__py3-none-any.whl

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.
Files changed (31) hide show
  1. bitunix_automated_crypto_trading/AsyncThreadRunner.py +81 -81
  2. bitunix_automated_crypto_trading/BitunixApi.py +278 -278
  3. bitunix_automated_crypto_trading/BitunixSignal.py +1099 -1099
  4. bitunix_automated_crypto_trading/BitunixWebSocket.py +254 -254
  5. bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py +74 -74
  6. bitunix_automated_crypto_trading/NotificationManager.py +23 -23
  7. bitunix_automated_crypto_trading/ThreadManager.py +68 -68
  8. bitunix_automated_crypto_trading/TickerManager.py +635 -635
  9. bitunix_automated_crypto_trading/bitunix.py +597 -594
  10. bitunix_automated_crypto_trading/config.py +90 -90
  11. bitunix_automated_crypto_trading/logger.py +84 -84
  12. {bitunix_automated_crypto_trading-2.6.7.dist-info → bitunix_automated_crypto_trading-2.6.8.dist-info}/METADATA +36 -36
  13. bitunix_automated_crypto_trading-2.6.8.dist-info/RECORD +17 -0
  14. bitunix_automated_crypto_trading/config.txt +0 -60
  15. bitunix_automated_crypto_trading/sampleenv.txt +0 -5
  16. bitunix_automated_crypto_trading/static/chart.css +0 -28
  17. bitunix_automated_crypto_trading/static/chart.js +0 -362
  18. bitunix_automated_crypto_trading/static/modal.css +0 -68
  19. bitunix_automated_crypto_trading/static/modal.js +0 -147
  20. bitunix_automated_crypto_trading/static/script.js +0 -166
  21. bitunix_automated_crypto_trading/static/styles.css +0 -118
  22. bitunix_automated_crypto_trading/templates/charts.html +0 -98
  23. bitunix_automated_crypto_trading/templates/login.html +0 -19
  24. bitunix_automated_crypto_trading/templates/main.html +0 -551
  25. bitunix_automated_crypto_trading/templates/modal-chart.html +0 -26
  26. bitunix_automated_crypto_trading/templates/modal-config.html +0 -34
  27. bitunix_automated_crypto_trading/templates/modal-logs.html +0 -15
  28. bitunix_automated_crypto_trading-2.6.7.dist-info/RECORD +0 -31
  29. {bitunix_automated_crypto_trading-2.6.7.dist-info → bitunix_automated_crypto_trading-2.6.8.dist-info}/WHEEL +0 -0
  30. {bitunix_automated_crypto_trading-2.6.7.dist-info → bitunix_automated_crypto_trading-2.6.8.dist-info}/entry_points.txt +0 -0
  31. {bitunix_automated_crypto_trading-2.6.7.dist-info → bitunix_automated_crypto_trading-2.6.8.dist-info}/top_level.txt +0 -0
@@ -1,362 +0,0 @@
1
- const charts = {};
2
-
3
- const createOrUpdateChart = (
4
- chartId, data, buysell, ema_study, ema_display, macd_study, macd_display, bbm_study, bbm_display, rsi_study, rsi_display, timeUnit
5
- ) => {
6
- const datasets = [];
7
- const scales = {}; // Dynamic y-axis configuration
8
-
9
- const studyColors = {
10
- ema_slow: 'rgba(0, 123, 255, 0.7)', // Blue for EMA Slow
11
- ema_medium: 'rgba(255, 123, 0, 0.7)', // Orange for EMA Medium
12
- ema_fast: 'rgba(0, 255, 123, 0.7)', // Green for EMA Fast
13
- macd_line: 'rgba(255, 0, 0, 0.7)', // Red for MACD Line
14
- macd_signal: 'rgba(123, 0, 255, 0.7)', // Purple for MACD Signal
15
- macd_histogram: 'rgba(123, 123, 123, 0.7)', // Gray for Histogram
16
- bbl: 'rgba(0, 0, 255, 0.7)', // Dark Blue for BBL
17
- bbm: 'rgba(26, 26, 17, 0.7)', // Dark Gray for BBM
18
- bbu: 'rgba(0, 123, 0, 0.7)', // Green for BBU
19
- rsi_slow: 'rgba(112, 91, 99, 0.7)', // Purple for RSI Slow
20
- rsi_fast: 'rgba(156, 23, 23, 0.7)', // Red for RSI Fast
21
- };
22
-
23
- const candlestickData = data.map(d => ({
24
- x: parseInt(d.time),
25
- o: parseFloat(d.open),
26
- h: parseFloat(d.high),
27
- l: parseFloat(d.low),
28
- c: parseFloat(d.close)
29
- }));
30
-
31
- const mapOptionalData = (data, key, yKey = key.replace('_', '')) => {
32
- return data.filter(d => typeof d[key] !== 'undefined' && parseFloat(d[key]) !== 0).map(d => ({
33
- x: parseInt(d.time),
34
- y: parseFloat(d[key])
35
- }));
36
- };
37
-
38
- const slowMA = mapOptionalData(data, 'ma_slow');
39
- const mediumMA = mapOptionalData(data, 'ma_medium');
40
- const fastMA = mapOptionalData(data, 'ma_fast');
41
- const macdLine = mapOptionalData(data, 'MACD_Line');
42
- const signalLine = mapOptionalData(data, 'MACD_Signal');
43
- const macdHistogram = mapOptionalData(data, 'MACD_Histogram');
44
- const BBL = mapOptionalData(data, 'BBL');
45
- const BBM = mapOptionalData(data, 'BBM');
46
- const BBU = mapOptionalData(data, 'BBU');
47
- const rsi_slow = mapOptionalData(data, 'rsi_slow');
48
- const rsi_fast = mapOptionalData(data, 'rsi_fast');
49
-
50
- // Find the min and max time from candlestickData
51
- const times = candlestickData.map(d => d.x);
52
- const minTime = Math.min(...times);
53
- const maxTime = Math.max(...times);
54
-
55
- const buyselldata = buysell.filter(d => {
56
- const time = parseInt(d.ctime);
57
- return time >= minTime && time <= maxTime;
58
- }).map(d => ({
59
- x: parseInt(d.ctime),
60
- y: parseFloat(d.price),
61
- side: d.side,
62
- backgroundColor: d.side === 'BUY' ? 'rgba(0, 255, 0, 0.9)' : 'rgba(255, 0, 0, 0.9)', // Color based on buy/sell
63
- borderColor: d.side === 'BUY' ? 'rgba(0, 255, 0, 1)' : 'rgba(255, 0, 0, 1)',
64
- borderWidth: 2,
65
- pointRadius: 5, // Increased for better visibility
66
- pointStyle: 'triangle' // Changed for better visibility
67
- }));
68
-
69
- const timeSettings = {
70
- second: { unit: 'second', stepSize: 1, displayFormats: { second: 'HH:mm:ss' } },
71
- minute: { unit: 'minute', stepSize: 1, displayFormats: { minute: 'HH:mm' } },
72
- hour: { unit: 'hour', stepSize: 1, displayFormats: { hour: 'HH:mm' } },
73
- day: { unit: 'day', stepSize: 1, displayFormats: { day: 'MMM dd' } }
74
- };
75
-
76
- const timeConfig = timeSettings[timeUnit] || timeSettings['second'];
77
-
78
-
79
- // Candlestick Data
80
- scales['y-axis'] = {
81
- type: 'linear',
82
- position: 'left',
83
- title: { display: true, text: 'CandleStick' }
84
- };
85
-
86
- datasets.push({
87
- type: 'candlestick',
88
- label: 'Candlestick Data',
89
- data: candlestickData,
90
- borderColor: 'rgba(0, 0, 0, 1)',
91
- backgroundColor: 'rgba(75, 192, 192, 0.2)',
92
- yAxisID: 'y-axis' // Unique y-axis ID
93
- });
94
-
95
- // EMA Study
96
- if (ema_study) {
97
- datasets.push({
98
- label: 'EMA Slow',
99
- data: slowMA,
100
- backgroundColor: studyColors.ema_slow,
101
- borderColor: studyColors.ema_slow,
102
- borderWidth: 1,
103
- fill: false,
104
- type: 'line',
105
- pointRadius: 0,
106
- yAxisID: 'y-axis',
107
- hidden: true
108
- });
109
-
110
- datasets.push({
111
- label: 'EMA Medium',
112
- data: mediumMA,
113
- backgroundColor: studyColors.ema_medium,
114
- borderColor: studyColors.ema_medium,
115
- borderWidth: 1,
116
- fill: false,
117
- type: 'line',
118
- pointRadius: 0,
119
- yAxisID: 'y-axis',
120
- hidden: !ema_display // Hide if not displayed
121
- });
122
-
123
- datasets.push({
124
- label: 'EMA Fast',
125
- data: fastMA,
126
- backgroundColor: studyColors.ema_fast,
127
- borderColor: studyColors.ema_fast,
128
- borderWidth: 1,
129
- fill: false,
130
- type: 'line',
131
- pointRadius: 0,
132
- yAxisID: 'y-axis',
133
- hidden: !ema_display // Hide if not displayed
134
- });
135
- }
136
-
137
- // BBM Study
138
- if (bbm_study) {
139
- datasets.push({
140
- label: 'BBU',
141
- data: BBU,
142
- backgroundColor: studyColors.bbu,
143
- borderColor: studyColors.bbu,
144
- borderWidth: 1,
145
- fill: false,
146
- type: 'line',
147
- pointRadius: 0,
148
- yAxisID: 'y-axis',
149
- hidden: !bbm_display // Hide if not displayed
150
- });
151
-
152
- datasets.push({
153
- label: 'BBM',
154
- data: BBM,
155
- backgroundColor: studyColors.bbm,
156
- borderColor: studyColors.bbm,
157
- borderWidth: 1,
158
- fill: false,
159
- type: 'line',
160
- pointRadius: 0,
161
- yAxisID: 'y-axis',
162
- hidden: !bbm_display // Hide if not displayed
163
- });
164
-
165
- datasets.push({
166
- label: 'BBL',
167
- data: BBL,
168
- backgroundColor: studyColors.bbl,
169
- borderColor: studyColors.bbl,
170
- borderWidth: 1,
171
- fill: false,
172
- type: 'line',
173
- pointRadius: 0,
174
- yAxisID: 'y-axis',
175
- hidden: !bbm_display // Hide if not displayed
176
- });
177
- }
178
-
179
- // MACD Study
180
- if (macd_study) {
181
-
182
- scales['y-axis-macd'] = {
183
- type: 'linear',
184
- position: 'left',
185
- title: { display: true, text: 'MACD' }
186
- };
187
-
188
- datasets.push({
189
- label: 'MACD Line',
190
- data: macdLine,
191
- backgroundColor: studyColors.macd_line,
192
- borderColor: studyColors.macd_line,
193
- borderWidth: 1,
194
- fill: false,
195
- type: 'line',
196
- pointRadius: 0,
197
- yAxisID: 'y-axis-macd',
198
- hidden: !macd_display // Hide if not displayed
199
- });
200
-
201
- datasets.push({
202
- label: 'MACD Signal',
203
- data: signalLine,
204
- backgroundColor: studyColors.macd_signal,
205
- borderColor: studyColors.macd_signal,
206
- borderWidth: 1,
207
- fill: false,
208
- type: 'line',
209
- pointRadius: 0,
210
- yAxisID: 'y-axis-macd',
211
- hidden: !macd_display // Hide if not displayed
212
- });
213
-
214
- scales['y-axis-macd-histogram'] = {
215
- type: 'linear',
216
- position: 'left',
217
- title: { display: true, text: 'MACD Histogram' }
218
- };
219
- datasets.push({
220
- label: 'MACD Histogram',
221
- data: macdHistogram,
222
- backgroundColor: studyColors.macd_histogram,
223
- borderColor: studyColors.macd_histogram,
224
- borderWidth: 0,
225
- type: 'bar',
226
- yAxisID: 'y-axis-macd-histogram',
227
- hidden: true
228
- });
229
- }
230
-
231
- // RSI Study
232
- if (rsi_study) {
233
-
234
- scales['y-axis-rsi'] = {
235
- type: 'linear',
236
- position: 'left',
237
- title: { display: true, text: 'RSI' }
238
- };
239
-
240
- datasets.push({
241
- label: 'RSI SLOW',
242
- data: rsi_slow,
243
- backgroundColor: studyColors.rsi_slow,
244
- borderColor: studyColors.rsi_slow,
245
- borderWidth: 1,
246
- fill: false,
247
- type: 'line',
248
- pointRadius: 0,
249
- yAxisID: 'y-axis-rsi',
250
- hidden: !rsi_display // Hide if not displayed
251
- });
252
-
253
- datasets.push({
254
- label: 'RSI FAST',
255
- data: rsi_fast,
256
- backgroundColor: studyColors.rsi_fast,
257
- borderColor: studyColors.rsi_fast,
258
- borderWidth: 1,
259
- fill: false,
260
- type: 'line',
261
- pointRadius: 0,
262
- yAxisID: 'y-axis-rsi',
263
- hidden: !rsi_display // Hide if not displayed
264
- });
265
- }
266
-
267
- // Buy/Sell data points
268
- datasets.push({
269
- label: 'BuySell',
270
- data: buyselldata,
271
- type: 'scatter',
272
- backgroundColor: buyselldata.map(d => d.backgroundColor),
273
- borderColor: buyselldata.map(d => d.borderColor),
274
- borderWidth: buyselldata.map(d => d.borderWidth),
275
- pointRadius: buyselldata.map(d => d.pointRadius),
276
- pointStyle: buyselldata.map(d => d.pointStyle),
277
- yAxisID: 'y-axis' // Unique y-axis ID
278
- });
279
-
280
-
281
- // Define chart configuration
282
- const chartConfig = {
283
- type: 'candlestick',
284
- data: { datasets },
285
- options: {
286
- scales: {
287
- x: {
288
- type: 'time',
289
- time: timeConfig
290
- },
291
- ...scales // Dynamically include y-axis configurations
292
- },
293
- plugins: {
294
- responsive: true,
295
- tooltip: {
296
- callbacks: {
297
- label: function (context) {
298
-
299
- // Handle cases where context.parsed might be undefined
300
- if (!context.parsed) {
301
- return `${context.dataset.label}: Data unavailable`;
302
- }
303
-
304
- const x = context.parsed.x !== undefined ? context.parsed.x : 'N/A';
305
- const y = context.parsed.y !== undefined ? context.parsed.y : 'N/A';
306
-
307
- // Check if the dataset is buysell
308
- if (context.dataset.label === 'BuySell') {
309
- const side = context.raw.side !== undefined ? context.raw.side : 'N/A'; // Side
310
- const price = context.raw.y !== undefined ? context.raw.y : 'N/A'; // Price
311
- const time = context.raw.x !== undefined ? context.raw.x : 'N/A'; // Time
312
- return `${context.dataset.label}: (Side: ${side}, Price: ${price}, Time: ${time})`;
313
- }
314
- // Check if the dataset is candlestick
315
- else if (context.dataset.type === 'candlestick') {
316
- const o = context.raw.o !== undefined ? context.raw.o : 'N/A'; // Open
317
- const h = context.raw.h !== undefined ? context.raw.h : 'N/A'; // High
318
- const l = context.raw.l !== undefined ? context.raw.l : 'N/A'; // Low
319
- const c = context.raw.c !== undefined ? context.raw.c : 'N/A'; // Close
320
- return `${context.dataset.label}: (Open: ${o}, High: ${h}, Low: ${l}, Close: ${c})`;
321
- }
322
- else {
323
- // Default for other datasets
324
- return `${context.dataset.label}: (${x}, ${y})`;
325
- }
326
- }
327
- }
328
- }
329
-
330
- }
331
- }
332
- };
333
-
334
- // Render or update the chart
335
- if (charts[chartId]) {
336
- // Assign only the 'data' of the specified dataset (indexed by x)
337
- chartConfig.data.datasets.forEach((dataset, index) => {
338
- if (charts[chartId].data.datasets[index]) {
339
- charts[chartId].data.datasets[index].data = dataset.data; // Update only the data property
340
- }
341
- });
342
- charts[chartId].options = chartConfig.options;
343
- charts[chartId].update();
344
- }
345
- else {
346
- if (charts[chartId]) {
347
- charts[chartId].destroy(); // Destroy the existing chart instance
348
- delete charts[chartId]
349
- }
350
-
351
- const chartContainer = document.querySelector(`.chart-container#${chartId}`);
352
- chartContainer.innerHTML=""
353
- const newCanvas = document.createElement('canvas');
354
- newCanvas.id = chartId;
355
- chartContainer.appendChild(newCanvas);
356
-
357
- const ctx = newCanvas.getContext('2d');
358
- charts[chartId] = new Chart(ctx, chartConfig);
359
- }
360
- };
361
-
362
-
@@ -1,68 +0,0 @@
1
- .scrollable-view {
2
- height: 80%;
3
- overflow-y: auto; /* Enable vertical scrolling */
4
- margin-bottom: 20px;
5
- }
6
-
7
- .modal-overlay {
8
- display: none; /* Hidden initially */
9
- position: fixed;
10
- top: 0;
11
- left: 0;
12
- width: 100%;
13
- height: 100%;
14
- background: rgba(0, 0, 0, 0.5);
15
- z-index: 999;
16
- }
17
-
18
- .modal-overlay.modal-open { /* Only show overlay when modal is active */
19
- display: block;
20
- }
21
-
22
- .modal-open {
23
- display: block;
24
- }
25
-
26
- .modal-container { /* Added container for positioning */
27
- width: 100%;
28
- height: 100vw; /* Set height equal to the screen width */
29
- max-height: 100vw; /* Ensure height does not exceed the width */
30
- position: fixed;
31
- z-index: 1000;
32
- top: 0;
33
- left: 0;
34
- width: 100%;
35
- height: 100%;
36
- display: flex;
37
- justify-content: center;
38
- align-items: center;
39
- pointer-events: none; /* Allow clicks to pass through when hidden */
40
- }
41
-
42
- .modal {
43
- width: 100%;
44
- height: 100vw; /* Set height equal to the screen width */
45
- max-height: 100vw; /* Ensure height does not exceed the width */
46
- background-color: white;
47
- padding: 20px;
48
- border: 1px solid #ccc;
49
- box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
50
- resize: none; /* Disable default resizing */
51
- pointer-events: auto; /* Make modal interactable */
52
- }
53
-
54
- .modal .resize-handle {
55
- position: absolute;
56
- width: 10px;
57
- height: 10px;
58
- background-color: gray;
59
- cursor: nwse-resize; /* Change cursor to indicate resizing */
60
- }
61
-
62
- .resize-top-right { top: 0; right: 0; cursor: nesw-resize; }
63
- .resize-bottom-right { bottom: 0; right: 0; cursor: nwse-resize; }
64
- .resize-bottom-left { bottom: 0; left: 0; cursor: nesw-resize; }
65
- .resize-top-left { top: 0; left: 0; cursor: nwse-resize; }
66
-
67
-
68
-
@@ -1,147 +0,0 @@
1
- function initializeModal(containerId, overlayId, modalId, options = {}) {
2
- const modal = document.getElementById(modalId);
3
- const overlay = document.getElementById(overlayId);
4
- const modalContainer = document.getElementById(containerId); // Assuming a fixed container
5
-
6
- if (!modal || !overlay || !modalContainer) {
7
- console.error('Modal or overlay elements not found.');
8
- return;
9
- }
10
-
11
- const {
12
- top = '10%',
13
- left = '10%',
14
- width = '80%',
15
- height = '80%',
16
- maxWidth = '80vw',
17
- maxHeight = '80vw',
18
- allowMove = true,
19
- allowResize = true,
20
- onClose = () => {}
21
- } = options;
22
-
23
- // Apply initial styles
24
- modal.style.top = top; // Position will be handled by the container
25
- modal.style.left = left; // Position will be handled by the container
26
- modal.style.width = width;
27
- modal.style.height = height;
28
- modal.style.maxWidth = maxWidth;
29
- modal.style.maxHeight = maxHeight;
30
- modalContainer.style.justifyContent = left === 'center' ? 'center' : (isNaN(parseFloat(left)) ? left : '');
31
- modalContainer.style.alignItems = top === 'center' ? 'center' : (isNaN(parseFloat(top)) ? top : '');
32
- if (!isNaN(parseFloat(left))) {
33
- modalContainer.style.paddingLeft = left;
34
- modalContainer.style.justifyContent = 'flex-start';
35
- }
36
- if (!isNaN(parseFloat(top))) {
37
- modalContainer.style.paddingTop = top;
38
- modalContainer.style.alignItems = 'flex-start';
39
- }
40
- modalContainer.style.pointerEvents = 'none';
41
- modal.style.pointerEvents = 'auto';
42
-
43
- let isDragging = false;
44
- let offsetX, offsetY;
45
-
46
- if (allowMove) {
47
- modal.addEventListener('mousedown', (event) => {
48
- isDragging = true;
49
- offsetX = event.clientX - modal.getBoundingClientRect().left;
50
- offsetY = event.clientY - modal.getBoundingClientRect().top;
51
- modal.style.cursor = 'move';
52
- });
53
-
54
- document.addEventListener('mousemove', (event) => {
55
- if (isDragging) {
56
- const newLeft = event.clientX - offsetX;
57
- const newTop = event.clientY - offsetY;
58
-
59
- // Keep modal within viewport (optional, but good practice)
60
- const viewportWidth = window.innerWidth;
61
- const viewportHeight = window.innerHeight;
62
- const modalWidth = modal.offsetWidth;
63
- const modalHeight = modal.offsetHeight;
64
-
65
- modal.style.left = `${Math.max(0, Math.min(newLeft, viewportWidth - modalWidth))}px`;
66
- modal.style.top = `${Math.max(0, Math.min(newTop, viewportHeight - modalHeight))}px`;
67
- }
68
- });
69
-
70
- document.addEventListener('mouseup', () => {
71
- isDragging = false;
72
- modal.style.cursor = 'default';
73
- });
74
- } else {
75
- modal.style.cursor = 'default'; // Prevent move cursor if not allowed
76
- }
77
-
78
- if (allowResize) {
79
- const resizeHandles = modal.querySelectorAll('.resize-handle');
80
- resizeHandles.forEach((handle) => {
81
- handle.addEventListener('mousedown', (event) => {
82
- event.stopPropagation(); // Prevent dragging from starting on resize handle
83
- const modalRect = modal.getBoundingClientRect();
84
- const isTop = handle.classList.contains('resize-top-right') || handle.classList.contains('resize-top-left');
85
- const isBottom = handle.classList.contains('resize-bottom-right') || handle.classList.contains('resize-bottom-left');
86
- const isLeft = handle.classList.contains('resize-bottom-left') || handle.classList.contains('resize-top-left');
87
- const isRight = handle.classList.contains('resize-bottom-right') || handle.classList.contains('resize-top-right');
88
-
89
- document.addEventListener('mousemove', (resizeEvent) => {
90
- if (isTop) {
91
- const deltaY = resizeEvent.clientY - modalRect.top;
92
- modal.style.height = `${modalRect.bottom - resizeEvent.clientY}px`;
93
- modal.style.top = `${resizeEvent.clientY}px`;
94
- }
95
- if (isBottom) {
96
- modal.style.height = `${resizeEvent.clientY - modalRect.top}px`;
97
- }
98
- if (isLeft) {
99
- const deltaX = resizeEvent.clientX - modalRect.left;
100
- modal.style.width = `${modalRect.right - resizeEvent.clientX}px`;
101
- modal.style.left = `${resizeEvent.clientX}px`;
102
- }
103
- if (isRight) {
104
- modal.style.width = `${resizeEvent.clientX - modalRect.left}px`;
105
- }
106
- });
107
-
108
- const stopResize = () => {
109
- document.removeEventListener('mousemove', null);
110
- document.removeEventListener('mouseup', stopResize);
111
- };
112
- document.addEventListener('mouseup', stopResize);
113
- });
114
- });
115
- } else {
116
- const resizeHandles = modal.querySelectorAll('.resize-handle');
117
- resizeHandles.forEach(handle => handle.style.display = 'none');
118
- }
119
-
120
- const openModal = () => {
121
- overlay.classList.add('modal-open');
122
- modalContainer.classList.add('modal-open');
123
- modal.classList.add('modal-open');
124
- modalContainer.style.pointerEvents = 'auto';
125
- };
126
-
127
- const closeModal = () => {
128
- overlay.classList.remove('modal-open');
129
- modalContainer.classList.remove('modal-open');
130
- modal.classList.remove('modal-open');
131
- modalContainer.style.pointerEvents = 'none';
132
- onClose();
133
- };
134
-
135
- const closeButton = modal.querySelector('#close-modal');
136
- if (closeButton) {
137
- closeButton.addEventListener('click', closeModal);
138
- }
139
-
140
- overlay.addEventListener('click', (event) => {
141
- if (event.target === overlay) {
142
- closeModal();
143
- }
144
- });
145
-
146
- return { open: openModal, close: closeModal };
147
- }