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.
- bitunix_automated_crypto_trading/AsyncThreadRunner.py +81 -81
- bitunix_automated_crypto_trading/BitunixApi.py +278 -278
- bitunix_automated_crypto_trading/BitunixSignal.py +1099 -1099
- bitunix_automated_crypto_trading/BitunixWebSocket.py +254 -254
- bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py +74 -74
- bitunix_automated_crypto_trading/NotificationManager.py +23 -23
- bitunix_automated_crypto_trading/ThreadManager.py +68 -68
- bitunix_automated_crypto_trading/TickerManager.py +635 -635
- bitunix_automated_crypto_trading/bitunix.py +597 -594
- bitunix_automated_crypto_trading/config.py +90 -90
- bitunix_automated_crypto_trading/logger.py +84 -84
- {bitunix_automated_crypto_trading-2.6.7.dist-info → bitunix_automated_crypto_trading-2.6.8.dist-info}/METADATA +36 -36
- bitunix_automated_crypto_trading-2.6.8.dist-info/RECORD +17 -0
- bitunix_automated_crypto_trading/config.txt +0 -60
- bitunix_automated_crypto_trading/sampleenv.txt +0 -5
- bitunix_automated_crypto_trading/static/chart.css +0 -28
- bitunix_automated_crypto_trading/static/chart.js +0 -362
- bitunix_automated_crypto_trading/static/modal.css +0 -68
- bitunix_automated_crypto_trading/static/modal.js +0 -147
- bitunix_automated_crypto_trading/static/script.js +0 -166
- bitunix_automated_crypto_trading/static/styles.css +0 -118
- bitunix_automated_crypto_trading/templates/charts.html +0 -98
- bitunix_automated_crypto_trading/templates/login.html +0 -19
- bitunix_automated_crypto_trading/templates/main.html +0 -551
- bitunix_automated_crypto_trading/templates/modal-chart.html +0 -26
- bitunix_automated_crypto_trading/templates/modal-config.html +0 -34
- bitunix_automated_crypto_trading/templates/modal-logs.html +0 -15
- bitunix_automated_crypto_trading-2.6.7.dist-info/RECORD +0 -31
- {bitunix_automated_crypto_trading-2.6.7.dist-info → bitunix_automated_crypto_trading-2.6.8.dist-info}/WHEEL +0 -0
- {bitunix_automated_crypto_trading-2.6.7.dist-info → bitunix_automated_crypto_trading-2.6.8.dist-info}/entry_points.txt +0 -0
- {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
|
-
}
|