@timmo001/groundwork-ui 0.9.18 → 0.9.19

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/gw-meter.ts +66 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timmo001/groundwork-ui",
3
- "version": "0.9.18",
3
+ "version": "0.9.19",
4
4
  "description": "Groundwork's framework-free web components, styled by the --gw-* tokens",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
package/src/gw-meter.ts CHANGED
@@ -18,12 +18,14 @@ import { base, type Tone } from "./styles.js";
18
18
  * @summary A labeled bar for a value in a range.
19
19
  *
20
20
  * @attr {string} label - What it measures.
21
- * @attr {number} value - The value. Defaults to 0.
21
+ * @attr {number} value - The value. Defaults to 0. Leave it unset or `NaN` for an unknown value, shown as `-` with an empty bar.
22
22
  * @attr {number} min - The bottom of the range. Defaults to 0.
23
23
  * @attr {number} max - The top of the range. Defaults to 100.
24
24
  * @attr {number} high - Past this it turns warning.
25
25
  * @attr {number} critical - Past this it turns danger.
26
+ * @attr {boolean} inverted - Lower is worse, such as for a battery: at or below `high` it turns warning, and at or below `critical` danger.
26
27
  * @attr {string} display - The value as shown, such as `42 GB`. Defaults to a percentage.
28
+ * @attr {string} caption - A quiet line under the bar.
27
29
  * @attr {"neutral"|"accent"|"info"|"success"|"warning"|"danger"|"important"} tone - A fixed color, overriding `high` and `critical`.
28
30
  * @attr {boolean} loading - Pulses the track and shows a skeleton for the value.
29
31
  *
@@ -31,6 +33,9 @@ import { base, type Tone } from "./styles.js";
31
33
  * @csspart value - The value as shown.
32
34
  * @csspart track - The bar's track.
33
35
  * @csspart fill - The filled part of the bar.
36
+ * @csspart caption - The line under the bar.
37
+ *
38
+ * @cssprop --gw-meter-height - The bar's height. Defaults to `0.5rem`.
34
39
  */
