- added a persistent 12 hour / 24 hour segmented control under the sidebar’s Time preferences.
Updated: [public/index.php](/Users/tyemeclifford/Documents/GH/weather/public/index.php) [public/assets/js/app.js](/Users/tyemeclifford/Documents/GH/weather/public/assets/js/app.js) [public/assets/css/styles.css](/Users/tyemeclifford/Documents/GH/weather/public/assets/css/styles.css) [README.md](/Users/tyemeclifford/Documents/GH/weather/README.md)
This commit is contained in:
@@ -10,7 +10,7 @@ A PHP, SQLite, HTML5, and JavaScript weather dashboard that uses public weather
|
||||
- RainViewer radar tiles on an interactive Leaflet map.
|
||||
- Local SQLite API cache with a five minute weather refresh window.
|
||||
- Hourly and daily archive snapshots with search and JSON detail export.
|
||||
- Cookie-backed display preferences, map layer toggles, dark/light theme, and import/export.
|
||||
- Cookie-backed display preferences, map layer toggles, 12/24 hour time format, dark/light theme, and import/export.
|
||||
- Current-date history facts comparing the projection to the same date last year.
|
||||
|
||||
## Run
|
||||
|
||||
@@ -302,6 +302,31 @@ body.light .status-line {
|
||||
accent-color: var(--pink);
|
||||
}
|
||||
|
||||
.segmented {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0;
|
||||
min-height: var(--control-h);
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.segmented button {
|
||||
min-height: var(--control-h);
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.segmented button[aria-pressed="true"] {
|
||||
background: linear-gradient(135deg, rgba(57, 168, 255, 0.28), rgba(255, 90, 165, 0.24));
|
||||
color: var(--text);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.control-actions {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
|
||||
+27
-2
@@ -3,6 +3,7 @@
|
||||
|
||||
const DEFAULT_PREFS = {
|
||||
theme: "dark",
|
||||
timeFormat: "12",
|
||||
locationQuery: "New York, NY",
|
||||
sections: {
|
||||
current: true,
|
||||
@@ -110,6 +111,17 @@
|
||||
|
||||
$("refreshButton").addEventListener("click", () => loadWeather(true));
|
||||
$("themeButton").addEventListener("click", toggleTheme);
|
||||
document.querySelectorAll("[data-time-format]").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
state.prefs.timeFormat = button.dataset.timeFormat === "24" ? "24" : "12";
|
||||
savePrefs();
|
||||
applyPreferences();
|
||||
if (state.dashboard) {
|
||||
renderDashboard();
|
||||
renderArchives();
|
||||
}
|
||||
});
|
||||
});
|
||||
$("exportPrefs").addEventListener("click", exportPreferences);
|
||||
$("importPrefsButton").addEventListener("click", () => $("importPrefsFile").click());
|
||||
$("importPrefsFile").addEventListener("change", importPreferences);
|
||||
@@ -635,11 +647,17 @@
|
||||
function applyPreferences() {
|
||||
document.body.classList.toggle("light", state.prefs.theme === "light");
|
||||
$("themeLabel").textContent = state.prefs.theme === "light" ? "Dark" : "Light";
|
||||
state.prefs.timeFormat = state.prefs.timeFormat === "24" ? "24" : "12";
|
||||
|
||||
document.querySelectorAll("[data-pref]").forEach((input) => {
|
||||
input.checked = Boolean(getPath(state.prefs, input.dataset.pref));
|
||||
});
|
||||
|
||||
document.querySelectorAll("[data-time-format]").forEach((button) => {
|
||||
const isActive = button.dataset.timeFormat === state.prefs.timeFormat;
|
||||
button.setAttribute("aria-pressed", isActive ? "true" : "false");
|
||||
});
|
||||
|
||||
Object.entries(state.prefs.sections).forEach(([name, visible]) => {
|
||||
document.querySelectorAll(`[data-section="${name}"]`).forEach((section) => {
|
||||
section.classList.toggle("is-hidden", !visible);
|
||||
@@ -827,7 +845,8 @@
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "2-digit"
|
||||
minute: "2-digit",
|
||||
hour12: useTwelveHourClock()
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
@@ -841,10 +860,16 @@
|
||||
}
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
weekday: "short",
|
||||
hour: "numeric"
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
hour12: useTwelveHourClock()
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
function useTwelveHourClock() {
|
||||
return state.prefs.timeFormat !== "24";
|
||||
}
|
||||
|
||||
function formatDay(value) {
|
||||
if (!value) {
|
||||
return "--";
|
||||
|
||||
@@ -87,6 +87,14 @@ declare(strict_types=1);
|
||||
<label><input type="checkbox" data-pref="metrics.pollen"> Pollen</label>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<h3>Time</h3>
|
||||
<div class="segmented" role="group" aria-label="Time format">
|
||||
<button type="button" data-time-format="12" aria-pressed="true">12 hour</button>
|
||||
<button type="button" data-time-format="24" aria-pressed="false">24 hour</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-actions">
|
||||
<button type="button" id="exportPrefs">
|
||||
<i data-lucide="download"></i>
|
||||
|
||||
Reference in New Issue
Block a user