@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.
Files changed (52) hide show
  1. package/CHANGELOG.md +42 -38
  2. package/LICENSE +25 -25
  3. package/README.md +67 -64
  4. package/dist/base.css +625 -625
  5. package/dist/components/AppShell.svelte +698 -698
  6. package/dist/components/Button.svelte +174 -174
  7. package/dist/components/Checkbox.svelte +235 -235
  8. package/dist/components/CheckboxGroup.svelte +207 -207
  9. package/dist/components/Combobox.svelte +901 -901
  10. package/dist/components/Dialog.svelte +359 -359
  11. package/dist/components/Disclosure.svelte +201 -201
  12. package/dist/components/EmptyState.svelte +88 -88
  13. package/dist/components/ErrorState.svelte +135 -135
  14. package/dist/components/ImageViewer.svelte +764 -764
  15. package/dist/components/KeyValueList.svelte +114 -114
  16. package/dist/components/ListDetail.svelte +218 -218
  17. package/dist/components/LoadRegion.svelte +219 -219
  18. package/dist/components/MediaFrame.svelte +110 -110
  19. package/dist/components/MediaImage.svelte +256 -256
  20. package/dist/components/Menu.svelte +258 -258
  21. package/dist/components/MenuCheckboxItem.svelte +119 -119
  22. package/dist/components/MenuGroup.svelte +50 -50
  23. package/dist/components/MenuItem.svelte +94 -94
  24. package/dist/components/MenuRadioGroup.svelte +62 -62
  25. package/dist/components/MenuRadioItem.svelte +110 -110
  26. package/dist/components/MenuSeparator.svelte +28 -28
  27. package/dist/components/Meter.svelte +283 -283
  28. package/dist/components/NativeSelect.svelte +135 -135
  29. package/dist/components/Notice.svelte +262 -262
  30. package/dist/components/Popover.svelte +222 -222
  31. package/dist/components/Progress.svelte +157 -157
  32. package/dist/components/RadioGroup.svelte +440 -440
  33. package/dist/components/Select.svelte +471 -471
  34. package/dist/components/Skeleton.svelte +149 -149
  35. package/dist/components/Spinner.svelte +118 -118
  36. package/dist/components/Stat.svelte +253 -253
  37. package/dist/components/Steps.svelte +189 -189
  38. package/dist/components/Switch.svelte +222 -222
  39. package/dist/components/Table.svelte +504 -504
  40. package/dist/components/Tabs.svelte +246 -246
  41. package/dist/components/Tag.svelte +40 -40
  42. package/dist/components/TextArea.svelte +272 -272
  43. package/dist/components/TextField.svelte +256 -256
  44. package/dist/components/Timeline.svelte +156 -156
  45. package/dist/components/Toaster.svelte +411 -411
  46. package/dist/components/Tooltip.svelte +200 -200
  47. package/dist/components/TooltipGroup.svelte +46 -46
  48. package/dist/components/internal/Scrim.svelte +52 -52
  49. package/dist/components/internal/Sheet.svelte +263 -263
  50. package/dist/fonts.css +71 -71
  51. package/dist/tokens.css +283 -283
  52. 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>