35
40
  export class GwMeter extends GwElement {
36
41
  static override properties = {
@@ -40,7 +45,9 @@ export class GwMeter extends GwElement {
40
45
  max: { type: "number" },
41
46
  high: { type: "number" },
42
47
  critical: { type: "number" },
48
+ inverted: { type: "boolean" },
43
49
  display: {},
50
+ caption: {},
44
51
  tone: { reflect: true },
45
52
  loading: { type: "boolean", reflect: true },
46
53
  } as const;
@@ -66,7 +73,7 @@ export class GwMeter extends GwElement {
66
73
  }
67
74
 
68
75
  .track {
69
- height: 0.5rem;
76
+ height: var(--gw-meter-height, 0.5rem);
70
77
  overflow: hidden;
71
78
  background: var(--gw-color-surface-lower);
72
79
  border: var(--gw-border-width) solid var(--gw-color-border-quiet);
@@ -82,19 +89,35 @@ export class GwMeter extends GwElement {
82
89
 
83
90
  .bar {
84
91
  min-height: 0;
85
- height: 0.5rem;
92
+ height: var(--gw-meter-height, 0.5rem);
86
93
  --gw-skeleton-radius: var(--gw-radius-pill);
87
94
  }
95
+
96
+ .caption {
97
+ display: block;
98
+ margin-block-start: var(--gw-space-1);
99
+ overflow: hidden;
100
+ font-size: var(--gw-font-size-xs);
101
+ color: var(--gw-color-text-secondary);
102
+ text-overflow: ellipsis;
103
+ white-space: nowrap;
104
+ }
105
+
106
+ .caption[hidden] {
107
+ display: none;
108
+ }
88
109
  `,
89
110
  ];
90
111
 
91
112
  declare label: string;
92
- declare value: number;
113
+ declare value: number | undefined;
93
114
  declare min: number;
94
115
  declare max: number;
95
116
  declare high: number | undefined;
96
117
  declare critical: number | undefined;
118
+ declare inverted: boolean;
97
119
  declare display: string;
120
+ declare caption: string;
98
121
  declare tone: Tone | undefined;
99
122
  declare loading: boolean;
100
123
 
@@ -126,6 +149,8 @@ export class GwMeter extends GwElement {
126
149
 
127
150
  readonly #bar = h("gw-skeleton", { class: "bar" });
128
151
 
152
+ readonly #caption = h("span", { class: "caption", part: "caption" });
153
+
129
154
  #rendered = false;
130
155
 
131
156
  constructor() {
@@ -136,21 +161,33 @@ export class GwMeter extends GwElement {
136
161
  this.max = 100;
137
162
  this.high = undefined;
138
163
  this.critical = undefined;
164
+ this.inverted = false;
139
165
  this.display = "";
166
+ this.caption = "";
140
167
  this.tone = undefined;
141
168
  this.loading = false;
142
169
  }
143
170
 
171
+ get #known() {
172
+ return this.value !== undefined && Number.isFinite(this.value)
173
+ ? this.value
174
+ : undefined;
175
+ }
176
+
177
+ #worse(limit: number | undefined) {
178
+ const value = this.#known;
179
+
180
+ if (limit === undefined || value === undefined) return false;
181
+
182
+ return this.inverted ? value <= limit : value >= limit;
183
+ }
184
+
144
185
  get #color() {
145
186
  if (this.tone !== undefined) return "var(--gw-tone)";
146
187
 
147
- if (this.critical !== undefined && this.value >= this.critical) {
148
- return "var(--gw-color-danger)";
149
- }
188
+ if (this.#worse(this.critical)) return "var(--gw-color-danger)";
150
189
 
151
- if (this.high !== undefined && this.value >= this.high) {
152
- return "var(--gw-color-warning)";
153
- }
190
+ if (this.#worse(this.high)) return "var(--gw-color-warning)";
154
191
 
155
192
  return "var(--gw-color-fill-accent-resting)";
156
193
  }
@@ -161,18 +198,21 @@ export class GwMeter extends GwElement {
161
198
  this.#value,
162
199
  this.#valueSkeleton,
163
200
  );
164
- root.append(this.#top, this.#track, this.#bar);
201
+ root.append(this.#top, this.#track, this.#bar, this.#caption);
165
202
  }
166
203
 
167
204
  protected override update(changed: ReadonlySet<string>) {
205
+ const value = this.#known;
206
+
168
207
  const span = this.max - this.min || 1;
169
208
 
170
- const percent = Math.min(
171
- 100,
172
- Math.max(0, ((this.value - this.min) / span) * 100),
173
- );
209
+ const percent =
210
+ value === undefined
211
+ ? 0
212
+ : Math.min(100, Math.max(0, ((value - this.min) / span) * 100));
174
213
 
175
- const text = this.display || `${Math.round(percent)}%`;
214
+ const text =
215
+ this.display || (value === undefined ? "-" : `${Math.round(percent)}%`);
176
216
 
177
217
  setAttribute(this.#top, "aria-busy", this.loading ? "true" : null);
178
218
  this.#label.textContent = this.label;
@@ -182,16 +222,24 @@ export class GwMeter extends GwElement {
182
222
  this.#valueSkeleton.hidden = !this.loading;
183
223
  this.#track.hidden = this.loading;
184
224
  this.#bar.hidden = !this.loading;
225
+ this.#caption.textContent = this.caption;
226
+ this.#caption.hidden = this.loading || this.caption === "";
185
227
  setAttribute(this.#track, "aria-label", this.label || null);
186
228
  setAttribute(this.#track, "aria-valuemin", this.min);
187
229
  setAttribute(this.#track, "aria-valuemax", this.max);
188
- setAttribute(this.#track, "aria-valuenow", this.value);
189
- setAttribute(this.#track, "aria-valuetext", text);
230
+ // A meter needs a value; when it's unknown, the text says so
231
+ setAttribute(this.#track, "aria-valuenow", value ?? this.min);
232
+ setAttribute(
233
+ this.#track,
234
+ "aria-valuetext",
235
+ value !== undefined || this.display !== "" ? text : "Unknown",
236
+ );
190
237
  this.#fill.style.width = `${percent}%`;
191
238
  this.#fill.style.setProperty("--gw-meter-color", this.#color);
192
239
 
193
240
  if (this.#rendered && changed.has("loading") && !this.loading) {
194
241
  fadeIn(this.#value);
242
+ fadeIn(this.#caption);
195
243
  }
196
244
 
197
245
  this.#rendered = true;