Claude commited on
Commit
43cb5cc
·
1 Parent(s): 134bb77

Replace inline expand with modal for data-type About panels

Browse files

- Remove click-to-expand inline panels that pushed page content down
- Hover over ▶ About still shows a brief one-line tooltip (CSS only)
- Click opens a centred modal with the full column definitions;
closes via ✕ button, backdrop click, or Escape key
- Modal content defined in JS (ABOUT map) to keep HTML clean

https://claude.ai/code/session_01TrCrzZmWbQscJiAQaW7Rg8

app/static/css/style.css CHANGED
@@ -122,12 +122,6 @@ select:disabled, input:disabled {
122
  padding-top: 0.2rem;
123
  }
124
 
125
- .check-row {
126
- display: flex;
127
- flex-direction: column;
128
- gap: 0;
129
- }
130
-
131
  .check-row-header {
132
  display: flex;
133
  align-items: center;
@@ -202,24 +196,73 @@ select:disabled, input:disabled {
202
 
203
  .about-toggle:hover::after { opacity: 1; }
204
 
205
- /* Expandable about panel */
206
- .check-about {
207
- margin-top: 0.55rem;
208
- padding: 0.7rem 0.85rem;
209
- background: rgba(255,255,255,0.025);
 
 
 
 
 
 
 
 
 
 
210
  border: 1px solid var(--border);
211
  border-radius: var(--radius);
212
- font-size: 0.78rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  color: var(--text-muted);
214
- line-height: 1.6;
 
 
 
 
 
215
  }
 
216
 
217
- .about-source {
218
- font-size: 0.75rem;
219
- margin-bottom: 0.6rem;
220
  color: var(--text-muted);
 
221
  }
222
 
 
 
 
 
 
223
  .about-source strong { color: var(--text); }
224
 
225
  /* ── Buttons ── */
 
122
  padding-top: 0.2rem;
123
  }
124
 
 
 
 
 
 
 
125
  .check-row-header {
126
  display: flex;
127
  align-items: center;
 
196
 
197
  .about-toggle:hover::after { opacity: 1; }
198
 
199
+ /* ── About modal ── */
200
+ .modal-overlay {
201
+ position: fixed;
202
+ inset: 0;
203
+ background: rgba(0, 0, 0, 0.65);
204
+ z-index: 500;
205
+ display: flex;
206
+ align-items: center;
207
+ justify-content: center;
208
+ padding: 1.5rem;
209
+ backdrop-filter: blur(3px);
210
+ }
211
+
212
+ .modal-card {
213
+ background: var(--surface);
214
  border: 1px solid var(--border);
215
  border-radius: var(--radius);
216
+ max-width: 660px;
217
+ width: 100%;
218
+ max-height: 80vh;
219
+ overflow-y: auto;
220
+ box-shadow: 0 24px 64px rgba(0, 0, 0, 0.65);
221
+ }
222
+
223
+ .modal-header {
224
+ display: flex;
225
+ align-items: center;
226
+ justify-content: space-between;
227
+ padding: 0.9rem 1.25rem;
228
+ border-bottom: 1px solid var(--border);
229
+ position: sticky;
230
+ top: 0;
231
+ background: var(--surface);
232
+ z-index: 1;
233
+ }
234
+
235
+ .modal-header h3 {
236
+ font-size: 0.88rem;
237
+ font-weight: 600;
238
+ color: var(--text);
239
+ }
240
+
241
+ .modal-close {
242
+ background: none;
243
+ border: none;
244
  color: var(--text-muted);
245
+ font-size: 1rem;
246
+ cursor: pointer;
247
+ padding: 0.2rem 0.5rem;
248
+ border-radius: 4px;
249
+ line-height: 1;
250
+ transition: color 0.15s, background 0.15s;
251
  }
252
+ .modal-close:hover { color: var(--text); background: var(--surface2); }
253
 
254
+ .modal-body {
255
+ padding: 1rem 1.25rem 1.25rem;
256
+ font-size: 0.82rem;
257
  color: var(--text-muted);
258
+ line-height: 1.65;
259
  }
260
 
261
+ .about-source {
262
+ font-size: 0.78rem;
263
+ margin-bottom: 0.85rem;
264
+ color: var(--text-muted);
265
+ }
266
  .about-source strong { color: var(--text); }
267
 
268
  /* ── Buttons ── */
app/static/index.html CHANGED
@@ -49,62 +49,20 @@
49
  <label>Data Types</label>
50
  <div class="data-type-group">
51
 
52
- <div class="check-row">
53
- <div class="check-row-header">
54
- <label class="check-label">
55
- <input type="checkbox" id="chk-scada" checked />
56
- 4-second SCADA
57
- </label>
58
- <button class="about-toggle" data-tip="4-sec power output &amp; quality flags from AEMO FPP Daily (PUBLIC_NEXT_DAY_FPPMW). Available from 28 Feb 2025." aria-expanded="false" aria-controls="about-scada">▶ About</button>
59
- </div>
60
- <div class="check-about hidden" id="about-scada">
61
- <p class="about-source">Source: <strong>PUBLIC_NEXT_DAY_FPPMW</strong> (FPP Daily) &nbsp;·&nbsp; Available from <strong>28 Feb 2025</strong></p>
62
- <div class="col-defs">
63
- <div class="col-def">
64
- <span class="col-name">INTERVAL_DATETIME</span>
65
- <span class="col-desc">Target 4-second grid slot (YYYY/MM/DD HH:MM:SS). Each measurement is snapped to the nearest standard interval.</span>
66
- </div>
67
- <div class="col-def">
68
- <span class="col-name">MEASUREMENT_DATETIME</span>
69
- <span class="col-desc">Actual timestamp the SCADA reading was recorded. May differ slightly from <code>INTERVAL_DATETIME</code> due to communication latency.</span>
70
- </div>
71
- <div class="col-def">
72
- <span class="col-name">MEASURED_MW</span>
73
- <span class="col-desc">Instantaneous power (MW). Positive = discharging (generation), negative = charging (load).</span>
74
- </div>
75
- <div class="col-def">
76
- <span class="col-name">MW_QUALITY_FLAG</span>
77
- <span class="col-desc">Data health code — <strong style="color:var(--good)">0</strong> Good · <strong style="color:var(--suspect)">1</strong> Substituted · <strong style="color:var(--bad)">2</strong> Bad · <strong style="color:var(--na)">3</strong> Manual Override.</span>
78
- </div>
79
- </div>
80
- </div>
81
  </div>
82
 
83
- <div class="check-row">
84
- <div class="check-row-header">
85
- <label class="check-label">
86
- <input type="checkbox" id="chk-energy" checked />
87
- 5-minute Dispatch
88
- </label>
89
- <button class="about-toggle" data-tip="5-min dispatch energy storage &amp; MW from AEMO DISPATCH_UNIT_SOLUTION. Available from 11 Feb 2025." aria-expanded="false" aria-controls="about-energy">▶ About</button>
90
- </div>
91
- <div class="check-about hidden" id="about-energy">
92
- <p class="about-source">Source: <strong>DISPATCH_UNIT_SOLUTION</strong> via Next_Day_Dispatch &nbsp;·&nbsp; Available from <strong>11 Feb 2025</strong></p>
93
- <div class="col-defs">
94
- <div class="col-def">
95
- <span class="col-name">INITIALMW</span>
96
- <span class="col-desc">Actual power output (MW) at the exact start of the 5-minute dispatch interval. Used as the baseline for the next dispatch instruction.</span>
97
- </div>
98
- <div class="col-def">
99
- <span class="col-name">INITIAL_ENERGY_STORAGE</span>
100
- <span class="col-desc">State of Energy (SoE) at interval start (MWh) — energy physically available in the battery before dispatch began.</span>
101
- </div>
102
- <div class="col-def">
103
- <span class="col-name">ENERGY_STORAGE</span>
104
- <span class="col-desc">Target energy level at interval end (MWh) — accounts for scheduled charging or discharging during the 5-minute block.</span>
105
- </div>
106
- </div>
107
- </div>
108
  </div>
109
 
110
  </div>
@@ -329,6 +287,17 @@
329
  </div>
330
  </footer>
331
 
 
 
 
 
 
 
 
 
 
 
 
332
  <script src="/static/js/app.js"></script>
333
  </body>
334
  </html>
 
49
  <label>Data Types</label>
50
  <div class="data-type-group">
51
 
52
+ <div class="check-row-header" style="margin-bottom:0">
53
+ <label class="check-label">
54
+ <input type="checkbox" id="chk-scada" checked />
55
+ 4-second SCADA
56
+ </label>
57
+ <button class="about-toggle" data-tip="4-sec power output &amp; quality flags · PUBLIC_NEXT_DAY_FPPMW · from 28 Feb 2025" data-about="scada">▶ About</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  </div>
59
 
60
+ <div class="check-row-header">
61
+ <label class="check-label">
62
+ <input type="checkbox" id="chk-energy" checked />
63
+ 5-minute Dispatch
64
+ </label>
65
+ <button class="about-toggle" data-tip="5-min energy storage &amp; dispatch MW · DISPATCH_UNIT_SOLUTION · from 11 Feb 2025" data-about="energy">▶ About</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  </div>
67
 
68
  </div>
 
287
  </div>
288
  </footer>
289
 
290
+ <!-- ── About modal ── -->
291
+ <div id="about-modal" class="modal-overlay hidden" role="dialog" aria-modal="true" aria-labelledby="modal-title">
292
+ <div class="modal-card">
293
+ <div class="modal-header">
294
+ <h3 id="modal-title"></h3>
295
+ <button id="modal-close" class="modal-close" aria-label="Close">✕</button>
296
+ </div>
297
+ <div class="modal-body" id="modal-body"></div>
298
+ </div>
299
+ </div>
300
+
301
  <script src="/static/js/app.js"></script>
302
  </body>
303
  </html>
app/static/js/app.js CHANGED
@@ -86,15 +86,82 @@ async function init() {
86
  chkEnergy.addEventListener('change', onCheckboxChange);
87
  btnLoad.addEventListener('click', onLoad);
88
 
89
- // ▶ About toggles
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  document.querySelectorAll('.about-toggle').forEach(btn => {
91
- btn.addEventListener('click', () => {
92
- const panel = document.getElementById(btn.getAttribute('aria-controls'));
93
- const expanded = btn.getAttribute('aria-expanded') === 'true';
94
- btn.setAttribute('aria-expanded', String(!expanded));
95
- panel.classList.toggle('hidden', expanded);
96
- btn.textContent = expanded ? '▶ About' : '▼ About';
97
- });
98
  });
99
  }
100
 
 
86
  chkEnergy.addEventListener('change', onCheckboxChange);
87
  btnLoad.addEventListener('click', onLoad);
88
 
89
+ // ▶ About modal
90
+ const aboutModal = document.getElementById('about-modal');
91
+ const modalTitle = document.getElementById('modal-title');
92
+ const modalBody = document.getElementById('modal-body');
93
+
94
+ const ABOUT = {
95
+ scada: {
96
+ title: '4-second SCADA · PUBLIC_NEXT_DAY_FPPMW',
97
+ html: `
98
+ <p class="about-source">
99
+ Source: <strong>PUBLIC_NEXT_DAY_FPPMW</strong> (FPP Daily)
100
+ &nbsp;·&nbsp; Available from <strong>28 Feb 2025</strong>
101
+ </p>
102
+ <div class="col-defs">
103
+ <div class="col-def">
104
+ <span class="col-name">INTERVAL_DATETIME</span>
105
+ <span class="col-desc">Target 4-second grid slot (YYYY/MM/DD HH:MM:SS). Each measurement is snapped to the nearest standard interval.</span>
106
+ </div>
107
+ <div class="col-def">
108
+ <span class="col-name">MEASUREMENT_DATETIME</span>
109
+ <span class="col-desc">Actual timestamp the SCADA reading was recorded. May differ slightly from <code>INTERVAL_DATETIME</code> due to communication latency.</span>
110
+ </div>
111
+ <div class="col-def">
112
+ <span class="col-name">MEASURED_MW</span>
113
+ <span class="col-desc">Instantaneous power (MW). Positive = discharging (generation), negative = charging (load).</span>
114
+ </div>
115
+ <div class="col-def">
116
+ <span class="col-name">MW_QUALITY_FLAG</span>
117
+ <span class="col-desc">Data health code — <strong style="color:var(--good)">0</strong> Good · <strong style="color:var(--suspect)">1</strong> Substituted · <strong style="color:var(--bad)">2</strong> Bad · <strong style="color:var(--na)">3</strong> Manual Override.</span>
118
+ </div>
119
+ </div>`,
120
+ },
121
+ energy: {
122
+ title: '5-minute Dispatch · DISPATCH_UNIT_SOLUTION',
123
+ html: `
124
+ <p class="about-source">
125
+ Source: <strong>DISPATCH_UNIT_SOLUTION</strong> via Next_Day_Dispatch
126
+ &nbsp;·&nbsp; Available from <strong>11 Feb 2025</strong>
127
+ </p>
128
+ <div class="col-defs">
129
+ <div class="col-def">
130
+ <span class="col-name">INITIALMW</span>
131
+ <span class="col-desc">Actual power output (MW) at the exact start of the 5-minute dispatch interval. Used as the baseline for the next dispatch instruction.</span>
132
+ </div>
133
+ <div class="col-def">
134
+ <span class="col-name">INITIAL_ENERGY_STORAGE</span>
135
+ <span class="col-desc">State of Energy (SoE) at interval start (MWh) — energy physically available in the battery before dispatch began.</span>
136
+ </div>
137
+ <div class="col-def">
138
+ <span class="col-name">ENERGY_STORAGE</span>
139
+ <span class="col-desc">Target energy level at interval end (MWh) — accounts for scheduled charging or discharging during the 5-minute block.</span>
140
+ </div>
141
+ </div>`,
142
+ },
143
+ };
144
+
145
+ function openAboutModal(key) {
146
+ const data = ABOUT[key];
147
+ modalTitle.textContent = data.title;
148
+ modalBody.innerHTML = data.html;
149
+ aboutModal.classList.remove('hidden');
150
+ document.body.style.overflow = 'hidden';
151
+ }
152
+
153
+ function closeAboutModal() {
154
+ aboutModal.classList.add('hidden');
155
+ document.body.style.overflow = '';
156
+ }
157
+
158
  document.querySelectorAll('.about-toggle').forEach(btn => {
159
+ btn.addEventListener('click', () => openAboutModal(btn.dataset.about));
160
+ });
161
+ document.getElementById('modal-close').addEventListener('click', closeAboutModal);
162
+ aboutModal.addEventListener('click', e => { if (e.target === aboutModal) closeAboutModal(); });
163
+ document.addEventListener('keydown', e => {
164
+ if (e.key === 'Escape' && !aboutModal.classList.contains('hidden')) closeAboutModal();
 
165
  });
166
  }
167