@timmo001/groundwork-ui 0.4.1 → 0.6.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/src/gw-facts.ts CHANGED
@@ -28,6 +28,10 @@ const skeletonWidths = ["60%", "40%", "75%", "50%", "65%"];
28
28
  * @attr {Fact[]} items - The pairs, as JSON.
29
29
  * @attr {boolean} loading - Shows skeleton pairs, keeping any labels already known.
30
30
  * @attr {number} rows - How many skeleton pairs while loading. Defaults to 4.
31
+ *
32
+ * @csspart list - The description list.
33
+ * @csspart label - Each label.
34
+ * @csspart value - Each value.
31
35
  */
32
36
  export class GwFacts extends GwElement {
33
37
  static override properties = {
@@ -69,7 +73,7 @@ export class GwFacts extends GwElement {
69
73
  declare loading: boolean;
70
74
  declare rows: number;
71
75
 
72
- readonly #list = h("dl");
76
+ readonly #list = h("dl", { part: "list" });
73
77
 
74
78
  #rendered = false;
75
79
 
@@ -97,13 +101,13 @@ export class GwFacts extends GwElement {
97
101
  ...Array.from({ length: count }, (_, index) => [
98
102
  h(
99
103
  "dt",
100
- {},
104
+ { part: "label" },
101
105
  items[index]?.label ??
102
106
  h("gw-skeleton-text", { style: "--gw-skeleton-width: 5rem" }),
103
107
  ),
104
108
  h(
105
109
  "dd",
106
- {},
110
+ { part: "value" },
107
111
  h("gw-skeleton-text", {
108
112
  style: `--gw-skeleton-width: ${skeletonWidths[index % skeletonWidths.length]}`,
109
113
  }),
@@ -114,8 +118,12 @@ export class GwFacts extends GwElement {
114
118
  this.#list.removeAttribute("aria-busy");
115
119
  this.#list.replaceChildren(
116
120
  ...items.flatMap((item) => [
117
- h("dt", {}, item.label),
118
- h("dd", {}, loaded ? h("span", {}, item.value) : item.value),
121
+ h("dt", { part: "label" }, item.label),
122
+ h(
123
+ "dd",
124
+ { part: "value" },
125
+ loaded ? h("span", {}, item.value) : item.value,
126
+ ),
119
127
  ]),
120
128
  );
121
129
  }
package/src/gw-gauge.ts CHANGED
@@ -76,6 +76,12 @@ const circle = (className: string) => {
76
76
  * @attr {boolean} inverted - Lower is worse, such as for a battery.
77
77
  * @attr {boolean} loading - Pulses the track and shows a skeleton for the reading.
78
78
  *
79
+ * @csspart dial - The dial, with the arc and the reading.
80
+ * @csspart value - The reading's number.
81
+ * @csspart unit - The unit after it.
82
+ * @csspart label - What it measures.
83
+ * @csspart caption - The line under the label.
84
+ *
79
85
  * @cssprop --gw-gauge-size - The dial's width. Defaults to `8.5rem`.
80
86
  * @cssprop --gw-gauge-color - The arc's color, overriding the thresholds.
81
87
  */
@@ -208,11 +214,11 @@ export class GwGauge extends GwElement {
208
214
 
209
215
  readonly #fill = circle("fill");
210
216
 
211
- readonly #dial = h("div", { class: "dial", role: "meter" });
217
+ readonly #dial = h("div", { class: "dial", part: "dial", role: "meter" });
212
218
 
213
- readonly #value = h("span", { class: "value" });
219
+ readonly #value = h("span", { class: "value", part: "value" });
214
220
 
215
- readonly #unit = h("span", { class: "unit" });
221
+ readonly #unit = h("span", { class: "unit", part: "unit" });
216
222
 
217
223
  readonly #shown = h("span", { class: "shown" }, this.#value, this.#unit);
218
224
 
@@ -220,9 +226,9 @@ export class GwGauge extends GwElement {
220
226
  style: "--gw-skeleton-width: 2em",
221
227
  });
222
228
 
223
- readonly #label = h("span", { class: "label" });
229
+ readonly #label = h("span", { class: "label", part: "label" });
224
230
 
225
- readonly #caption = h("span", { class: "caption" });
231
+ readonly #caption = h("span", { class: "caption", part: "caption" });
226
232
 
227
233
  readonly #captionSkeleton = h("gw-skeleton-text", {
228
234
  style: "--gw-skeleton-width: 5rem",
package/src/gw-meter.ts CHANGED
@@ -26,6 +26,11 @@ import { base, type Tone } from "./styles.js";
26
26
  * @attr {string} display - The value as shown, such as `42 GB`. Defaults to a percentage.
27
27
  * @attr {"neutral"|"accent"|"info"|"success"|"warning"|"danger"|"important"} tone - A fixed color, overriding `high` and `critical`.
28
28
  * @attr {boolean} loading - Pulses the track and shows a skeleton for the value.
29
+ *
30
+ * @csspart top - The row with the label and value.
31
+ * @csspart value - The value as shown.
32
+ * @csspart track - The bar's track.
33
+ * @csspart fill - The filled part of the bar.
29
34
  */
30
35
  export class GwMeter extends GwElement {
31
36
  static override properties = {
@@ -93,7 +98,7 @@ export class GwMeter extends GwElement {
93
98
  declare tone: Tone | undefined;
94
99
  declare loading: boolean;
95
100
 
96
- readonly #top = h("div", { class: "top" });
101
+ readonly #top = h("div", { class: "top", part: "top" });
97
102
 
98
103
  readonly #label = h("span");
99
104
 
@@ -101,15 +106,23 @@ export class GwMeter extends GwElement {
101
106
  style: "--gw-skeleton-width: 6rem",
102
107
  });
103
108
 
104
- readonly #value = h("span", { class: "value", "aria-hidden": "true" });
109
+ readonly #value = h("span", {
110
+ class: "value",
111
+ part: "value",
112
+ "aria-hidden": "true",
113
+ });
105
114
 
106
115
  readonly #valueSkeleton = h("gw-skeleton-text", {
107
116
  style: "--gw-skeleton-width: 2.5rem",
108
117
  });
109
118
 
110
- readonly #fill = h("div", { class: "fill" });
119
+ readonly #fill = h("div", { class: "fill", part: "fill" });
111
120
 
112
- readonly #track = h("div", { class: "track", role: "meter" }, this.#fill);
121
+ readonly #track = h(
122
+ "div",
123
+ { class: "track", part: "track", role: "meter" },
124
+ this.#fill,
125
+ );
113
126
 
114
127
  readonly #bar = h("gw-skeleton", { class: "bar" });
115
128
 
@@ -29,6 +29,10 @@ const emptyIcon =
29
29
  *
30
30
  * @slot - The `gw-notification` cards.
31
31
  *
32
+ * @csspart header - The row with the heading and Clear all.
33
+ * @csspart heading - The heading.
34
+ * @csspart list - The list of cards.
35
+ *
32
36
  * @fires {Event} clear - When Clear all is pressed.
33
37
  */
34
38
  export class GwNotificationCenter extends GwElement {
@@ -75,7 +79,7 @@ export class GwNotificationCenter extends GwElement {
75
79
 
76
80
  declare heading: string;
77
81
 
78
- readonly #heading = h("h2");
82
+ readonly #heading = h("h2", { part: "heading" });
79
83
 
80
84
  readonly #clear = h(
81
85
  "gw-button",
@@ -83,7 +87,11 @@ export class GwNotificationCenter extends GwElement {
83
87
  "Clear all",
84
88
  );
85
89
 
86
- readonly #list = h("div", { class: "list", role: "list" }, h("slot"));
90
+ readonly #list = h(
91
+ "div",
92
+ { class: "list", part: "list", role: "list" },
93
+ h("slot"),
94
+ );
87
95
 
88
96
  readonly #empty = h(
89
97
  "gw-empty-state",
@@ -138,7 +146,10 @@ export class GwNotificationCenter extends GwElement {
138
146
  this.#clear.addEventListener("click", () => {
139
147
  this.clear();
140
148
  });
141
- root.append(h("header", {}, this.#heading, this.#clear), this.#body);
149
+ root.append(
150
+ h("header", { part: "header" }, this.#heading, this.#clear),
151
+ this.#body,
152
+ );
142
153
  }
143
154
 
144
155
  protected override update() {
@@ -34,6 +34,8 @@ const closeIcon =
34
34
  *
35
35
  * @csspart card - The card.
36
36
  *
37
+ * @cssprop --gw-tone - The icon's color, for a tone of your own. `tone` sets it.
38
+ *
37
39
  * @fires {Event} dismiss - When the close button is pressed. Cancel it to keep the card.
38
40
  */
39
41
  export class GwNotification extends GwElement {
@@ -81,6 +81,13 @@ let lastList = 0;
81
81
  * @attr {boolean} search - Shows a search field above the list.
82
82
  * @attr {string} placeholder - The search placeholder. Defaults to `Search`.
83
83
  *
84
+ * @csspart search - The search field's row.
85
+ * @csspart listbox - The scrolling list.
86
+ * @csspart option - Each option.
87
+ * @csspart group - Each group heading.
88
+ * @csspart description - An option's second line.
89
+ * @csspart none - The no matches line.
90
+ *
84
91
  * @cssprop --gw-option-list-height - The list's maximum height. Defaults to `20rem`.
85
92
  *
86
93
  * @fires {CustomEvent<{ value: string }>} pick - When an option is picked, with its `value` in `detail`.
@@ -273,11 +280,15 @@ export class GwOptionList extends GwElement {
273
280
 
274
281
  readonly #listbox = h(
275
282
  "div",
276
- { role: "listbox", tabindex: "-1" },
283
+ { role: "listbox", part: "listbox", tabindex: "-1" },
277
284
  this.#spacer,
278
285
  );
279
286
 
280
- readonly #none = h("div", { class: "none", hidden: true }, "No matches");
287
+ readonly #none = h(
288
+ "div",
289
+ { class: "none", part: "none", hidden: true },
290
+ "No matches",
291
+ );
281
292
 
282
293
  #query = "";
283
294
 
@@ -433,7 +444,12 @@ export class GwOptionList extends GwElement {
433
444
  });
434
445
 
435
446
  root.append(
436
- h("div", { class: "search" }, createIcon(searchIcon), this.#input),
447
+ h(
448
+ "div",
449
+ { class: "search", part: "search" },
450
+ createIcon(searchIcon),
451
+ this.#input,
452
+ ),
437
453
  this.#listbox,
438
454
  this.#none,
439
455
  );
@@ -585,7 +601,7 @@ export class GwOptionList extends GwElement {
585
601
  if (row.kind === "group") {
586
602
  const element = h(
587
603
  "div",
588
- { class: "row group", role: "presentation" },
604
+ { class: "row group", part: "group", role: "presentation" },
589
605
  row.label,
590
606
  );
591
607
 
@@ -602,6 +618,7 @@ export class GwOptionList extends GwElement {
602
618
  "div",
603
619
  {
604
620
  class: "row",
621
+ part: "option",
605
622
  role: "option",
606
623
  id: `${this.#id}-${index}`,
607
624
  "data-index": index,
@@ -618,7 +635,11 @@ export class GwOptionList extends GwElement {
618
635
  h("span", {}, option.label),
619
636
  option.description === undefined
620
637
  ? null
621
- : h("span", { class: "description" }, option.description),
638
+ : h(
639
+ "span",
640
+ { class: "description", part: "description" },
641
+ option.description,
642
+ ),
622
643
  ),
623
644
  option.swatches === undefined
624
645
  ? null
@@ -19,6 +19,10 @@ import { base, createIcon, setIconPath } from "./styles.js";
19
19
  * @attr {string} icon - SVG path data for the icon tile.
20
20
  *
21
21
  * @slot actions - The page's controls.
22
+ *
23
+ * @csspart tile - The icon tile.
24
+ * @csspart heading - The title.
25
+ * @csspart description - The line under the title.
22
26
  */
23
27
  export class GwPageHeader extends GwElement {
24
28
  static override properties = {
@@ -86,11 +90,11 @@ export class GwPageHeader extends GwElement {
86
90
 
87
91
  readonly #icon = createIcon("");
88
92
 
89
- readonly #tile = h("span", { class: "tile" }, this.#icon);
93
+ readonly #tile = h("span", { class: "tile", part: "tile" }, this.#icon);
90
94
 
91
- readonly #heading = h("h1");
95
+ readonly #heading = h("h1", { part: "heading" });
92
96
 
93
- readonly #description = h("p");
97
+ readonly #description = h("p", { part: "description" });
94
98
 
95
99
  constructor() {
96
100
  super();
package/src/gw-popover.ts CHANGED
@@ -38,6 +38,13 @@ const sheetBelow = 600;
38
38
  * @slot - The content.
39
39
  * @slot actions - Buttons; one with a `value` closes it with that value.
40
40
  *
41
+ * @csspart surface - The popover's surface.
42
+ * @csspart heading - The heading.
43
+ * @csspart body - The content.
44
+ * @csspart footer - The actions row.
45
+ *
46
+ * @cssprop --gw-popover-width - Its width beside a button. Defaults to `20rem`.
47
+ *
41
48
  * @fires {Event} close - When it closes; `returnValue` holds the button's value.
42
49
  */
43
50
  export class GwPopover extends GwElement {
@@ -54,7 +61,7 @@ export class GwPopover extends GwElement {
54
61
  position: fixed;
55
62
  inset: unset;
56
63
  box-sizing: border-box;
57
- width: min(20rem, calc(100vw - 2 * ${gap}px));
64
+ width: min(var(--gw-popover-width, 20rem), calc(100vw - 2 * ${gap}px));
58
65
  padding: var(--gw-space-4);
59
66
  margin: 0;
60
67
  color: var(--gw-color-text-primary);
@@ -185,7 +192,7 @@ export class GwPopover extends GwElement {
185
192
  /** The value of the action that closed it, or "" for Esc or a click outside. */
186
193
  returnValue = "";
187
194
 
188
- readonly #heading = h("h2", { id: "heading" });
195
+ readonly #heading = h("h2", { id: "heading", part: "heading" });
189
196
 
190
197
  readonly #closeButton = h(
191
198
  "gw-button",
@@ -202,7 +209,12 @@ export class GwPopover extends GwElement {
202
209
 
203
210
  readonly #popover = h(
204
211
  "div",
205
- { popover: "manual", tabindex: "-1", "data-placement": "center" },
212
+ {
213
+ popover: "manual",
214
+ tabindex: "-1",
215
+ "data-placement": "center",
216
+ part: "surface",
217
+ },
206
218
  h(
207
219
  "header",
208
220
  {},
@@ -210,11 +222,11 @@ export class GwPopover extends GwElement {
210
222
  "div",
211
223
  { class: "content" },
212
224
  this.#heading,
213
- h("div", { class: "body", id: "body" }, h("slot")),
225
+ h("div", { class: "body", id: "body", part: "body" }, h("slot")),
214
226
  ),
215
227
  this.#closeButton,
216
228
  ),
217
- h("footer", {}, h("slot", { name: "actions" })),
229
+ h("footer", { part: "footer" }, h("slot", { name: "actions" })),
218
230
  );
219
231
 
220
232
  readonly #dismissal = new Dismissal(
@@ -17,6 +17,8 @@ import { base } from "./styles.js";
17
17
  *
18
18
  * @slot - The heading text.
19
19
  * @slot actions - Buttons at the end of the row.
20
+ *
21
+ * @csspart heading - The heading.
20
22
  */
21
23
  export class GwSectionTitle extends GwElement {
22
24
  static override properties = {
@@ -46,7 +48,7 @@ export class GwSectionTitle extends GwElement {
46
48
 
47
49
  declare level: number;
48
50
 
49
- readonly #heading = h("div", { role: "heading" }, h("slot"));
51
+ readonly #heading = h("div", { role: "heading", part: "heading" }, h("slot"));
50
52
 
51
53
  constructor() {
52
54
  super();
@@ -32,6 +32,7 @@ export interface Segment {
32
32
  * @attr {"show"|"hide"|"auto"} labels - Whether option text shows beside icons. Defaults to `show`.
33
33
  * @attr {Segment[]} options - The options, as JSON.
34
34
  *
35
+ * @csspart option - Each option.
35
36
  * @csspart indicator - The pill that slides to the chosen option.
36
37
  *
37
38
  * @fires {Event} change - When the chosen option changes.
@@ -267,7 +268,7 @@ export class GwSegmented extends GwElement {
267
268
 
268
269
  const label = h(
269
270
  "label",
270
- { class: option.icon ? "has-icon" : null },
271
+ { class: option.icon ? "has-icon" : null, part: "option" },
271
272
  input,
272
273
  option.icon ? createIcon(option.icon) : null,
273
274
  h("span", { class: "text" }, option.label),
@@ -89,6 +89,8 @@ export class GwSkeletonText extends GwSkeleton {
89
89
  * @summary A placeholder for a paragraph.
90
90
  *
91
91
  * @attr {number} lines - How many lines. Defaults to 3.
92
+ *
93
+ * @csspart line - Each line.
92
94
  */
93
95
  export class GwSkeletonLines extends GwElement {
94
96
  static override properties = {
@@ -129,7 +131,7 @@ export class GwSkeletonLines extends GwElement {
129
131
  protected override update() {
130
132
  this.renderRoot.replaceChildren(
131
133
  ...Array.from({ length: Math.max(1, this.lines || 1) }, () =>
132
- h("gw-skeleton-text"),
134
+ h("gw-skeleton-text", { part: "line" }),
133
135
  ),
134
136
  );
135
137
  }
package/src/gw-spinner.ts CHANGED
@@ -15,6 +15,8 @@ import { base } from "./styles.js";
15
15
  *
16
16
  * @attr {string} label - What's loading, for assistive technology. Defaults to `Loading`.
17
17
  *
18
+ * @csspart ring - The spinning ring.
19
+ *
18
20
  * @cssprop --gw-spinner-size - The ring's size. Defaults to `1.25rem`.
19
21
  */
20
22
  export class GwSpinner extends GwElement {
@@ -67,7 +69,9 @@ export class GwSpinner extends GwElement {
67
69
  }
68
70
 
69
71
  protected override create(root: ShadowRoot) {
70
- root.append(h("span", { class: "ring", role: "status" }, this.#label));
72
+ root.append(
73
+ h("span", { class: "ring", part: "ring", role: "status" }, this.#label),
74
+ );
71
75
  }
72
76
 
73
77
  protected override update() {