@hashsome/ui 0.1.0
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/LICENSE +674 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/entities/action-button.d.ts +17 -0
- package/dist/entities/action-button.js +56 -0
- package/dist/entities/action-button.js.map +1 -0
- package/dist/entities/artwork-ring.d.ts +17 -0
- package/dist/entities/artwork-ring.js +89 -0
- package/dist/entities/artwork-ring.js.map +1 -0
- package/dist/entities/climate-tile.d.ts +20 -0
- package/dist/entities/climate-tile.js +98 -0
- package/dist/entities/climate-tile.js.map +1 -0
- package/dist/entities/light-color.d.ts +52 -0
- package/dist/entities/light-color.js +148 -0
- package/dist/entities/light-color.js.map +1 -0
- package/dist/entities/light-tile.d.ts +24 -0
- package/dist/entities/light-tile.js +139 -0
- package/dist/entities/light-tile.js.map +1 -0
- package/dist/entities/media-browser.d.ts +15 -0
- package/dist/entities/media-browser.js +67 -0
- package/dist/entities/media-browser.js.map +1 -0
- package/dist/entities/media-controls.d.ts +20 -0
- package/dist/entities/media-controls.js +53 -0
- package/dist/entities/media-controls.js.map +1 -0
- package/dist/entities/media-player-bar.d.ts +14 -0
- package/dist/entities/media-player-bar.js +78 -0
- package/dist/entities/media-player-bar.js.map +1 -0
- package/dist/entities/media-player-body.d.ts +16 -0
- package/dist/entities/media-player-body.js +18 -0
- package/dist/entities/media-player-body.js.map +1 -0
- package/dist/entities/media-player-column.d.ts +18 -0
- package/dist/entities/media-player-column.js +34 -0
- package/dist/entities/media-player-column.js.map +1 -0
- package/dist/entities/media-player-page.d.ts +12 -0
- package/dist/entities/media-player-page.js +13 -0
- package/dist/entities/media-player-page.js.map +1 -0
- package/dist/entities/media-progress.d.ts +7 -0
- package/dist/entities/media-progress.js +30 -0
- package/dist/entities/media-progress.js.map +1 -0
- package/dist/entities/media-seek.d.ts +9 -0
- package/dist/entities/media-seek.js +19 -0
- package/dist/entities/media-seek.js.map +1 -0
- package/dist/entities/nav-dock.d.ts +13 -0
- package/dist/entities/nav-dock.js +22 -0
- package/dist/entities/nav-dock.js.map +1 -0
- package/dist/entities/nav-rail.d.ts +19 -0
- package/dist/entities/nav-rail.js +18 -0
- package/dist/entities/nav-rail.js.map +1 -0
- package/dist/entities/now-playing.d.ts +20 -0
- package/dist/entities/now-playing.js +34 -0
- package/dist/entities/now-playing.js.map +1 -0
- package/dist/entities/scene-button.d.ts +13 -0
- package/dist/entities/scene-button.js +6 -0
- package/dist/entities/scene-button.js.map +1 -0
- package/dist/entities/seek-line.d.ts +9 -0
- package/dist/entities/seek-line.js +71 -0
- package/dist/entities/seek-line.js.map +1 -0
- package/dist/entities/sensor-readout.d.ts +28 -0
- package/dist/entities/sensor-readout.js +95 -0
- package/dist/entities/sensor-readout.js.map +1 -0
- package/dist/entities/top-bar.d.ts +68 -0
- package/dist/entities/top-bar.js +248 -0
- package/dist/entities/top-bar.js.map +1 -0
- package/dist/entity-handle.d.ts +21 -0
- package/dist/entity-handle.js +16 -0
- package/dist/entity-handle.js.map +1 -0
- package/dist/gallery/fixtures.d.ts +4 -0
- package/dist/gallery/fixtures.js +119 -0
- package/dist/gallery/fixtures.js.map +1 -0
- package/dist/gallery/gallery.d.ts +9 -0
- package/dist/gallery/gallery.js +355 -0
- package/dist/gallery/gallery.js.map +1 -0
- package/dist/gallery/index.d.ts +2 -0
- package/dist/gallery/index.js +2 -0
- package/dist/gallery/index.js.map +1 -0
- package/dist/gallery/props-data.d.ts +10 -0
- package/dist/gallery/props-data.js +718 -0
- package/dist/gallery/props-data.js.map +1 -0
- package/dist/hooks.d.ts +19 -0
- package/dist/hooks.js +53 -0
- package/dist/hooks.js.map +1 -0
- package/dist/icon-data.d.ts +6 -0
- package/dist/icon-data.js +59193 -0
- package/dist/icon-data.js.map +1 -0
- package/dist/icon.d.ts +9 -0
- package/dist/icon.js +28 -0
- package/dist/icon.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/cover.d.ts +5 -0
- package/dist/layout/cover.js +8 -0
- package/dist/layout/cover.js.map +1 -0
- package/dist/layout/date-time-field.d.ts +15 -0
- package/dist/layout/date-time-field.js +108 -0
- package/dist/layout/date-time-field.js.map +1 -0
- package/dist/layout/detail-provider.d.ts +22 -0
- package/dist/layout/detail-provider.js +26 -0
- package/dist/layout/detail-provider.js.map +1 -0
- package/dist/layout/drawer-controls.d.ts +46 -0
- package/dist/layout/drawer-controls.js +93 -0
- package/dist/layout/drawer-controls.js.map +1 -0
- package/dist/layout/energy-chart.d.ts +37 -0
- package/dist/layout/energy-chart.js +85 -0
- package/dist/layout/energy-chart.js.map +1 -0
- package/dist/layout/energy-usage.d.ts +6 -0
- package/dist/layout/energy-usage.js +18 -0
- package/dist/layout/energy-usage.js.map +1 -0
- package/dist/layout/entity-drawer.d.ts +15 -0
- package/dist/layout/entity-drawer.js +77 -0
- package/dist/layout/entity-drawer.js.map +1 -0
- package/dist/layout/fade-scroll.d.ts +14 -0
- package/dist/layout/fade-scroll.js +59 -0
- package/dist/layout/fade-scroll.js.map +1 -0
- package/dist/layout/grid.d.ts +9 -0
- package/dist/layout/grid.js +6 -0
- package/dist/layout/grid.js.map +1 -0
- package/dist/layout/history-section.d.ts +19 -0
- package/dist/layout/history-section.js +39 -0
- package/dist/layout/history-section.js.map +1 -0
- package/dist/layout/hold-progress.d.ts +2 -0
- package/dist/layout/hold-progress.js +9 -0
- package/dist/layout/hold-progress.js.map +1 -0
- package/dist/layout/icon-button-styles.d.ts +9 -0
- package/dist/layout/icon-button-styles.js +28 -0
- package/dist/layout/icon-button-styles.js.map +1 -0
- package/dist/layout/page.d.ts +17 -0
- package/dist/layout/page.js +29 -0
- package/dist/layout/page.js.map +1 -0
- package/dist/layout/plain-button.d.ts +11 -0
- package/dist/layout/plain-button.js +17 -0
- package/dist/layout/plain-button.js.map +1 -0
- package/dist/layout/room-header.d.ts +12 -0
- package/dist/layout/room-header.js +11 -0
- package/dist/layout/room-header.js.map +1 -0
- package/dist/layout/round-button.d.ts +13 -0
- package/dist/layout/round-button.js +19 -0
- package/dist/layout/round-button.js.map +1 -0
- package/dist/layout/sensor-history.d.ts +15 -0
- package/dist/layout/sensor-history.js +91 -0
- package/dist/layout/sensor-history.js.map +1 -0
- package/dist/layout/sensor-stats.d.ts +21 -0
- package/dist/layout/sensor-stats.js +79 -0
- package/dist/layout/sensor-stats.js.map +1 -0
- package/dist/layout/series-chart.d.ts +25 -0
- package/dist/layout/series-chart.js +46 -0
- package/dist/layout/series-chart.js.map +1 -0
- package/dist/layout/stepper.d.ts +10 -0
- package/dist/layout/stepper.js +12 -0
- package/dist/layout/stepper.js.map +1 -0
- package/dist/layout/swatch-row.d.ts +19 -0
- package/dist/layout/swatch-row.js +20 -0
- package/dist/layout/swatch-row.js.map +1 -0
- package/dist/layout/tile.d.ts +67 -0
- package/dist/layout/tile.js +207 -0
- package/dist/layout/tile.js.map +1 -0
- package/dist/layout/use-drawer.d.ts +33 -0
- package/dist/layout/use-drawer.js +34 -0
- package/dist/layout/use-drawer.js.map +1 -0
- package/dist/layout/use-hold.d.ts +21 -0
- package/dist/layout/use-hold.js +61 -0
- package/dist/layout/use-hold.js.map +1 -0
- package/dist/provider.d.ts +37 -0
- package/dist/provider.js +95 -0
- package/dist/provider.js.map +1 -0
- package/dist/status.d.ts +8 -0
- package/dist/status.js +27 -0
- package/dist/status.js.map +1 -0
- package/dist/theme/density.d.ts +16 -0
- package/dist/theme/density.js +7 -0
- package/dist/theme/density.js.map +1 -0
- package/dist/theme/global-styles.d.ts +5 -0
- package/dist/theme/global-styles.js +44 -0
- package/dist/theme/global-styles.js.map +1 -0
- package/dist/theme/index.d.ts +19 -0
- package/dist/theme/index.js +106 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme/overrides.d.ts +28 -0
- package/dist/theme/overrides.js +24 -0
- package/dist/theme/overrides.js.map +1 -0
- package/dist/theme/tokens.d.ts +78 -0
- package/dist/theme/tokens.js +1 -0
- package/dist/theme/tokens.js.map +1 -0
- package/dist/theme/use-typography.d.ts +4 -0
- package/dist/theme/use-typography.js +9 -0
- package/dist/theme/use-typography.js.map +1 -0
- package/dist/use-entity-history.d.ts +12 -0
- package/dist/use-entity-history.js +43 -0
- package/dist/use-entity-history.js.map +1 -0
- package/dist/use-media-browser.d.ts +31 -0
- package/dist/use-media-browser.js +87 -0
- package/dist/use-media-browser.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
3
|
+
import { Flex, Typography } from 'e-prim';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { useEntityHistory } from '../use-entity-history.js';
|
|
6
|
+
import { DateTimeField } from './date-time-field.js';
|
|
7
|
+
import { useDetail } from './detail-provider.js';
|
|
8
|
+
import { aggregate, downsample, fromDateTimeInput, periodStart, samplesBetween, toDateTimeInput, } from './sensor-stats.js';
|
|
9
|
+
import { RangeSwitcher, SeriesChart, } from './series-chart.js';
|
|
10
|
+
const DAY = 24 * 60 * 60_000;
|
|
11
|
+
const RANGES = [
|
|
12
|
+
{ value: '1h', label: '1H' },
|
|
13
|
+
{ value: '1d', label: '1D' },
|
|
14
|
+
{ value: '1w', label: '1W' },
|
|
15
|
+
{ value: '1m', label: '1M' },
|
|
16
|
+
{ value: 'custom', label: 'Custom' },
|
|
17
|
+
];
|
|
18
|
+
/** The month's hourly points up to where the week's finer points begin, then those. */
|
|
19
|
+
function mergeHistory(coarse, fine) {
|
|
20
|
+
const from = fine?.[0]?.timestamp;
|
|
21
|
+
return [
|
|
22
|
+
...(coarse ?? []).filter((sample) => from === undefined || sample.timestamp < from),
|
|
23
|
+
...(fine ?? []),
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
const WINDOW_MS = {
|
|
27
|
+
'1h': 60 * 60_000,
|
|
28
|
+
'1d': DAY,
|
|
29
|
+
'1w': 7 * DAY,
|
|
30
|
+
'1m': 30 * DAY,
|
|
31
|
+
};
|
|
32
|
+
function formatNumber(value) {
|
|
33
|
+
return String(Math.round(value * 10) / 10);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A sensor's history for its detail drawer: a bare sparkline while the drawer is at its normal
|
|
37
|
+
* width, and — once expanded — the full chart with a range switcher (including a custom from/to
|
|
38
|
+
* date and time range). Min / max tiles aggregate the supplied samples: today / this week / this month
|
|
39
|
+
* collapsed, today / last 7 / last 30 days expanded, plus the custom range when one is chosen.
|
|
40
|
+
*/
|
|
41
|
+
export function SensorHistory({ samples: given, entity, unit, }) {
|
|
42
|
+
// Hourly averages for the month, with five-minute ones for the last week laid over them, so the
|
|
43
|
+
// 1H and 1D views have more than a point or two.
|
|
44
|
+
const month = useEntityHistory(given ? undefined : entity, { range: '1m', bucket: '1h' });
|
|
45
|
+
const week = useEntityHistory(given ? undefined : entity, { range: '1w', bucket: '5m' });
|
|
46
|
+
const samples = given ?? mergeHistory(month.result?.points, week.result?.points);
|
|
47
|
+
const { detail } = useDetail();
|
|
48
|
+
const expanded = detail?.expanded ?? false;
|
|
49
|
+
const now = new Date();
|
|
50
|
+
const [range, setRange] = useState('1d');
|
|
51
|
+
const [from, setFrom] = useState(() => toDateTimeInput(new Date(now.getTime() - 7 * DAY)));
|
|
52
|
+
const [to, setTo] = useState(() => toDateTimeInput(now));
|
|
53
|
+
if (samples.length === 0) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const fromDate = fromDateTimeInput(from);
|
|
57
|
+
const toDate = fromDateTimeInput(to);
|
|
58
|
+
const customWindow = fromDate && toDate && fromDate <= toDate ? { from: fromDate, to: toDate } : undefined;
|
|
59
|
+
// Collapsed always shows the 1D sparkline; the switcher only exists once expanded.
|
|
60
|
+
const activeRange = expanded ? range : '1d';
|
|
61
|
+
const chartSamples = activeRange === 'custom'
|
|
62
|
+
? customWindow
|
|
63
|
+
? samplesBetween(samples, customWindow.from, customWindow.to)
|
|
64
|
+
: []
|
|
65
|
+
: samplesBetween(samples, new Date(now.getTime() - WINDOW_MS[activeRange]), now);
|
|
66
|
+
const stat = (start) => aggregate(samplesBetween(samples, start, now));
|
|
67
|
+
const tiles = expanded
|
|
68
|
+
? [
|
|
69
|
+
{ label: 'Today', value: stat(periodStart('today', now)) },
|
|
70
|
+
{ label: 'Last 7 days', value: stat(periodStart('last7d', now)) },
|
|
71
|
+
{ label: 'Last 30 days', value: stat(periodStart('last30d', now)) },
|
|
72
|
+
...(range === 'custom'
|
|
73
|
+
? [
|
|
74
|
+
{
|
|
75
|
+
label: 'Custom range',
|
|
76
|
+
value: customWindow
|
|
77
|
+
? aggregate(samplesBetween(samples, customWindow.from, customWindow.to))
|
|
78
|
+
: undefined,
|
|
79
|
+
},
|
|
80
|
+
]
|
|
81
|
+
: []),
|
|
82
|
+
]
|
|
83
|
+
: [
|
|
84
|
+
{ label: 'Today', value: stat(periodStart('today', now)) },
|
|
85
|
+
{ label: 'This week', value: stat(periodStart('week', now)) },
|
|
86
|
+
{ label: 'This month', value: stat(periodStart('month', now)) },
|
|
87
|
+
];
|
|
88
|
+
return (_jsxs(Flex, { direction: "column", gap: 2, mt: 4, children: [_jsxs(Flex, { justify: "space-between", align: "center", children: [_jsx(Typography, { as: "span", variant: "eyebrow", uppercase: true, color: "textMuted", children: "History" }), expanded ? _jsx(RangeSwitcher, { range: range, options: RANGES, onChange: setRange }) : null] }), expanded && range === 'custom' ? (_jsxs(Flex, { align: "center", gap: 4, css: { flexWrap: 'wrap' }, children: [_jsx(DateTimeField, { label: "From", value: from, max: to, onChange: setFrom }), _jsx(DateTimeField, { label: "To", value: to, min: from, align: "right", onChange: setTo })] })) : null, _jsx(Flex, { gap: 2, children: tiles.map((tile) => (_jsxs(Flex, { direction: "column", grow: 1, minWidth: 0, background: "surfaceRaised", radius: "row", py: 2.5, px: 3, css: { flexBasis: 0 }, children: [_jsx(Typography, { as: "span", variant: "secondary", color: "textMuted", children: tile.label }), _jsx(Typography, { as: "span", variant: "bodyStrong", noWrap: true, children: tile.value
|
|
89
|
+
? `${formatNumber(tile.value.min)} – ${formatNumber(tile.value.max)} ${unit}`
|
|
90
|
+
: '—' })] }, tile.label))) }), chartSamples.length === 0 ? (_jsx(Typography, { as: "span", variant: "secondary", color: "textMuted", children: "No readings in this range." })) : (_jsx(SeriesChart, { samples: downsample(chartSamples, 240), range: activeRange, expanded: expanded, unit: unit, fitDomain: true }))] }));
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensor-history.js","sourceRoot":"","sources":["../../src/layout/sensor-history.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SeriesSample } from './series-chart.tsx';
|
|
2
|
+
export interface MinMax {
|
|
3
|
+
min: number;
|
|
4
|
+
max: number;
|
|
5
|
+
avg: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function startOfDay(date: Date): Date;
|
|
8
|
+
/** Local midnight of the most recent Monday. */
|
|
9
|
+
export declare function startOfWeek(date: Date): Date;
|
|
10
|
+
export declare function startOfMonth(date: Date): Date;
|
|
11
|
+
/** The period windows the readout drawer aggregates over; `from` inclusive, `to` is `now`. */
|
|
12
|
+
export declare function periodStart(period: 'today' | 'week' | 'month' | 'last7d' | 'last30d', now: Date): Date;
|
|
13
|
+
export declare function samplesBetween(samples: SeriesSample[], from: Date, to: Date): SeriesSample[];
|
|
14
|
+
/** Min / max / average of the samples, or `undefined` when there are none. */
|
|
15
|
+
export declare function aggregate(samples: SeriesSample[]): MinMax | undefined;
|
|
16
|
+
/** Thins a long series to at most `limit` points by bucket-averaging, so a month of readings stays cheap to draw. */
|
|
17
|
+
export declare function downsample(samples: SeriesSample[], limit: number): SeriesSample[];
|
|
18
|
+
/** `YYYY-MM-DDTHH:mm` in local time, the value format of `<input type="datetime-local">`. */
|
|
19
|
+
export declare function toDateTimeInput(date: Date): string;
|
|
20
|
+
/** The local time a `YYYY-MM-DDTHH:mm` string names, or `undefined` if it isn't one. */
|
|
21
|
+
export declare function fromDateTimeInput(value: string): Date | undefined;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const DAY = 24 * 60 * 60_000;
|
|
2
|
+
export function startOfDay(date) {
|
|
3
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
4
|
+
}
|
|
5
|
+
/** Local midnight of the most recent Monday. */
|
|
6
|
+
export function startOfWeek(date) {
|
|
7
|
+
const day = startOfDay(date);
|
|
8
|
+
const sinceMonday = (day.getDay() + 6) % 7;
|
|
9
|
+
return new Date(day.getFullYear(), day.getMonth(), day.getDate() - sinceMonday);
|
|
10
|
+
}
|
|
11
|
+
export function startOfMonth(date) {
|
|
12
|
+
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
13
|
+
}
|
|
14
|
+
/** The period windows the readout drawer aggregates over; `from` inclusive, `to` is `now`. */
|
|
15
|
+
export function periodStart(period, now) {
|
|
16
|
+
switch (period) {
|
|
17
|
+
case 'today':
|
|
18
|
+
return startOfDay(now);
|
|
19
|
+
case 'week':
|
|
20
|
+
return startOfWeek(now);
|
|
21
|
+
case 'month':
|
|
22
|
+
return startOfMonth(now);
|
|
23
|
+
case 'last7d':
|
|
24
|
+
return new Date(now.getTime() - 7 * DAY);
|
|
25
|
+
case 'last30d':
|
|
26
|
+
return new Date(now.getTime() - 30 * DAY);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function samplesBetween(samples, from, to) {
|
|
30
|
+
const lo = from.getTime();
|
|
31
|
+
const hi = to.getTime();
|
|
32
|
+
return samples.filter((sample) => {
|
|
33
|
+
const t = Date.parse(sample.timestamp);
|
|
34
|
+
return t >= lo && t <= hi;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/** Min / max / average of the samples, or `undefined` when there are none. */
|
|
38
|
+
export function aggregate(samples) {
|
|
39
|
+
if (samples.length === 0) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
let min = Infinity;
|
|
43
|
+
let max = -Infinity;
|
|
44
|
+
let sum = 0;
|
|
45
|
+
for (const sample of samples) {
|
|
46
|
+
min = Math.min(min, sample.value);
|
|
47
|
+
max = Math.max(max, sample.value);
|
|
48
|
+
sum += sample.value;
|
|
49
|
+
}
|
|
50
|
+
return { min, max, avg: sum / samples.length };
|
|
51
|
+
}
|
|
52
|
+
/** Thins a long series to at most `limit` points by bucket-averaging, so a month of readings stays cheap to draw. */
|
|
53
|
+
export function downsample(samples, limit) {
|
|
54
|
+
if (samples.length <= limit) {
|
|
55
|
+
return samples;
|
|
56
|
+
}
|
|
57
|
+
const size = Math.ceil(samples.length / limit);
|
|
58
|
+
const out = [];
|
|
59
|
+
for (let i = 0; i < samples.length; i += size) {
|
|
60
|
+
const bucket = samples.slice(i, i + size);
|
|
61
|
+
out.push({
|
|
62
|
+
timestamp: bucket[Math.floor(bucket.length / 2)].timestamp,
|
|
63
|
+
value: bucket.reduce((sum, s) => sum + s.value, 0) / bucket.length,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
69
|
+
/** `YYYY-MM-DDTHH:mm` in local time, the value format of `<input type="datetime-local">`. */
|
|
70
|
+
export function toDateTimeInput(date) {
|
|
71
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
72
|
+
}
|
|
73
|
+
/** The local time a `YYYY-MM-DDTHH:mm` string names, or `undefined` if it isn't one. */
|
|
74
|
+
export function fromDateTimeInput(value) {
|
|
75
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/.exec(value);
|
|
76
|
+
return match
|
|
77
|
+
? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]), Number(match[4]), Number(match[5]))
|
|
78
|
+
: undefined;
|
|
79
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensor-stats.js","sourceRoot":"","sources":["../../src/layout/sensor-stats.ts"],"names":[],"mappings":"AAQA,MAAM,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;AAE7B,MAAM,UAAU,UAAU,CAAC,IAAU;IACnC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,WAAW,CAAC,IAAU;IACpC,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC3C,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,WAAW,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAU;IACrC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,WAAW,CACzB,MAAyD,EACzD,GAAS;IAET,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,OAAO;YACV,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;QACzB,KAAK,MAAM;YACT,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;QAC1B,KAAK,OAAO;YACV,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;QAC3B,KAAK,QAAQ;YACX,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;QAC3C,KAAK,SAAS;YACZ,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAuB,EAAE,IAAU,EAAE,EAAQ;IAC1E,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAC1B,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IACxB,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE;QAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,SAAS,CAAC,OAAuB;IAC/C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,GAAG,GAAG,QAAQ,CAAC;IACnB,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC;IACpB,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;AACjD,CAAC;AAED,qHAAqH;AACrH,MAAM,UAAU,UAAU,CAAC,OAAuB,EAAE,KAAa;IAC/D,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;QAC5B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QAC1C,GAAG,CAAC,IAAI,CAAC;YACP,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAE,CAAC,SAAS;YAC3D,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM;SACnE,CAAC,CAAC;IACL,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAEtD,6FAA6F;AAC7F,MAAM,UAAU,eAAe,CAAC,IAAU;IACxC,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;AACtI,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,KAAK,GAAG,2CAA2C,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtE,OAAO,KAAK;QACV,CAAC,CAAC,IAAI,IAAI,CACN,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EACpB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CACjB;QACH,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface SeriesSample {
|
|
2
|
+
/** ISO 8601. */
|
|
3
|
+
timestamp: string;
|
|
4
|
+
value: number;
|
|
5
|
+
}
|
|
6
|
+
/** The window a chart covers; `custom` is a from/to pair the caller has already applied to the samples. */
|
|
7
|
+
export type SeriesRange = '1h' | '1d' | '1w' | '1m' | 'custom';
|
|
8
|
+
export declare function formatTick(range: SeriesRange, iso: string): string;
|
|
9
|
+
/** A bare sparkline, or — when `expanded` — the full chart with axes, grid and a tooltip. Shared by the power and sensor drawers. */
|
|
10
|
+
export declare function SeriesChart({ samples, range, expanded, unit, fitDomain, }: {
|
|
11
|
+
samples: SeriesSample[];
|
|
12
|
+
range: SeriesRange;
|
|
13
|
+
expanded: boolean;
|
|
14
|
+
unit: string;
|
|
15
|
+
/** Fit the y axis to the data instead of starting at 0 — right for temperature/humidity, wrong for power. */
|
|
16
|
+
fitDomain?: boolean;
|
|
17
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
18
|
+
export declare function RangeSwitcher<T extends string>({ range, options, onChange, }: {
|
|
19
|
+
range: T;
|
|
20
|
+
options: {
|
|
21
|
+
value: T;
|
|
22
|
+
label: string;
|
|
23
|
+
}[];
|
|
24
|
+
onChange: (range: T) => void;
|
|
25
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
3
|
+
import { Box, Flex, Typography, useColorByKey } from 'e-prim';
|
|
4
|
+
import { useTypographySize } from '../theme/use-typography.js';
|
|
5
|
+
import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis, } from 'recharts';
|
|
6
|
+
export function formatTick(range, iso) {
|
|
7
|
+
const date = new Date(iso);
|
|
8
|
+
if (range === '1h' || range === '1d') {
|
|
9
|
+
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
10
|
+
}
|
|
11
|
+
if (range === '1w') {
|
|
12
|
+
return date.toLocaleDateString([], { weekday: 'short' });
|
|
13
|
+
}
|
|
14
|
+
return date.toLocaleDateString([], { month: 'short', day: 'numeric' });
|
|
15
|
+
}
|
|
16
|
+
/** A bare sparkline, or — when `expanded` — the full chart with axes, grid and a tooltip. Shared by the power and sensor drawers. */
|
|
17
|
+
export function SeriesChart({ samples, range, expanded, unit, fitDomain = false, }) {
|
|
18
|
+
const accent = useColorByKey('accent') ?? '';
|
|
19
|
+
const textMuted = useColorByKey('textMuted') ?? '';
|
|
20
|
+
const line = useColorByKey('line') ?? '';
|
|
21
|
+
const tickSize = useTypographySize('secondary');
|
|
22
|
+
return (_jsx(Box, { height: expanded ? 220 : 48, mt: 3, children: _jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(AreaChart, { data: samples, margin: { top: 4, right: 0, bottom: 0, left: 0 }, children: [expanded ? _jsx(CartesianGrid, { stroke: line, strokeDasharray: "3 3", vertical: false }) : null, expanded ? (_jsx(XAxis, { dataKey: "timestamp", tickFormatter: (value) => formatTick(range, value), tick: { fontSize: tickSize, fill: textMuted }, axisLine: false, tickLine: false, minTickGap: 24 })) : null, expanded ? (_jsx(YAxis, { width: 36, ...(fitDomain ? { domain: ['auto', 'auto'] } : {}), tick: { fontSize: tickSize, fill: textMuted }, axisLine: false, tickLine: false })) : null, _jsx(Tooltip, { content: _jsx(ChartTooltip, { unit: unit }), ...(expanded
|
|
23
|
+
? {}
|
|
24
|
+
: { allowEscapeViewBox: { x: false, y: true }, position: { y: -52 } }) }), _jsx(Area, { type: "monotone", dataKey: "value", stroke: accent, fill: accent, fillOpacity: 0.2, strokeWidth: 2, dot: false, isAnimationActive: false })] }) }) }));
|
|
25
|
+
}
|
|
26
|
+
export function RangeSwitcher({ range, options, onChange, }) {
|
|
27
|
+
return (_jsx(Flex, { background: "surfaceRaised", radius: "full", p: 0.5, gap: 0.5, children: options.map((option) => {
|
|
28
|
+
const active = option.value === range;
|
|
29
|
+
return (_jsx(Flex, { as: "button", type: "button", onClick: () => onChange(option.value), grow: 1, radius: "full", cursor: "pointer", align: "center", justify: "center", py: 1.25, px: 2.5, background: active ? 'accent' : 'transparent', color: active ? 'accentText' : 'text', children: _jsx(Typography, { as: "span", variant: "label", children: option.label }) }, option.value));
|
|
30
|
+
}) }));
|
|
31
|
+
}
|
|
32
|
+
function ChartTooltip({ active, payload, unit, }) {
|
|
33
|
+
if (!active || !payload?.length) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const sample = payload[0]?.payload;
|
|
37
|
+
if (!sample) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return (_jsx(Box, { background: "surfaceRaised", color: "text", border: true, radius: "small", typography: "body", py: 1.5, px: 2.5, children: _jsxs(Flex, { direction: "column", children: [_jsx(Typography, { as: "span", variant: "secondary", color: "textMuted", children: new Date(sample.timestamp).toLocaleString([], {
|
|
41
|
+
month: 'short',
|
|
42
|
+
day: 'numeric',
|
|
43
|
+
hour: '2-digit',
|
|
44
|
+
minute: '2-digit',
|
|
45
|
+
}) }), _jsxs(Typography, { as: "span", variant: "bodyStrong", children: [Math.round(sample.value * 10) / 10, " ", unit] })] }) }));
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"series-chart.js","sourceRoot":"","sources":["../../src/layout/series-chart.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/** A pill holding a minus button, a value and a plus button. The buttons inside lose their own
|
|
3
|
+
* background and shrink to fit the pill. */
|
|
4
|
+
export declare function Stepper({ children }: {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
7
|
+
/** The value between a `Stepper`'s buttons. */
|
|
8
|
+
export declare function StepperValue({ children }: {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
3
|
+
import { Flex, Typography } from 'e-prim';
|
|
4
|
+
/** A pill holding a minus button, a value and a plus button. The buttons inside lose their own
|
|
5
|
+
* background and shrink to fit the pill. */
|
|
6
|
+
export function Stepper({ children }) {
|
|
7
|
+
return (_jsx(Flex, { align: "center", background: "surfaceRaised", radius: "full", height: 32, gap: 1, px: 1, css: { '& button': { background: 'none', width: 28, height: 28 } }, children: children }));
|
|
8
|
+
}
|
|
9
|
+
/** The value between a `Stepper`'s buttons. */
|
|
10
|
+
export function StepperValue({ children }) {
|
|
11
|
+
return (_jsx(Typography, { as: "span", variant: "body", minWidth: "4.2em", align: "center", css: { fontVariantNumeric: 'tabular-nums' }, children: children }));
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stepper.js","sourceRoot":"","sources":["../../src/layout/stepper.tsx"],"names":[],"mappings":";AAAA,sCAAsC;AACtC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG1C;4CAC4C;AAC5C,MAAM,UAAU,OAAO,CAAC,EAAE,QAAQ,EAA2B;IAC3D,OAAO,CACL,KAAC,IAAI,IACH,KAAK,EAAC,QAAQ,EACd,UAAU,EAAC,eAAe,EAC1B,MAAM,EAAC,MAAM,EACb,MAAM,EAAE,EAAE,EACV,GAAG,EAAE,CAAC,EACN,EAAE,EAAE,CAAC,EACL,GAAG,EAAE,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,YAEjE,QAAQ,GACJ,CACR,CAAC;AACJ,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,YAAY,CAAC,EAAE,QAAQ,EAA2B;IAChE,OAAO,CACL,KAAC,UAAU,IACT,EAAE,EAAC,MAAM,EACT,OAAO,EAAC,MAAM,EACd,QAAQ,EAAC,OAAO,EAChB,KAAK,EAAC,QAAQ,EACd,GAAG,EAAE,EAAE,kBAAkB,EAAE,cAAc,EAAE,YAE1C,QAAQ,GACE,CACd,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IconName } from '../icon-data.ts';
|
|
2
|
+
export interface SwatchItem {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
selected: boolean;
|
|
6
|
+
onPick: () => void;
|
|
7
|
+
/** Literal CSS background of the dot. */
|
|
8
|
+
color: string;
|
|
9
|
+
/** Optional glyph drawn on the dot (the dot grows slightly to fit it). */
|
|
10
|
+
icon?: IconName;
|
|
11
|
+
}
|
|
12
|
+
/** The row of circular choices a tile shows in place of its label (light colors, climate modes):
|
|
13
|
+
* springs in left to right, with the selected one ringed. `customLabel` + `onCustom` add a trailing
|
|
14
|
+
* rainbow "custom" swatch after a divider. */
|
|
15
|
+
export declare function SwatchRow({ items, customLabel, onCustom, }: {
|
|
16
|
+
items: SwatchItem[];
|
|
17
|
+
customLabel?: string;
|
|
18
|
+
onCustom?: () => void;
|
|
19
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
3
|
+
import { Box, Flex } from 'e-prim';
|
|
4
|
+
import { motion } from 'motion/react';
|
|
5
|
+
import { Icon } from '../icon.js';
|
|
6
|
+
const SWATCH_SPRING = { type: 'spring', stiffness: 520, damping: 26 };
|
|
7
|
+
/** The row of circular choices a tile shows in place of its label (light colors, climate modes):
|
|
8
|
+
* springs in left to right, with the selected one ringed. `customLabel` + `onCustom` add a trailing
|
|
9
|
+
* rainbow "custom" swatch after a divider. */
|
|
10
|
+
export function SwatchRow({ items, customLabel, onCustom, }) {
|
|
11
|
+
return (_jsxs(Flex, { as: motion.div, align: "center", justify: "center", gap: 0.5, minWidth: 0, initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.15 }, children: [items.map((item, index) => (_jsx(Flex, { type: "button", as: motion.button, initial: { scale: 0, opacity: 0 }, animate: { scale: 1, opacity: 1 }, transition: { ...SWATCH_SPRING, delay: index * 0.04 }, "aria-label": item.label, "aria-pressed": item.selected, title: item.label, onClick: item.onPick, center: true, width: 30, height: 30, p: 0, radius: "full", cursor: "pointer", css: { flex: 'none', background: 'transparent' }, children: _jsx(Flex, { as: "span", center: true, width: item.icon ? 24 : 20, height: item.icon ? 24 : 20, radius: "full", color: "accentText", css: ({ palette }) => ({
|
|
12
|
+
background: item.color,
|
|
13
|
+
// A ring in the page's own colors, so it reads on either theme.
|
|
14
|
+
boxShadow: item.selected
|
|
15
|
+
? `0 0 0 2px ${palette.surface}, 0 0 0 4px ${palette.text}`
|
|
16
|
+
: 'none',
|
|
17
|
+
}), children: item.icon ? _jsx(Icon, { name: item.icon, size: 14 }) : null }) }, item.key))), onCustom ? (_jsxs(_Fragment, { children: [items.length > 0 ? (_jsx(Box, { as: "span", "aria-hidden": "true", width: 1, height: 24, mx: 1.5, background: "line", css: { opacity: 0.4 } })) : null, _jsx(Flex, { as: motion.button, initial: { scale: 0, opacity: 0 }, animate: { scale: 1, opacity: 1 }, transition: { ...SWATCH_SPRING, delay: items.length * 0.04 }, type: "button", "aria-label": customLabel ?? 'Custom', title: customLabel ?? 'Custom', onClick: onCustom, center: true, width: 30, height: 30, p: 0, radius: "full", cursor: "pointer", css: { flex: 'none', background: 'transparent' }, children: _jsx(Flex, { as: "span", center: true, width: 20, height: 20, radius: "full", css: {
|
|
18
|
+
background: 'conic-gradient(#ff5a4d, #ffd24a, #5fd17a, #4dc9ff, #6a7bff, #c75bff, #ff5a4d)',
|
|
19
|
+
}, children: _jsx(Box, { as: "span", width: 8, height: 8, radius: "full", background: "surface" }) }) })] })) : null] }));
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swatch-row.js","sourceRoot":"","sources":["../../src/layout/swatch-row.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { IconName } from '../icon-data.ts';
|
|
3
|
+
import { type EntityStatus } from '../status.ts';
|
|
4
|
+
import { type EnergyChartProps } from './energy-chart.tsx';
|
|
5
|
+
import { type LogbookEntry } from './history-section.tsx';
|
|
6
|
+
export interface TileProps {
|
|
7
|
+
/** The tile's name. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** Icon id, e.g. `lu:lightbulb`. */
|
|
10
|
+
icon?: IconName;
|
|
11
|
+
/** Second line. Replaced by the status label when the entity is not ready. */
|
|
12
|
+
secondary?: ReactNode;
|
|
13
|
+
/** Status of what the tile shows; anything but `ready` disables it and shows why (Loading, Unavailable, Not found). */
|
|
14
|
+
status?: EntityStatus;
|
|
15
|
+
/** Whether the thing it controls is on: lights the icon (and the whole card for an on/off tile) with the accent color. */
|
|
16
|
+
active?: boolean;
|
|
17
|
+
/** Highlights the tile as pending / done / failed after an action. */
|
|
18
|
+
feedback?: 'pending' | 'done' | 'error' | undefined;
|
|
19
|
+
/** Called on a tap — not after a drag or a hold. */
|
|
20
|
+
onPress?: (() => void) | undefined;
|
|
21
|
+
/** 0..1. Renders a fill bar; enables drag and arrow keys to change it. */
|
|
22
|
+
fill?: number;
|
|
23
|
+
/** Called once when a drag ends or an arrow key is pressed. */
|
|
24
|
+
onFillChange?: (fill: number) => void;
|
|
25
|
+
/** Controls shown on the right (steppers, icon buttons). */
|
|
26
|
+
trailing?: ReactNode;
|
|
27
|
+
/** Replaces the label and status with custom content (e.g. color swatches) while set; the tile stops being one big button. `openDetail` opens the drawer. */
|
|
28
|
+
overlay?: (controls: {
|
|
29
|
+
openDetail: () => void;
|
|
30
|
+
}) => ReactNode;
|
|
31
|
+
/** Native tooltip text. */
|
|
32
|
+
title?: string;
|
|
33
|
+
/** Rendered in the shared detail drawer on a long-press (~500ms). Adds the hold gesture. */
|
|
34
|
+
detail?: ReactNode;
|
|
35
|
+
/** Subtitle under the drawer header's name, e.g. "Light". Only used when `detail` is set. */
|
|
36
|
+
kind?: string;
|
|
37
|
+
/**
|
|
38
|
+
* A power-draw chart, shown in the drawer above `history` (e.g. from a Shelly's energy
|
|
39
|
+
* monitoring). Generic across every entity type.
|
|
40
|
+
*/
|
|
41
|
+
energy?: EnergyChartProps;
|
|
42
|
+
/**
|
|
43
|
+
* Recent activity, shown in the drawer below `detail`/`energy`. Generic across every entity
|
|
44
|
+
* type — whichever component has a way to fetch it just passes it through.
|
|
45
|
+
*/
|
|
46
|
+
history?: LogbookEntry[];
|
|
47
|
+
}
|
|
48
|
+
/** Base pill used by all entity tiles. Tap to press; hold (with `detail` set) to open the drawer. */
|
|
49
|
+
export declare function Tile({ label, icon, secondary, status, active, feedback, onPress, fill, onFillChange, trailing, overlay, title, detail, kind, energy, history, }: TileProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
50
|
+
export declare function IconButton({ icon, label, onClick, disabled, active, pressed, primary, size, glyph, color, feedback, }: {
|
|
51
|
+
icon: IconName;
|
|
52
|
+
label: string;
|
|
53
|
+
onClick?: () => void;
|
|
54
|
+
disabled?: boolean;
|
|
55
|
+
active?: boolean;
|
|
56
|
+
primary?: boolean;
|
|
57
|
+
/** Icon glyph size in px; the button's own circle scales with it (2x). Defaults to this button's usual, larger size when omitted. */
|
|
58
|
+
size?: number;
|
|
59
|
+
/** Icon glyph size in px, leaving the button's circle at its usual size (unlike `size`, which scales both). */
|
|
60
|
+
glyph?: number;
|
|
61
|
+
/** Literal CSS color for the glyph (e.g. a light's current color). */
|
|
62
|
+
color?: string;
|
|
63
|
+
/** For a toggle: whether it is on, announced to assistive tech (`aria-pressed`). Pair it with `active` to color it. */
|
|
64
|
+
pressed?: boolean;
|
|
65
|
+
/** Shows the outcome of an action the press started: `pending` dims it, `done` flashes it with the accent, `error` turns it red. Every press also gives a small squeeze. */
|
|
66
|
+
feedback?: 'pending' | 'done' | 'error' | undefined;
|
|
67
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|