[Perl] DAQ system for the FAPG.
refactor separate range controls from timeframe selectors
Replace the expanding timeframe buttons (click to reveal +/−) with two distinct button groups: 1. Range control (−/label/+) — adjusts the range of the active timeframe 2. Timeframe selector (Hourly/Daily/Weekly/Monthly/Yearly) — selects Clicking a timeframe button no longer triggers expansion. The range label shows the current multiplier (e.g. '3 days') and updates live. - Remove timeframe-option wrapper divs and is-expanded CSS - Simplify timeframe.js (remove expanded state) - graph-controls.js now binds range adjustment to the active timeframe
Changed files
roles/dashboard/public/css/app.css
@@ -239,21 +239,9 @@
239
239
}
240
240
241
241
/* ----------------------------------------------------------------
242
Removed:
Timeframe control adjustments: hide +/− by default,
243
Removed:
show when option is expanded (JS adds is-expanded)
242
Added:
Timeframe control active state
244
243
---------------------------------------------------------------- */
245
Removed:
.timeframe-adjustment {
246
Removed:
display: none;
247
Removed:
}
248
Removed:
249
Removed:
.timeframe-option.is-expanded .timeframe-adjustment {
250
Removed:
display: inline-flex;
251
Removed:
align-items: center;
252
Removed:
justify-content: center;
253
Removed:
}
254
Removed:
255
244
.timeframe-button.is-active {
256
Removed:
/* JS toggles this; override Bootstrap outline style */
257
245
background-color: var(--fapg-accent) !important;
258
246
border-color: var(--fapg-accent) !important;
259
247
color: #fff !important;
roles/dashboard/public/js/dashboard/graph-controls.js
@@ -1,4 +1,4 @@
1
Removed:
import { MAX_TIMEFRAME_RANGE, TIMEFRAMES } from "./constants.js";
1
Added:
import { MAX_TIMEFRAME_RANGE } from "./constants.js";
2
2
import { timeframeRangeLabel } from "./timeframe.js";
3
3
4
4
function updateRawDownloadLinks(timeframe) {
@@ -13,40 +13,44 @@
13
13
}
14
14
15
15
function updateControls(timeframe) {
16
Removed:
document.querySelectorAll("[data-timeframe-option]").forEach(option => {
17
Removed:
const value = option.dataset.timeframeOption;
18
Removed:
const range = timeframe.ranges[value] || 1;
19
Removed:
const expanded = value === timeframe.expanded();
20
Removed:
const button = option.querySelector(".timeframe-button");
16
Added:
const range = timeframe.range();
21
17
22
Removed:
option.classList.toggle("is-active", value === timeframe.selected());
23
Removed:
option.classList.toggle("is-expanded", expanded);
24
Removed:
button.classList.toggle("is-active", value === timeframe.selected());
25
Removed:
button.textContent = expanded ? timeframeRangeLabel(value, range) : button.dataset.timeframeLabel;
26
Removed:
option.querySelector('[data-timeframe-adjustment="decrease"]').disabled = range <= 1;
27
Removed:
option.querySelector('[data-timeframe-adjustment="increase"]').disabled = range >= MAX_TIMEFRAME_RANGE;
28
Removed:
});
29
Removed:
30
18
document.querySelectorAll(".timeframe-button").forEach(button => {
31
19
button.classList.toggle("is-active", button.dataset.timeframe === timeframe.selected());
32
20
});
21
Added:
22
Added:
document.querySelectorAll("[data-range-label]").forEach(label => {
23
Added:
label.textContent = timeframeRangeLabel(timeframe.selected(), range);
24
Added:
});
25
Added:
26
Added:
document.querySelectorAll('[data-range-adjustment="decrease"]').forEach(button => {
27
Added:
button.disabled = range <= 1;
28
Added:
});
29
Added:
30
Added:
document.querySelectorAll('[data-range-adjustment="increase"]').forEach(button => {
31
Added:
button.disabled = range >= MAX_TIMEFRAME_RANGE;
32
Added:
});
33
Added:
33
34
updateRawDownloadLinks(timeframe);
34
35
}
35
36
36
37
export function initialiseGraphControls(timeframe, refreshCharts) {
37
38
document.querySelectorAll(".timeframe-button").forEach(button => {
38
39
button.addEventListener("click", () => {
39
Removed:
timeframe.select(button.dataset.timeframe, Boolean(button.closest("[data-timeframe-option]")));
40
Added:
timeframe.select(button.dataset.timeframe);
40
41
updateControls(timeframe);
41
42
refreshCharts();
42
43
});
43
44
});
44
Removed:
document.querySelectorAll("[data-timeframe-adjustment]").forEach(button => {
45
Added:
46
Added:
document.querySelectorAll("[data-range-adjustment]").forEach(button => {
45
47
button.addEventListener("click", () => {
46
Removed:
timeframe.adjust(button.dataset.timeframe, button.dataset.timeframeAdjustment === "increase" ? 1 : -1);
48
Added:
const direction = button.dataset.rangeAdjustment === "increase" ? 1 : -1;
49
Added:
timeframe.adjust(timeframe.selected(), direction);
47
50
updateControls(timeframe);
48
51
refreshCharts();
49
52
});
50
53
});
54
Added:
51
55
updateControls(timeframe);
52
56
}
roles/dashboard/public/js/dashboard/timeframe.js
@@ -2,7 +2,6 @@
2
2
3
3
export function createTimeframeState() {
4
4
let active = DEFAULT_TIMEFRAME;
5
Removed:
let expanded = null;
6
5
const ranges = Object.fromEntries(Object.keys(TIMEFRAMES).map(timeframe => [timeframe, 1]));
7
6
8
7
function selected() {
@@ -12,9 +11,8 @@
12
11
return {
13
12
selected,
14
13
range: () => ranges[selected()] || 1,
15
Removed:
expanded: () => expanded,
16
14
ranges,
17
Removed:
select(timeframe, expand = false) {
15
Added:
select(timeframe) {
18
16
const next = TIMEFRAMES[timeframe] ? timeframe : DEFAULT_TIMEFRAME;
19
17
20
18
if (next === active) {
@@ -22,13 +20,11 @@
22
20
}
23
21
24
22
active = next;
25
Removed:
expanded = expand ? active : null;
26
23
},
27
24
adjust(timeframe, adjustment) {
28
25
if (!TIMEFRAMES[timeframe]) return;
29
26
30
27
active = timeframe;
31
Removed:
expanded = timeframe;
32
28
ranges[timeframe] = Math.min(
33
29
MAX_TIMEFRAME_RANGE,
34
30
Math.max(1, (ranges[timeframe] || 1) + adjustment)
roles/dashboard/t/01-graph-page.t
@@ -24,7 +24,7 @@
24
24
->element_exists('section#probe-ph[data-panel="ph"] canvas#chart-ph')
25
25
->element_exists_not('[data-status="ph"]')
26
26
->element_exists(
27
Removed:
'div.toolbar-items div.graph-control.timeframe-control[role="group"]'
27
Added:
'div.toolbar-items div.graph-control .timeframe-control[role="group"]'
28
28
)
29
29
->text_is(
30
30
'button[data-graph-controls-toggle][aria-expanded="false"][aria-controls="graph-controls-panel"]',
@@ -44,7 +44,7 @@
44
44
45
45
for my $timeframe (qw(hour day week month year)) {
46
46
$t->element_exists(
47
Removed:
qq{[data-timeframe-option="$timeframe"] [data-timeframe="$timeframe"]}
47
Added:
qq{.timeframe-control button[data-timeframe="$timeframe"]}
48
48
);
49
49
}
50
50
roles/dashboard/templates/dashboard/graph-control.html.ep
@@ -10,28 +10,34 @@
10
10
aria-controls="graph-controls-panel"
11
11
data-graph-controls-toggle>Graph controls</button>
12
12
<div class="collapse d-md-block graph-controls-panel" id="graph-controls-panel">
13
Removed:
<div class="graph-control btn-group timeframe-control" role="group" aria-label="<%= $aria_label %>">
14
Removed:
% for my $timeframe ( [ hour => 'Hourly' ], [ day => 'Daily' ], [ week => 'Weekly' ], [ month => 'Monthly' ], [ year => 'Yearly' ] ) {
15
Removed:
<div class="timeframe-option d-inline-flex" data-timeframe-option="<%= $timeframe->[0] %>">
16
Removed:
<button
17
Removed:
class="btn btn-sm btn-outline-secondary timeframe-adjustment"
18
Removed:
type="button"
19
Removed:
data-timeframe-adjustment="decrease"
20
Removed:
data-timeframe="<%= $timeframe->[0] %>"
21
Removed:
aria-label="Decrease <%= $timeframe->[0] %> range">−</button>
22
Removed:
<button
23
Removed:
class="btn btn-sm btn-outline-secondary timeframe-button"
24
Removed:
type="button"
25
Removed:
data-timeframe="<%= $timeframe->[0] %>"
26
Removed:
data-timeframe-label="<%= $timeframe->[1] %>"><%= $timeframe->[1] %></button>
27
Removed:
<button
28
Removed:
class="btn btn-sm btn-outline-secondary timeframe-adjustment"
29
Removed:
type="button"
30
Removed:
data-timeframe-adjustment="increase"
31
Removed:
data-timeframe="<%= $timeframe->[0] %>"
32
Removed:
aria-label="Increase <%= $timeframe->[0] %> range">+</button>
33
Removed:
</div>
34
Removed:
% }
13
Added:
<div class="graph-control d-inline-flex gap-2" aria-label="<%= $aria_label %>">
14
Added:
<div class="btn-group range-control" role="group" aria-label="Adjust range">
15
Added:
<button
16
Added:
class="btn btn-sm btn-outline-secondary"
17
Added:
type="button"
18
Added:
data-range-adjustment="decrease"
19
Added:
aria-label="Decrease range">−</button>
20
Added:
<button
21
Added:
class="btn btn-sm btn-outline-secondary range-label"
22
Added:
type="button"
23
Added:
data-range-label
24
Added:
disabled
25
Added:
aria-live="polite">1</button>
26
Added:
<button
27
Added:
class="btn btn-sm btn-outline-secondary"
28
Added:
type="button"
29
Added:
data-range-adjustment="increase"
30
Added:
aria-label="Increase range">+</button>
31
Added:
</div>
32
Added:
<div class="btn-group timeframe-control" role="group" aria-label="Select timeframe">
33
Added:
% for my $timeframe ( [ hour => 'Hourly' ], [ day => 'Daily' ], [ week => 'Weekly' ], [ month => 'Monthly' ], [ year => 'Yearly' ] ) {
34
Added:
<button
35
Added:
class="btn btn-sm btn-outline-secondary timeframe-button"
36
Added:
type="button"
37
Added:
data-timeframe="<%= $timeframe->[0] %>"
38
Added:
data-timeframe-label="<%= $timeframe->[1] %>"><%= $timeframe->[1] %></button>
39
Added:
% }
40
Added:
</div>
35
41
</div>
36
42
</div>
37
43
</div>