Show availability on battery widget

This commit is contained in:
SirBlob committed 2026-10-31 22:53:17 -04:00
1 parent ff1d2213dc
commit 63b53df6ff
3 files changed
+169 -63

No files matched your search

+89 -23
View File
@@ -1,30 +1,36 @@
<script lang="ts">
import Icon from '@iconify/svelte';
import { slide } from 'svelte/transition';
import {
batteryCapacityWh,
batteryHealth,
availabilityLabel,
batteryIcon,
batteryStatusLabel,
formatDuration,
isBatteryFull,
isBatteryLow,
powerRateLabel,
moodLabel,
weekdayHoursLabel,
weekendHoursLabel,
type BatteryState
} from '#lib/desktop/battery';
let { battery, onclose }: { battery: BatteryState; onclose: () => void } = $props();
const isFull = $derived(isBatteryFull(battery));
let scheduleOpen = $state(false);
const schedule = [
{ label: 'Weekdays', value: weekdayHoursLabel },
{ label: 'Weekends', value: weekendHoursLabel }
];
const isLow = $derived(isBatteryLow(battery));
const timeLeftLabel = $derived(
`${formatDuration(battery.minutesRemaining)} ${battery.charging ? 'until full' : 'left'}`
battery.charging
? `back in ${formatDuration(battery.minutesRemaining)}`
: `${formatDuration(battery.minutesRemaining)} left`
);
const details = $derived([
{ label: 'Battery size', value: `${batteryCapacityWh}Wh` },
{ label: 'Health', value: `${batteryHealth(battery)}%` },
{ label: 'Charge cycles', value: `${battery.chargeCycles}` },
{ label: 'Power', value: isFull ? '-' : powerRateLabel(battery) }
{ label: 'My time', value: battery.localTime },
{ label: battery.charging ? 'Back online' : 'Offline at', value: battery.charging ? battery.backOnlineAt : battery.offlineAt }
]);
function handleKeydown(event: KeyboardEvent) {
@@ -42,12 +48,10 @@
<Icon icon={batteryIcon(battery)} width="30" />
</span>
<div class="summary-text">
<span class="title">Battery</span>
<span class="title">Availability</span>
<span class="status">
{batteryStatusLabel(battery)}
{#if !isFull}
<span class="time-left">· {timeLeftLabel}</span>
{/if}
{availabilityLabel(battery)}
<span class="time-left">· {timeLeftLabel}</span>
</span>
</div>
<span class="level">{battery.level}%</span>
@@ -57,6 +61,8 @@
<div class="level-fill" style:width="{battery.level}%"></div>
</div>
<p class="mood">{moodLabel(battery)}</p>
<dl class="details">
{#each details as detail (detail.label)}
<div class="detail-row">
@@ -65,6 +71,25 @@
</div>
{/each}
</dl>
<section class="schedule">
<button class="schedule-toggle" aria-expanded={scheduleOpen} onclick={() => (scheduleOpen = !scheduleOpen)}>
<Icon icon="mdi:calendar-clock" width="16" />
<span>Schedule</span>
<span class="chevron" class:open={scheduleOpen}><Icon icon="mdi:chevron-down" width="16" /></span>
</button>
{#if scheduleOpen}
<dl class="schedule-list" transition:slide={{ duration: 180 }}>
{#each schedule as day (day.label)}
<div class="detail-row">
<dt>{day.label}</dt>
<dd>{day.value}</dd>
</div>
{/each}
</dl>
{/if}
</section>
</div>
</div>
@@ -151,12 +176,19 @@
transition: width 0.3s ease;
}
.mood {
margin: 0.6rem 1rem 0;
color: var(--state-color);
font-size: 0.8rem;
font-style: italic;
}
.details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.5rem 1.25rem;
grid-template-columns: 1fr;
gap: 0.45rem;
margin: 0;
padding: 1rem;
padding: 0.9rem 1rem 1rem;
font-size: 0.8rem;
}
@@ -181,6 +213,44 @@
letter-spacing: normal;
}
.schedule {
border-top: 1px solid color-mix(in srgb, var(--foreground) 12%, transparent);
}
.schedule-toggle {
display: flex;
align-items: center;
gap: 0.45rem;
width: 100%;
padding: 0.6rem 1rem;
background: none;
border: none;
color: var(--foreground);
font-size: 0.8rem;
}
.schedule-toggle:hover {
color: var(--accent);
}
.chevron {
display: grid;
margin-left: auto;
transition: transform 0.2s ease;
}
.chevron.open {
transform: rotate(180deg);
}
.schedule-list {
display: grid;
gap: 0.45rem;
margin: 0;
padding: 0 1rem 0.9rem;
font-size: 0.8rem;
}
@media (max-width: 640px) {
.popover {
position: fixed;
@@ -189,9 +259,5 @@
left: 8px;
width: auto;
}
.details {
grid-template-columns: 1fr;
}
}
</style>