@mystravil/hardpoint 0.2.0 → 0.2.1
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/CHANGELOG.md +42 -38
- package/LICENSE +25 -25
- package/README.md +67 -64
- package/dist/base.css +625 -625
- package/dist/components/AppShell.svelte +698 -698
- package/dist/components/Button.svelte +174 -174
- package/dist/components/Checkbox.svelte +235 -235
- package/dist/components/CheckboxGroup.svelte +207 -207
- package/dist/components/Combobox.svelte +901 -901
- package/dist/components/Dialog.svelte +359 -359
- package/dist/components/Disclosure.svelte +201 -201
- package/dist/components/EmptyState.svelte +88 -88
- package/dist/components/ErrorState.svelte +135 -135
- package/dist/components/ImageViewer.svelte +764 -764
- package/dist/components/KeyValueList.svelte +114 -114
- package/dist/components/ListDetail.svelte +218 -218
- package/dist/components/LoadRegion.svelte +219 -219
- package/dist/components/MediaFrame.svelte +110 -110
- package/dist/components/MediaImage.svelte +256 -256
- package/dist/components/Menu.svelte +258 -258
- package/dist/components/MenuCheckboxItem.svelte +119 -119
- package/dist/components/MenuGroup.svelte +50 -50
- package/dist/components/MenuItem.svelte +94 -94
- package/dist/components/MenuRadioGroup.svelte +62 -62
- package/dist/components/MenuRadioItem.svelte +110 -110
- package/dist/components/MenuSeparator.svelte +28 -28
- package/dist/components/Meter.svelte +283 -283
- package/dist/components/NativeSelect.svelte +135 -135
- package/dist/components/Notice.svelte +262 -262
- package/dist/components/Popover.svelte +222 -222
- package/dist/components/Progress.svelte +157 -157
- package/dist/components/RadioGroup.svelte +440 -440
- package/dist/components/Select.svelte +471 -471
- package/dist/components/Skeleton.svelte +149 -149
- package/dist/components/Spinner.svelte +118 -118
- package/dist/components/Stat.svelte +253 -253
- package/dist/components/Steps.svelte +189 -189
- package/dist/components/Switch.svelte +222 -222
- package/dist/components/Table.svelte +504 -504
- package/dist/components/Tabs.svelte +246 -246
- package/dist/components/Tag.svelte +40 -40
- package/dist/components/TextArea.svelte +272 -272
- package/dist/components/TextField.svelte +256 -256
- package/dist/components/Timeline.svelte +156 -156
- package/dist/components/Toaster.svelte +411 -411
- package/dist/components/Tooltip.svelte +200 -200
- package/dist/components/TooltipGroup.svelte +46 -46
- package/dist/components/internal/Scrim.svelte +52 -52
- package/dist/components/internal/Sheet.svelte +263 -263
- package/dist/fonts.css +71 -71
- package/dist/tokens.css +283 -283
- package/package.json +81 -76
|
@@ -1,253 +1,253 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* `standard` (default): the size for a row of several stats. `display`:
|
|
6
|
-
* the 32 to 56px display size, for the view's one display subject.
|
|
7
|
-
*/
|
|
8
|
-
export type StatVariant = 'standard' | 'display';
|
|
9
|
-
|
|
10
|
-
/** A status hue for the delta: `ok` good, `warn` needs attention, `danger` bad. */
|
|
11
|
-
export type StatTone = 'ok' | 'warn' | 'danger';
|
|
12
|
-
|
|
13
|
-
export type StatProps = Omit<HTMLAttributes<HTMLDListElement>, 'children'> & {
|
|
14
|
-
/** The caps label. A fixed string the app defines. */
|
|
15
|
-
label: string;
|
|
16
|
-
/**
|
|
17
|
-
* The value in Geist with tabular figures. A string is shown as given
|
|
18
|
-
* and changes at once. A number is shown through `format`; when it
|
|
19
|
-
* changes after the first render, it ticks from the shown figure to
|
|
20
|
-
* the new one (BRIEF, motion rule 5).
|
|
21
|
-
*/
|
|
22
|
-
value: string | number;
|
|
23
|
-
/**
|
|
24
|
-
* Formats a number `value` at rest and at every step of a tick, for
|
|
25
|
-
* example `(n) => n.toLocaleString('en-US')`. Each step is rounded to
|
|
26
|
-
* the larger number of decimal places of the old and new value before
|
|
27
|
-
* it is formatted. Without `format` a number shows as `String(value)`,
|
|
28
|
-
* and the steps show those decimal places.
|
|
29
|
-
*/
|
|
30
|
-
format?: (value: number) => string;
|
|
31
|
-
/** A unit after the value, smaller and in the muted ink: `EUR`, `ms`, `tasks`. */
|
|
32
|
-
unit?: string;
|
|
33
|
-
/**
|
|
34
|
-
* The change, in the muted ink. A number gets its sign: `+12`, `−3`
|
|
35
|
-
* (a true minus), `0`. A string is shown as written, sign included.
|
|
36
|
-
*/
|
|
37
|
-
delta?: string | number;
|
|
38
|
-
/** What the delta compares with, after it: `since last week`. */
|
|
39
|
-
deltaNote?: string;
|
|
40
|
-
/**
|
|
41
|
-
* Colours the delta in a status hue. Without it the delta stays muted.
|
|
42
|
-
* The label or `deltaNote` must say the same, because colour is never
|
|
43
|
-
* the only signal.
|
|
44
|
-
*/
|
|
45
|
-
tone?: StatTone;
|
|
46
|
-
variant?: StatVariant;
|
|
47
|
-
};
|
|
48
|
-
</script>
|
|
49
|
-
|
|
50
|
-
<script lang="ts">
|
|
51
|
-
import { untrack } from 'svelte';
|
|
52
|
-
import { Tween } from 'svelte/motion';
|
|
53
|
-
import { curve, duration, useMotion } from '../motion/index.js';
|
|
54
|
-
|
|
55
|
-
let {
|
|
56
|
-
label,
|
|
57
|
-
value,
|
|
58
|
-
format,
|
|
59
|
-
unit,
|
|
60
|
-
delta,
|
|
61
|
-
deltaNote,
|
|
62
|
-
tone,
|
|
63
|
-
variant = 'standard',
|
|
64
|
-
class: className,
|
|
65
|
-
...rest
|
|
66
|
-
}: StatProps = $props();
|
|
67
|
-
|
|
68
|
-
const change = $derived(
|
|
69
|
-
typeof delta === 'number'
|
|
70
|
-
? delta > 0
|
|
71
|
-
? `+${delta}`
|
|
72
|
-
: delta < 0
|
|
73
|
-
? `\u2212${-delta}`
|
|
74
|
-
: String(delta)
|
|
75
|
-
: delta
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
// ---- Tick (BRIEF, motion rule 5) ---------------------------------------
|
|
79
|
-
|
|
80
|
-
// A number value that changes after the first render ticks from the
|
|
81
|
-
// figure on screen to the new one. Quiet motion, so it runs in both tiers.
|
|
82
|
-
// Strings, reduced motion and live regions change at once.
|
|
83
|
-
const LIVE = '[aria-live]:not([aria-live="off"]), [role="status"], [role="log"], [role="alert"]';
|
|
84
|
-
|
|
85
|
-
const motion = useMotion();
|
|
86
|
-
let valueEl: HTMLElement | undefined;
|
|
87
|
-
// Starts on the first value, so nothing moves on the first render.
|
|
88
|
-
const tween = new Tween(untrack(() => (typeof value === 'number' ? value : 0)));
|
|
89
|
-
let last = untrack(() => value);
|
|
90
|
-
// The decimal places of the running tick's steps.
|
|
91
|
-
let places = $state(0);
|
|
92
|
-
// The figure on screen when the tick began. A hidden copy keeps its width.
|
|
93
|
-
let from = $state('');
|
|
94
|
-
|
|
95
|
-
/** The decimal places of `n` as `String()` writes it, exponent included. */
|
|
96
|
-
function decimals(n: number): number {
|
|
97
|
-
const [digits, exp = '0'] = String(n).split('e');
|
|
98
|
-
return Math.min(100, Math.max(0, (digits.split('.')[1]?.length ?? 0) - Number(exp)));
|
|
99
|
-
}
|
|
100
|
-
const atRest = (n: number) => (format ? format(n) : String(n));
|
|
101
|
-
/** One step: rounded to `p` places, never `-0`, then formatted. */
|
|
102
|
-
function atStep(n: number, p: number): string {
|
|
103
|
-
const r = Number(n.toFixed(p)) || 0;
|
|
104
|
-
return format ? format(r) : r.toFixed(p);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const text = $derived(typeof value === 'number' ? atRest(value) : value);
|
|
108
|
-
const ticking = $derived(
|
|
109
|
-
typeof value === 'number' && !Number.isNaN(value) && tween.current !== value
|
|
110
|
-
);
|
|
111
|
-
const step = $derived(ticking ? atStep(tween.current, places) : '');
|
|
112
|
-
|
|
113
|
-
// Before the DOM updates, so the first frame after a change still shows
|
|
114
|
-
// the old figure. Duration and curve are read from the value element at
|
|
115
|
-
// the change, so a retuned token applies. A change during a tick starts
|
|
116
|
-
// from the step on screen.
|
|
117
|
-
$effect.pre(() => {
|
|
118
|
-
const to = value;
|
|
119
|
-
untrack(() => {
|
|
120
|
-
const was = last;
|
|
121
|
-
last = to;
|
|
122
|
-
if (typeof to !== 'number' || to === was) return;
|
|
123
|
-
const resting = tween.current === tween.target;
|
|
124
|
-
const shown = resting ? tween.current : Number(tween.current.toFixed(places)) || 0;
|
|
125
|
-
const ms =
|
|
126
|
-
valueEl && typeof was === 'number' && !motion.reduced && !valueEl.closest(LIVE)
|
|
127
|
-
? duration(valueEl, '--dur-tick')
|
|
128
|
-
: 0;
|
|
129
|
-
if (!valueEl || ms <= 0 || !Number.isFinite(shown) || !Number.isFinite(to)) {
|
|
130
|
-
void tween.set(to, { duration: 0 });
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
from = resting ? atRest(shown) : atStep(shown, places);
|
|
134
|
-
places = Math.max(decimals(shown), decimals(to));
|
|
135
|
-
// Tween would run the old tick for one more frame before the new
|
|
136
|
-
// one starts; stop it on the step on screen first.
|
|
137
|
-
if (!resting) void tween.set(shown, { duration: 0 });
|
|
138
|
-
void tween.set(to, { duration: ms, easing: curve(valueEl, '--tick') });
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
</script>
|
|
142
|
-
|
|
143
|
-
<!--
|
|
144
|
-
A caps label over a key figure in Geist with tabular figures (BRIEF, visual
|
|
145
|
-
rules 3 and 4; da3-65). A number value ticks to a new value over
|
|
146
|
-
`--dur-tick` on the `--tick` curve, never on the first render. The final
|
|
147
|
-
value stays in the accessible text; the moving steps are aria-hidden. The
|
|
148
|
-
root carries `data-ticking` during a tick. Extra attributes and `class` go
|
|
149
|
-
to the <dl>.
|
|
150
|
-
-->
|
|
151
|
-
<dl
|
|
152
|
-
{...rest}
|
|
153
|
-
class={['hp-stat', className]}
|
|
154
|
-
data-variant={variant}
|
|
155
|
-
data-tone={tone}
|
|
156
|
-
data-ticking={ticking ? '' : undefined}
|
|
157
|
-
>
|
|
158
|
-
<dt class="hp-stat-label hp-label">{label}</dt>
|
|
159
|
-
<dd
|
|
160
|
-
bind:this={valueEl}
|
|
161
|
-
class={['hp-stat-value', variant === 'display' ? 'hp-display' : 'hp-page-title hp-num']}
|
|
162
|
-
>
|
|
163
|
-
<span class="hp-stat-figure"
|
|
164
|
-
><span class="hp-stat-final">{text}</span>{#if ticking}<span
|
|
165
|
-
class="hp-stat-from"
|
|
166
|
-
aria-hidden="true">{from}</span
|
|
167
|
-
><span class="hp-stat-step" aria-hidden="true">{step}</span>{/if}</span
|
|
168
|
-
>{#if unit}{' '}<span class="hp-stat-unit">{unit}</span>{/if}
|
|
169
|
-
</dd>
|
|
170
|
-
{#if change !== undefined && change !== ''}
|
|
171
|
-
<dd class="hp-stat-delta">
|
|
172
|
-
<span class="hp-stat-change">{change}</span>{#if deltaNote}{' '}{deltaNote}{/if}
|
|
173
|
-
</dd>
|
|
174
|
-
{/if}
|
|
175
|
-
</dl>
|
|
176
|
-
|
|
177
|
-
<style>
|
|
178
|
-
.hp-stat {
|
|
179
|
-
display: grid;
|
|
180
|
-
align-content: start;
|
|
181
|
-
gap: var(--space-1);
|
|
182
|
-
min-width: 0;
|
|
183
|
-
}
|
|
184
|
-
.hp-stat[data-variant='display'] {
|
|
185
|
-
gap: var(--space-2);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.hp-stat-label {
|
|
189
|
-
color: var(--muted);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/* Geist even inside a mono context such as a table cell. The size comes
|
|
193
|
-
from base.css: .hp-page-title (24px, 20px on compact) with .hp-num, or
|
|
194
|
-
.hp-display. A long figure breaks rather than widen a phone page. */
|
|
195
|
-
.hp-stat-value {
|
|
196
|
-
font-family: var(--sans);
|
|
197
|
-
overflow-wrap: anywhere;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/* During a tick the figure stacks three layers in one grid cell: the
|
|
201
|
-
final value (transparent, read by screen readers), a hidden copy of
|
|
202
|
-
the figure shown when the tick began, and the moving step. The cell is
|
|
203
|
-
as wide as the wider of the old and new figure, so the unit after it
|
|
204
|
-
holds still; the steps lie between the two in value, and with tabular
|
|
205
|
-
figures in width too. */
|
|
206
|
-
.hp-stat[data-ticking] .hp-stat-figure {
|
|
207
|
-
display: inline-grid;
|
|
208
|
-
}
|
|
209
|
-
.hp-stat[data-ticking] .hp-stat-figure > * {
|
|
210
|
-
grid-area: 1 / 1;
|
|
211
|
-
}
|
|
212
|
-
.hp-stat[data-ticking] .hp-stat-final {
|
|
213
|
-
opacity: 0;
|
|
214
|
-
}
|
|
215
|
-
.hp-stat-from {
|
|
216
|
-
visibility: hidden;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.hp-stat-unit {
|
|
220
|
-
color: var(--muted);
|
|
221
|
-
font-size: var(--type-body);
|
|
222
|
-
font-weight: 400;
|
|
223
|
-
letter-spacing: 0;
|
|
224
|
-
}
|
|
225
|
-
.hp-stat[data-variant='display'] .hp-stat-unit {
|
|
226
|
-
font-size: var(--type-section);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/* 16px on touch, the dense 14px on fine pointers. */
|
|
230
|
-
.hp-stat-delta {
|
|
231
|
-
color: var(--muted);
|
|
232
|
-
font-size: var(--type-body);
|
|
233
|
-
line-height: 24px;
|
|
234
|
-
font-variant-numeric: tabular-nums;
|
|
235
|
-
overflow-wrap: anywhere;
|
|
236
|
-
}
|
|
237
|
-
@media (pointer: fine) {
|
|
238
|
-
.hp-stat-delta {
|
|
239
|
-
font-size: var(--type-dense);
|
|
240
|
-
line-height: 20px;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.hp-stat[data-tone='ok'] .hp-stat-change {
|
|
245
|
-
color: var(--ok);
|
|
246
|
-
}
|
|
247
|
-
.hp-stat[data-tone='warn'] .hp-stat-change {
|
|
248
|
-
color: var(--warn);
|
|
249
|
-
}
|
|
250
|
-
.hp-stat[data-tone='danger'] .hp-stat-change {
|
|
251
|
-
color: var(--danger);
|
|
252
|
-
}
|
|
253
|
-
</style>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `standard` (default): the size for a row of several stats. `display`:
|
|
6
|
+
* the 32 to 56px display size, for the view's one display subject.
|
|
7
|
+
*/
|
|
8
|
+
export type StatVariant = 'standard' | 'display';
|
|
9
|
+
|
|
10
|
+
/** A status hue for the delta: `ok` good, `warn` needs attention, `danger` bad. */
|
|
11
|
+
export type StatTone = 'ok' | 'warn' | 'danger';
|
|
12
|
+
|
|
13
|
+
export type StatProps = Omit<HTMLAttributes<HTMLDListElement>, 'children'> & {
|
|
14
|
+
/** The caps label. A fixed string the app defines. */
|
|
15
|
+
label: string;
|
|
16
|
+
/**
|
|
17
|
+
* The value in Geist with tabular figures. A string is shown as given
|
|
18
|
+
* and changes at once. A number is shown through `format`; when it
|
|
19
|
+
* changes after the first render, it ticks from the shown figure to
|
|
20
|
+
* the new one (BRIEF, motion rule 5).
|
|
21
|
+
*/
|
|
22
|
+
value: string | number;
|
|
23
|
+
/**
|
|
24
|
+
* Formats a number `value` at rest and at every step of a tick, for
|
|
25
|
+
* example `(n) => n.toLocaleString('en-US')`. Each step is rounded to
|
|
26
|
+
* the larger number of decimal places of the old and new value before
|
|
27
|
+
* it is formatted. Without `format` a number shows as `String(value)`,
|
|
28
|
+
* and the steps show those decimal places.
|
|
29
|
+
*/
|
|
30
|
+
format?: (value: number) => string;
|
|
31
|
+
/** A unit after the value, smaller and in the muted ink: `EUR`, `ms`, `tasks`. */
|
|
32
|
+
unit?: string;
|
|
33
|
+
/**
|
|
34
|
+
* The change, in the muted ink. A number gets its sign: `+12`, `−3`
|
|
35
|
+
* (a true minus), `0`. A string is shown as written, sign included.
|
|
36
|
+
*/
|
|
37
|
+
delta?: string | number;
|
|
38
|
+
/** What the delta compares with, after it: `since last week`. */
|
|
39
|
+
deltaNote?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Colours the delta in a status hue. Without it the delta stays muted.
|
|
42
|
+
* The label or `deltaNote` must say the same, because colour is never
|
|
43
|
+
* the only signal.
|
|
44
|
+
*/
|
|
45
|
+
tone?: StatTone;
|
|
46
|
+
variant?: StatVariant;
|
|
47
|
+
};
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<script lang="ts">
|
|
51
|
+
import { untrack } from 'svelte';
|
|
52
|
+
import { Tween } from 'svelte/motion';
|
|
53
|
+
import { curve, duration, useMotion } from '../motion/index.js';
|
|
54
|
+
|
|
55
|
+
let {
|
|
56
|
+
label,
|
|
57
|
+
value,
|
|
58
|
+
format,
|
|
59
|
+
unit,
|
|
60
|
+
delta,
|
|
61
|
+
deltaNote,
|
|
62
|
+
tone,
|
|
63
|
+
variant = 'standard',
|
|
64
|
+
class: className,
|
|
65
|
+
...rest
|
|
66
|
+
}: StatProps = $props();
|
|
67
|
+
|
|
68
|
+
const change = $derived(
|
|
69
|
+
typeof delta === 'number'
|
|
70
|
+
? delta > 0
|
|
71
|
+
? `+${delta}`
|
|
72
|
+
: delta < 0
|
|
73
|
+
? `\u2212${-delta}`
|
|
74
|
+
: String(delta)
|
|
75
|
+
: delta
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// ---- Tick (BRIEF, motion rule 5) ---------------------------------------
|
|
79
|
+
|
|
80
|
+
// A number value that changes after the first render ticks from the
|
|
81
|
+
// figure on screen to the new one. Quiet motion, so it runs in both tiers.
|
|
82
|
+
// Strings, reduced motion and live regions change at once.
|
|
83
|
+
const LIVE = '[aria-live]:not([aria-live="off"]), [role="status"], [role="log"], [role="alert"]';
|
|
84
|
+
|
|
85
|
+
const motion = useMotion();
|
|
86
|
+
let valueEl: HTMLElement | undefined;
|
|
87
|
+
// Starts on the first value, so nothing moves on the first render.
|
|
88
|
+
const tween = new Tween(untrack(() => (typeof value === 'number' ? value : 0)));
|
|
89
|
+
let last = untrack(() => value);
|
|
90
|
+
// The decimal places of the running tick's steps.
|
|
91
|
+
let places = $state(0);
|
|
92
|
+
// The figure on screen when the tick began. A hidden copy keeps its width.
|
|
93
|
+
let from = $state('');
|
|
94
|
+
|
|
95
|
+
/** The decimal places of `n` as `String()` writes it, exponent included. */
|
|
96
|
+
function decimals(n: number): number {
|
|
97
|
+
const [digits, exp = '0'] = String(n).split('e');
|
|
98
|
+
return Math.min(100, Math.max(0, (digits.split('.')[1]?.length ?? 0) - Number(exp)));
|
|
99
|
+
}
|
|
100
|
+
const atRest = (n: number) => (format ? format(n) : String(n));
|
|
101
|
+
/** One step: rounded to `p` places, never `-0`, then formatted. */
|
|
102
|
+
function atStep(n: number, p: number): string {
|
|
103
|
+
const r = Number(n.toFixed(p)) || 0;
|
|
104
|
+
return format ? format(r) : r.toFixed(p);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const text = $derived(typeof value === 'number' ? atRest(value) : value);
|
|
108
|
+
const ticking = $derived(
|
|
109
|
+
typeof value === 'number' && !Number.isNaN(value) && tween.current !== value
|
|
110
|
+
);
|
|
111
|
+
const step = $derived(ticking ? atStep(tween.current, places) : '');
|
|
112
|
+
|
|
113
|
+
// Before the DOM updates, so the first frame after a change still shows
|
|
114
|
+
// the old figure. Duration and curve are read from the value element at
|
|
115
|
+
// the change, so a retuned token applies. A change during a tick starts
|
|
116
|
+
// from the step on screen.
|
|
117
|
+
$effect.pre(() => {
|
|
118
|
+
const to = value;
|
|
119
|
+
untrack(() => {
|
|
120
|
+
const was = last;
|
|
121
|
+
last = to;
|
|
122
|
+
if (typeof to !== 'number' || to === was) return;
|
|
123
|
+
const resting = tween.current === tween.target;
|
|
124
|
+
const shown = resting ? tween.current : Number(tween.current.toFixed(places)) || 0;
|
|
125
|
+
const ms =
|
|
126
|
+
valueEl && typeof was === 'number' && !motion.reduced && !valueEl.closest(LIVE)
|
|
127
|
+
? duration(valueEl, '--dur-tick')
|
|
128
|
+
: 0;
|
|
129
|
+
if (!valueEl || ms <= 0 || !Number.isFinite(shown) || !Number.isFinite(to)) {
|
|
130
|
+
void tween.set(to, { duration: 0 });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
from = resting ? atRest(shown) : atStep(shown, places);
|
|
134
|
+
places = Math.max(decimals(shown), decimals(to));
|
|
135
|
+
// Tween would run the old tick for one more frame before the new
|
|
136
|
+
// one starts; stop it on the step on screen first.
|
|
137
|
+
if (!resting) void tween.set(shown, { duration: 0 });
|
|
138
|
+
void tween.set(to, { duration: ms, easing: curve(valueEl, '--tick') });
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
</script>
|
|
142
|
+
|
|
143
|
+
<!--
|
|
144
|
+
A caps label over a key figure in Geist with tabular figures (BRIEF, visual
|
|
145
|
+
rules 3 and 4; da3-65). A number value ticks to a new value over
|
|
146
|
+
`--dur-tick` on the `--tick` curve, never on the first render. The final
|
|
147
|
+
value stays in the accessible text; the moving steps are aria-hidden. The
|
|
148
|
+
root carries `data-ticking` during a tick. Extra attributes and `class` go
|
|
149
|
+
to the <dl>.
|
|
150
|
+
-->
|
|
151
|
+
<dl
|
|
152
|
+
{...rest}
|
|
153
|
+
class={['hp-stat', className]}
|
|
154
|
+
data-variant={variant}
|
|
155
|
+
data-tone={tone}
|
|
156
|
+
data-ticking={ticking ? '' : undefined}
|
|
157
|
+
>
|
|
158
|
+
<dt class="hp-stat-label hp-label">{label}</dt>
|
|
159
|
+
<dd
|
|
160
|
+
bind:this={valueEl}
|
|
161
|
+
class={['hp-stat-value', variant === 'display' ? 'hp-display' : 'hp-page-title hp-num']}
|
|
162
|
+
>
|
|
163
|
+
<span class="hp-stat-figure"
|
|
164
|
+
><span class="hp-stat-final">{text}</span>{#if ticking}<span
|
|
165
|
+
class="hp-stat-from"
|
|
166
|
+
aria-hidden="true">{from}</span
|
|
167
|
+
><span class="hp-stat-step" aria-hidden="true">{step}</span>{/if}</span
|
|
168
|
+
>{#if unit}{' '}<span class="hp-stat-unit">{unit}</span>{/if}
|
|
169
|
+
</dd>
|
|
170
|
+
{#if change !== undefined && change !== ''}
|
|
171
|
+
<dd class="hp-stat-delta">
|
|
172
|
+
<span class="hp-stat-change">{change}</span>{#if deltaNote}{' '}{deltaNote}{/if}
|
|
173
|
+
</dd>
|
|
174
|
+
{/if}
|
|
175
|
+
</dl>
|
|
176
|
+
|
|
177
|
+
<style>
|
|
178
|
+
.hp-stat {
|
|
179
|
+
display: grid;
|
|
180
|
+
align-content: start;
|
|
181
|
+
gap: var(--space-1);
|
|
182
|
+
min-width: 0;
|
|
183
|
+
}
|
|
184
|
+
.hp-stat[data-variant='display'] {
|
|
185
|
+
gap: var(--space-2);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.hp-stat-label {
|
|
189
|
+
color: var(--muted);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* Geist even inside a mono context such as a table cell. The size comes
|
|
193
|
+
from base.css: .hp-page-title (24px, 20px on compact) with .hp-num, or
|
|
194
|
+
.hp-display. A long figure breaks rather than widen a phone page. */
|
|
195
|
+
.hp-stat-value {
|
|
196
|
+
font-family: var(--sans);
|
|
197
|
+
overflow-wrap: anywhere;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* During a tick the figure stacks three layers in one grid cell: the
|
|
201
|
+
final value (transparent, read by screen readers), a hidden copy of
|
|
202
|
+
the figure shown when the tick began, and the moving step. The cell is
|
|
203
|
+
as wide as the wider of the old and new figure, so the unit after it
|
|
204
|
+
holds still; the steps lie between the two in value, and with tabular
|
|
205
|
+
figures in width too. */
|
|
206
|
+
.hp-stat[data-ticking] .hp-stat-figure {
|
|
207
|
+
display: inline-grid;
|
|
208
|
+
}
|
|
209
|
+
.hp-stat[data-ticking] .hp-stat-figure > * {
|
|
210
|
+
grid-area: 1 / 1;
|
|
211
|
+
}
|
|
212
|
+
.hp-stat[data-ticking] .hp-stat-final {
|
|
213
|
+
opacity: 0;
|
|
214
|
+
}
|
|
215
|
+
.hp-stat-from {
|
|
216
|
+
visibility: hidden;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.hp-stat-unit {
|
|
220
|
+
color: var(--muted);
|
|
221
|
+
font-size: var(--type-body);
|
|
222
|
+
font-weight: 400;
|
|
223
|
+
letter-spacing: 0;
|
|
224
|
+
}
|
|
225
|
+
.hp-stat[data-variant='display'] .hp-stat-unit {
|
|
226
|
+
font-size: var(--type-section);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* 16px on touch, the dense 14px on fine pointers. */
|
|
230
|
+
.hp-stat-delta {
|
|
231
|
+
color: var(--muted);
|
|
232
|
+
font-size: var(--type-body);
|
|
233
|
+
line-height: 24px;
|
|
234
|
+
font-variant-numeric: tabular-nums;
|
|
235
|
+
overflow-wrap: anywhere;
|
|
236
|
+
}
|
|
237
|
+
@media (pointer: fine) {
|
|
238
|
+
.hp-stat-delta {
|
|
239
|
+
font-size: var(--type-dense);
|
|
240
|
+
line-height: 20px;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.hp-stat[data-tone='ok'] .hp-stat-change {
|
|
245
|
+
color: var(--ok);
|
|
246
|
+
}
|
|
247
|
+
.hp-stat[data-tone='warn'] .hp-stat-change {
|
|
248
|
+
color: var(--warn);
|
|
249
|
+
}
|
|
250
|
+
.hp-stat[data-tone='danger'] .hp-stat-change {
|
|
251
|
+
color: var(--danger);
|
|
252
|
+
}
|
|
253
|
+
</style>
|