@timmo001/groundwork-ui 0.4.1 → 0.5.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.
@@ -10,6 +10,7 @@ import type { OptionItem } from "./gw-option-list.js";
10
10
  import "./gw-popover.js";
11
11
  import "./gw-segmented.js";
12
12
  import "./gw-select.js";
13
+ import "./gw-theme-gallery.js";
13
14
  import { base } from "./styles.js";
14
15
 
15
16
  // MDI's palette
@@ -33,15 +34,21 @@ const modes = [
33
34
  * `@timmo001/groundwork-tokens/page` does all of that. Show an import's
34
35
  * result, such as contrast misses, in `status`.
35
36
  *
37
+ * `gallery` swaps the theme select for `gw-theme-gallery`'s cards, which
38
+ * preview each theme in the chosen mode.
39
+ *
36
40
  * @element gw-theme-picker
37
41
  * @summary A button opening the page's mode, theme and import.
38
42
  *
39
43
  * @attr {"auto"|"light"|"dark"|"darker"} mode - The mode in use. Defaults to `auto`.
40
44
  * @attr {string} theme - The theme in use, by id.
41
- * @attr {OptionItem[]} themes - The themes to offer, as JSON.
45
+ * @attr {OptionItem[]} themes - The themes to offer, as JSON. The select hides with fewer than two. With `gallery`, a `group` gathers a family's themes.
46
+ * @attr {boolean} gallery - Shows the themes as preview cards rather than a select.
42
47
  * @attr {string} status - A line under the controls, such as an import's result.
43
48
  * @attr {string} label - The button's accessible name and the popover's heading. Defaults to `Theme`.
44
49
  *
50
+ * @csspart gallery - The theme gallery, with `gallery`.
51
+ *
45
52
  * @fires {CustomEvent<{ mode: string }>} mode - When a mode is picked.
46
53
  * @fires {CustomEvent<{ id: string }>} theme - When a theme is picked.
47
54
  * @fires {CustomEvent<{ text: string, name: string }>} import - When a theme file is chosen, with its text and file name.
@@ -52,6 +59,7 @@ export class GwThemePicker extends GwElement {
52
59
  mode: { reflect: true },
53
60
  theme: { reflect: true },
54
61
  themes: { type: "json" },
62
+ gallery: { type: "boolean", reflect: true },
55
63
  status: {},
56
64
  label: {},
57
65
  } as const;
@@ -63,6 +71,18 @@ export class GwThemePicker extends GwElement {
63
71
  display: inline-block;
64
72
  }
65
73
 
74
+ :host([gallery]) gw-popover {
75
+ --gw-popover-width: 34rem;
76
+ }
77
+
78
+ .gallery {
79
+ max-height: min(24rem, 50vh);
80
+ overflow: auto;
81
+ /* Room for the cards' focus rings */
82
+ padding: var(--gw-space-1);
83
+ margin: calc(var(--gw-space-1) * -1);
84
+ }
85
+
66
86
  .controls {
67
87
  display: flex;
68
88
  flex-direction: column;
@@ -81,6 +101,7 @@ export class GwThemePicker extends GwElement {
81
101
  declare mode: string;
82
102
  declare theme: string;
83
103
  declare themes: readonly OptionItem[];
104
+ declare gallery: boolean;
84
105
  declare status: string;
85
106
  declare label: string;
86
107
 
@@ -95,6 +116,11 @@ export class GwThemePicker extends GwElement {
95
116
 
96
117
  readonly #theme = h("gw-select", { label: "Theme" });
97
118
 
119
+ readonly #gallery = h("gw-theme-gallery", {
120
+ class: "gallery",
121
+ part: "gallery",
122
+ });
123
+
98
124
  readonly #status = h("p", { class: "status", role: "status" });
99
125
 
100
126
  readonly #file = h("input", {
@@ -106,7 +132,14 @@ export class GwThemePicker extends GwElement {
106
132
  readonly #popover = h(
107
133
  "gw-popover",
108
134
  {},
109
- h("div", { class: "controls" }, this.#mode, this.#theme, this.#status),
135
+ h(
136
+ "div",
137
+ { class: "controls" },
138
+ this.#mode,
139
+ this.#theme,
140
+ this.#gallery,
141
+ this.#status,
142
+ ),
110
143
  h(
111
144
  "gw-button",
112
145
  {
@@ -129,6 +162,7 @@ export class GwThemePicker extends GwElement {
129
162
  this.mode = "auto";
130
163
  this.theme = "";
131
164
  this.themes = [];
165
+ this.gallery = false;
132
166
  this.status = "";
133
167
  this.label = "Theme";
134
168
  }
@@ -136,6 +170,9 @@ export class GwThemePicker extends GwElement {
136
170
  /** Opens the popover under the button. */
137
171
  show() {
138
172
  this.#popover.show(this.#button);
173
+
174
+ // A custom theme's CSS may have changed since the cards were drawn
175
+ if (this.gallery) this.#gallery.refresh();
139
176
  }
140
177
 
141
178
  #detail<Detail>(type: string, detail: Detail) {
@@ -157,6 +194,11 @@ export class GwThemePicker extends GwElement {
157
194
  this.theme = this.#theme.value;
158
195
  this.dispatchEvent(this.#detail("theme", { id: this.theme }));
159
196
  });
197
+ this.#gallery.addEventListener("theme", (event) => {
198
+ event.stopPropagation();
199
+ this.theme = this.#gallery.theme;
200
+ this.dispatchEvent(this.#detail("theme", { id: this.theme }));
201
+ });
160
202
  this.#popover.querySelector(".import")?.addEventListener("click", () => {
161
203
  this.#file.click();
162
204
  });
@@ -186,6 +228,12 @@ export class GwThemePicker extends GwElement {
186
228
  this.#mode.value = this.mode;
187
229
  this.#theme.options = Array.isArray(this.themes) ? this.themes : [];
188
230
  this.#theme.value = this.theme;
231
+ this.#theme.hidden = this.gallery || this.#theme.options.length < 2;
232
+ this.#gallery.hidden = !this.gallery;
233
+ this.#gallery.themes = Array.isArray(this.themes) ? this.themes : [];
234
+ this.#gallery.theme = this.theme;
235
+ // Auto leaves it to follow the page, or the themed container around it
236
+ this.#gallery.mode = this.mode === "auto" ? "" : this.mode;
189
237
  this.#status.textContent = this.status;
190
238
  this.#status.hidden = !this.status;
191
239
  }
package/src/index.ts CHANGED
@@ -77,6 +77,8 @@ export * from "./gw-skeleton.js";
77
77
 
78
78
  export * from "./gw-spinner.js";
79
79
 
80
+ export * from "./gw-theme-gallery.js";
81
+
80
82
  export * from "./gw-theme-picker.js";
81
83
 
82
84
  export * from "./gw-toast.js";