@sailingrotevista/rotevista-dash 2.0.2 → 2.0.4
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.
- package/app.js +75 -18
- package/index.html +5 -5
- package/package.json +1 -1
- package/style.css +53 -29
package/app.js
CHANGED
|
@@ -283,39 +283,96 @@ function drawGraph(d, id, min, max, isTws, isHercules) {
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
// ==========================================================================
|
|
286
|
-
// 8. EVENTI E INTERAZIONI (Touch
|
|
286
|
+
// 8. EVENTI E INTERAZIONI (Smart Touch Manager)
|
|
287
287
|
// ==========================================================================
|
|
288
288
|
|
|
289
|
-
//
|
|
290
|
-
window.addEventListener('contextmenu', e => e.preventDefault());
|
|
289
|
+
// Uccide il menu contestuale di Android/iOS
|
|
290
|
+
window.addEventListener('contextmenu', e => e.preventDefault(), true);
|
|
291
291
|
|
|
292
292
|
function toggleFocusMode(type, element) {
|
|
293
293
|
const container = document.querySelector('.main-container');
|
|
294
294
|
const parentPanel = element.closest('.side-panel');
|
|
295
295
|
const isLeft = parentPanel.classList.contains('left-panel');
|
|
296
|
+
|
|
296
297
|
isFocusActive = !isFocusActive;
|
|
297
|
-
|
|
298
|
-
|
|
298
|
+
|
|
299
|
+
if (isFocusActive) {
|
|
300
|
+
container.classList.add('focus-active');
|
|
301
|
+
container.classList.add(isLeft ? 'focus-side-left' : 'focus-side-right');
|
|
302
|
+
parentPanel.classList.add('has-focus');
|
|
303
|
+
element.classList.add('is-focused');
|
|
304
|
+
} else {
|
|
305
|
+
container.classList.remove('focus-active', 'focus-side-left', 'focus-side-right');
|
|
306
|
+
document.querySelectorAll('.side-panel').forEach(p => p.classList.remove('has-focus'));
|
|
307
|
+
document.querySelectorAll('.data-box').forEach(b => b.classList.remove('is-focused'));
|
|
308
|
+
}
|
|
299
309
|
}
|
|
300
310
|
|
|
311
|
+
// Configurazione Interazioni per i 4 grafici principali
|
|
301
312
|
['stw', 'sog', 'tws', 'depth'].forEach(type => {
|
|
302
313
|
const el = document.getElementById(type + '-graph').closest('.data-box');
|
|
303
314
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
+
let lastTapTime = 0;
|
|
316
|
+
let tapTimeout;
|
|
317
|
+
|
|
318
|
+
const handleInteraction = (e) => {
|
|
319
|
+
// Impedisce al browser di fare qualsiasi cosa (zoom, menu, click fantasma)
|
|
320
|
+
if (e.cancelable) e.preventDefault();
|
|
321
|
+
|
|
322
|
+
const currentTime = new Date().getTime();
|
|
323
|
+
const tapDelay = currentTime - lastTapTime;
|
|
324
|
+
|
|
325
|
+
// --- 1. RILEVAMENTO LONG PRESS ---
|
|
326
|
+
// Avviamo un timer per la pressione lunga
|
|
327
|
+
pressTimer = setTimeout(() => {
|
|
328
|
+
if (!isFocusActive) {
|
|
329
|
+
toggleFocusMode(type, el);
|
|
330
|
+
lastTapTime = 0; // Reset per non innescare click singoli al rilascio
|
|
331
|
+
}
|
|
332
|
+
}, 800); // 800ms per attivare il Focus
|
|
333
|
+
|
|
334
|
+
// --- 2. GESTIONE DOPPIO E SINGOLO TOCCO ---
|
|
335
|
+
// Se tocchiamo di nuovo entro 300ms è un DOUBLE TAP
|
|
336
|
+
if (tapDelay < 300 && tapDelay > 0) {
|
|
337
|
+
clearTimeout(tapTimeout); // Cancella l'azione del singolo tap
|
|
338
|
+
if (!isFocusActive) {
|
|
339
|
+
// Toggle Hercules Mode
|
|
340
|
+
graphModes[type] = graphModes[type] === 'standard' ? 'hercules' : 'standard';
|
|
341
|
+
localStorage.setItem('mode_' + type, graphModes[type]);
|
|
342
|
+
el.style.backgroundColor = "rgba(255,255,255,0.15)"; setTimeout(() => el.style.backgroundColor = "", 200);
|
|
343
|
+
}
|
|
344
|
+
lastTapTime = 0;
|
|
345
|
+
} else {
|
|
346
|
+
// Se è passato più tempo, potrebbe essere un SINGOLO TAP
|
|
347
|
+
lastTapTime = currentTime;
|
|
348
|
+
tapTimeout = setTimeout(() => {
|
|
349
|
+
// Se siamo in focus mode, il singolo tap esce
|
|
350
|
+
if (isFocusActive && el.classList.contains('is-focused')) {
|
|
351
|
+
toggleFocusMode(type, el);
|
|
352
|
+
}
|
|
353
|
+
}, 350); // Attesa per vedere se arriva il secondo tap
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const stopInteraction = () => {
|
|
358
|
+
clearTimeout(pressTimer);
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// Usiamo PointerEvents: funzionano identici per Mouse e Touch
|
|
362
|
+
el.addEventListener('pointerdown', handleInteraction);
|
|
363
|
+
el.addEventListener('pointerup', stopInteraction);
|
|
364
|
+
el.addEventListener('pointerleave', stopInteraction);
|
|
315
365
|
});
|
|
316
366
|
|
|
317
|
-
// Fullscreen via Hotspot
|
|
318
|
-
if (ui.hotspot) {
|
|
367
|
+
// Fullscreen via Hotspot (Click Singolo)
|
|
368
|
+
if (ui.hotspot) {
|
|
369
|
+
ui.hotspot.addEventListener('click', (e) => {
|
|
370
|
+
e.preventDefault();
|
|
371
|
+
const doc = document.documentElement, isF = document.fullscreenElement || document.webkitFullscreenElement;
|
|
372
|
+
if (!isF) { if (doc.requestFullscreen) doc.requestFullscreen(); else if (doc.webkitRequestFullscreen) doc.webkitRequestFullscreen(); }
|
|
373
|
+
else { if (document.exitFullscreen) document.exitFullscreen(); else if (document.webkitExitFullscreen) document.webkitExitFullscreen(); }
|
|
374
|
+
});
|
|
375
|
+
}
|
|
319
376
|
|
|
320
377
|
// ==========================================================================
|
|
321
378
|
// 9. INIZIALIZZAZIONE
|
package/index.html
CHANGED
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
|
|
41
41
|
<!-- HEADING MEAN -->
|
|
42
42
|
<div class="data-box">
|
|
43
|
-
<div class="label-row"><span class="label">HEADING MEAN</span></div>
|
|
43
|
+
<div class="label-row"><span class="label">HEADING (MEAN)</span></div>
|
|
44
44
|
<span class="value value-large" id="hdg">000°</span>
|
|
45
45
|
</div>
|
|
46
46
|
|
|
47
47
|
<!-- COG MEAN -->
|
|
48
48
|
<div class="data-box">
|
|
49
|
-
<div class="label-row"><span class="label">COG MEAN</span></div>
|
|
49
|
+
<div class="label-row"><span class="label">COG (MEAN)</span></div>
|
|
50
50
|
<span class="value value-large" id="cog">000°</span>
|
|
51
51
|
</div>
|
|
52
52
|
|
|
@@ -136,19 +136,19 @@
|
|
|
136
136
|
|
|
137
137
|
<!-- TWA MEAN -->
|
|
138
138
|
<div class="data-box">
|
|
139
|
-
<div class="label-row"><span class="label">TWA MEAN</span></div>
|
|
139
|
+
<div class="label-row"><span class="label">TWA (MEAN)</span></div>
|
|
140
140
|
<span class="value value-large" id="twa-avg">---°</span>
|
|
141
141
|
</div>
|
|
142
142
|
|
|
143
143
|
<!-- AWA MEAN -->
|
|
144
144
|
<div class="data-box">
|
|
145
|
-
<div class="label-row"><span class="label">AWA MEAN</span></div>
|
|
145
|
+
<div class="label-row"><span class="label">AWA (MEAN)</span></div>
|
|
146
146
|
<span class="value value-large" id="awa-avg">---°</span>
|
|
147
147
|
</div>
|
|
148
148
|
|
|
149
149
|
<!-- TWD MEAN con Bussola -->
|
|
150
150
|
<div class="data-box">
|
|
151
|
-
<div class="label-row"><span class="label">TWD MEAN</span></div>
|
|
151
|
+
<div class="label-row"><span class="label">TWD (MEAN)</span></div>
|
|
152
152
|
<div class="value-with-compass">
|
|
153
153
|
<svg class="mini-compass" viewBox="0 0 40 40">
|
|
154
154
|
<circle cx="20" cy="20" r="19" fill="#151515" stroke="#444" stroke-width="1.5"/>
|
package/package.json
CHANGED
package/style.css
CHANGED
|
@@ -22,7 +22,7 @@ body {
|
|
|
22
22
|
gap: 8px;
|
|
23
23
|
grid-template-columns: 1.7fr 3fr 1.7fr;
|
|
24
24
|
grid-template-rows: 100%;
|
|
25
|
-
transition: all 0.
|
|
25
|
+
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/* ==========================================================================
|
|
@@ -43,14 +43,22 @@ body {
|
|
|
43
43
|
container-type: size;
|
|
44
44
|
flex: 1;
|
|
45
45
|
overflow: hidden;
|
|
46
|
+
-webkit-touch-callout: none;
|
|
47
|
+
-webkit-user-select: none;
|
|
48
|
+
user-select: none;
|
|
49
|
+
touch-action: none;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
/* Altezze Desktop */
|
|
49
53
|
.data-box:nth-child(1), .data-box:nth-child(2) { height: 25vh; }
|
|
50
54
|
.data-box:nth-child(3), .data-box:nth-child(4), .data-box:nth-child(5) { height: 16.666vh; }
|
|
51
55
|
|
|
56
|
+
/* Allineamento Speculare Data Box */
|
|
57
|
+
.left-panel .data-box { align-items: flex-start; text-align: left; }
|
|
58
|
+
.right-panel .data-box { align-items: flex-end; text-align: right; }
|
|
59
|
+
|
|
52
60
|
/* ==========================================================================
|
|
53
|
-
3. TACTICAL FOCUS MODE (ORIZZONTALE)
|
|
61
|
+
3. TACTICAL FOCUS MODE (ORIZZONTALE) E INGRANDIMENTI
|
|
54
62
|
========================================================================== */
|
|
55
63
|
.focus-active { grid-template-columns: 1fr 1fr !important; }
|
|
56
64
|
.focus-active .side-panel:not(.has-focus) { display: none !important; }
|
|
@@ -62,17 +70,35 @@ body {
|
|
|
62
70
|
.focus-active.focus-side-right .side-panel.has-focus { grid-column: 2 !important; }
|
|
63
71
|
|
|
64
72
|
.focus-active .has-focus .data-box:not(.is-focused) { display: none !important; }
|
|
65
|
-
.focus-active .has-focus .data-box.is-focused { height: 100vh !important; border: none; background: rgba(255, 255, 255, 0.05); }
|
|
73
|
+
.focus-active .has-focus .data-box.is-focused { height: 100vh !important; border: none; background: rgba(255, 255, 255, 0.05); padding: 20px; }
|
|
74
|
+
|
|
75
|
+
/* Ingrandimenti in Focus Mode */
|
|
76
|
+
.focus-active .is-focused .value { font-size: clamp(4rem, 25cqh, 10rem) !important; margin-top: 15px; }
|
|
77
|
+
.focus-active .is-focused .scale-labels { font-size: 24px !important; min-width: 60px !important; line-height: 1.2; }
|
|
78
|
+
.focus-active .is-focused .label-row .label { font-size: 1.5rem !important; }
|
|
79
|
+
.focus-active .is-focused .label-row .unit { font-size: 1.5rem !important; }
|
|
80
|
+
|
|
81
|
+
/* Ingrandimento scritta Hercules */
|
|
82
|
+
.focus-active .is-focused.box-hercules .unit::before,
|
|
83
|
+
.focus-active .is-focused.box-hercules .unit::after,
|
|
84
|
+
.focus-active .is-focused.box-hercules .label::before,
|
|
85
|
+
.focus-active .is-focused.box-hercules .label::after {
|
|
86
|
+
font-size: 14px !important;
|
|
87
|
+
letter-spacing: 2px;
|
|
88
|
+
}
|
|
66
89
|
|
|
67
|
-
/* Tipografia e Grafica in Focus */
|
|
68
|
-
.focus-active .is-focused .value { font-size: clamp(3rem, 18cqh, 8rem) !important; margin-top: 10px; }
|
|
69
|
-
.focus-active .is-focused .scale-labels { font-size: 22px !important; min-width: 40px !important; }
|
|
70
90
|
.focus-active .is-focused .sparkline path { stroke-width: 0.8px !important; }
|
|
71
91
|
|
|
72
92
|
/* ==========================================================================
|
|
73
|
-
4. TIPOGRAFIA DINAMICA
|
|
93
|
+
4. TIPOGRAFIA DINAMICA E SIMMETRIA
|
|
74
94
|
========================================================================== */
|
|
75
95
|
.label-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 2px; width: 100%; }
|
|
96
|
+
|
|
97
|
+
/* Simmetria Titoli/Unità */
|
|
98
|
+
.left-panel .label-row { justify-content: space-between; }
|
|
99
|
+
.right-panel .label-row { justify-content: space-between; flex-direction: row; }
|
|
100
|
+
.right-panel .label-row .label:only-child { margin-left: auto; }
|
|
101
|
+
|
|
76
102
|
.label { color: #666; font-size: 0.65rem; font-weight: bold; text-transform: uppercase; }
|
|
77
103
|
.unit { color: #888; font-size: 0.6rem; font-weight: bold; }
|
|
78
104
|
.value { color: #fff; font-size: clamp(1.2rem, 22cqh, 3rem); font-weight: 600; line-height: 0.9; letter-spacing: -1px; padding-bottom: 5px; }
|
|
@@ -107,12 +133,23 @@ body {
|
|
|
107
133
|
#depth-graph { stroke: #3498db; fill: rgba(52, 152, 219, 0.12); }
|
|
108
134
|
#tws-graph { stroke: #f1c40f; fill: rgba(241, 196, 15, 0.12); }
|
|
109
135
|
|
|
136
|
+
/* ==========================================================================
|
|
137
|
+
6. HERCULES MODE (LABEL TOP E SIMMETRIA)
|
|
138
|
+
========================================================================== */
|
|
110
139
|
.line-hercules { filter: drop-shadow(0 0 5px #ff0000); stroke-width: 1.8px !important; }
|
|
111
140
|
.box-hercules { background: rgba(255, 0, 0, 0.08) !important; }
|
|
112
|
-
|
|
141
|
+
|
|
142
|
+
/* Simmetria Scritta Hercules */
|
|
143
|
+
.box-hercules .unit::before, .box-hercules .unit::after,
|
|
144
|
+
.box-hercules .label::before, .box-hercules .label::after {
|
|
145
|
+
font-size: 7px; color: #ff4444; font-weight: 900; letter-spacing: 1px; text-transform: uppercase;
|
|
146
|
+
}
|
|
147
|
+
.left-panel .box-hercules .unit::before { content: "HERCULES "; }
|
|
148
|
+
.right-panel .box-hercules .unit::after { content: " HERCULES"; }
|
|
149
|
+
.right-panel .box-hercules .label:only-child::before { content: "HERCULES "; }
|
|
113
150
|
|
|
114
151
|
/* ==========================================================================
|
|
115
|
-
|
|
152
|
+
7. STATI E ANIMAZIONI
|
|
116
153
|
========================================================================== */
|
|
117
154
|
#status { position: absolute; top: 5px; right: 15px; font-size: 0.5rem; text-transform: uppercase; z-index: 1000; }
|
|
118
155
|
.online { color: #2ecc71; opacity: 0.5; }
|
|
@@ -126,7 +163,7 @@ body {
|
|
|
126
163
|
#wind-gauge { width: 100%; height: 100%; max-height: 100%; object-fit: contain; }
|
|
127
164
|
|
|
128
165
|
/* ==========================================================================
|
|
129
|
-
|
|
166
|
+
8. RESPONSIVE (PORTRAIT)
|
|
130
167
|
========================================================================== */
|
|
131
168
|
@media (max-aspect-ratio: 0.9 / 1) {
|
|
132
169
|
.main-container {
|
|
@@ -134,29 +171,16 @@ body {
|
|
|
134
171
|
grid-template-rows: 45vh 55vh !important;
|
|
135
172
|
}
|
|
136
173
|
|
|
137
|
-
|
|
138
|
-
.center-panel { grid-row: 1 !important; grid-column: 1 / span 2 !important; }
|
|
174
|
+
.center-panel { grid-row: 1 !important; grid-column: 1 / span 2 !important; padding: 5px 0; }
|
|
139
175
|
.left-panel { grid-row: 2 !important; grid-column: 1 !important; }
|
|
140
176
|
.right-panel { grid-row: 2 !important; grid-column: 2 !important; }
|
|
141
177
|
|
|
142
|
-
|
|
143
|
-
.
|
|
144
|
-
|
|
145
|
-
flex-direction: column !important;
|
|
146
|
-
}
|
|
147
|
-
.focus-active .center-panel {
|
|
148
|
-
flex: 0 0 45vh !important;
|
|
149
|
-
width: 100% !important;
|
|
150
|
-
}
|
|
151
|
-
.focus-active .side-panel.has-focus {
|
|
152
|
-
flex: 0 0 55vh !important;
|
|
153
|
-
width: 100% !important;
|
|
154
|
-
display: flex !important;
|
|
155
|
-
}
|
|
178
|
+
.main-container.focus-active { display: flex !important; flex-direction: column !important; }
|
|
179
|
+
.focus-active .center-panel { flex: 0 0 45vh !important; width: 100% !important; }
|
|
180
|
+
.focus-active .side-panel.has-focus { flex: 0 0 55vh !important; width: 100% !important; display: flex !important; }
|
|
156
181
|
|
|
157
|
-
/* Calibrazione altezze box per Portrait */
|
|
158
182
|
.data-box:nth-child(1), .data-box:nth-child(2) { height: 13.5vh; }
|
|
159
183
|
.data-box:nth-child(3), .data-box:nth-child(4), .data-box:nth-child(5) { height: 9.3vh; }
|
|
160
|
-
|
|
161
|
-
.value-
|
|
184
|
+
.value-large { font-size: clamp(1.2rem, 35cqh, 2.5rem); margin: auto 0; }
|
|
185
|
+
.value.dual-val { font-size: clamp(0.9rem, 30cqh, 1.4rem); }
|
|
162
186
|
}
|