@phcdevworks/spectre-components 0.0.1 → 1.1.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.
Files changed (62) hide show
  1. package/README.md +113 -40
  2. package/dist/base-2rD8EmdC.d.cts +32 -0
  3. package/dist/base-2rD8EmdC.d.ts +32 -0
  4. package/dist/button.cjs +295 -170
  5. package/dist/button.cjs.map +1 -1
  6. package/dist/button.d.cts +30 -61
  7. package/dist/button.d.ts +30 -61
  8. package/dist/button.js +296 -171
  9. package/dist/button.js.map +1 -1
  10. package/dist/checkbox.cjs +286 -71
  11. package/dist/checkbox.cjs.map +1 -1
  12. package/dist/checkbox.d.cts +38 -43
  13. package/dist/checkbox.d.ts +38 -43
  14. package/dist/checkbox.js +287 -72
  15. package/dist/checkbox.js.map +1 -1
  16. package/dist/fieldset.cjs +233 -88
  17. package/dist/fieldset.cjs.map +1 -1
  18. package/dist/fieldset.d.cts +23 -40
  19. package/dist/fieldset.d.ts +23 -40
  20. package/dist/fieldset.js +233 -88
  21. package/dist/fieldset.js.map +1 -1
  22. package/dist/form-iI7vV-3W.d.cts +6 -0
  23. package/dist/form-iI7vV-3W.d.ts +6 -0
  24. package/dist/index.cjs +564 -1029
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +5 -2
  27. package/dist/index.d.ts +5 -2
  28. package/dist/index.js +566 -1030
  29. package/dist/index.js.map +1 -1
  30. package/dist/input.cjs +232 -119
  31. package/dist/input.cjs.map +1 -1
  32. package/dist/input.d.cts +52 -68
  33. package/dist/input.d.ts +52 -68
  34. package/dist/input.js +232 -119
  35. package/dist/input.js.map +1 -1
  36. package/dist/label.cjs +232 -73
  37. package/dist/label.cjs.map +1 -1
  38. package/dist/label.d.cts +13 -22
  39. package/dist/label.d.ts +13 -22
  40. package/dist/label.js +232 -73
  41. package/dist/label.js.map +1 -1
  42. package/dist/projectable-BzDQ4xp_.d.cts +17 -0
  43. package/dist/projectable-s3SgvjGX.d.ts +17 -0
  44. package/dist/radio.cjs +286 -71
  45. package/dist/radio.cjs.map +1 -1
  46. package/dist/radio.d.cts +38 -43
  47. package/dist/radio.d.ts +38 -43
  48. package/dist/radio.js +287 -72
  49. package/dist/radio.js.map +1 -1
  50. package/dist/select.cjs +292 -163
  51. package/dist/select.cjs.map +1 -1
  52. package/dist/select.d.cts +35 -58
  53. package/dist/select.d.ts +35 -58
  54. package/dist/select.js +293 -163
  55. package/dist/select.js.map +1 -1
  56. package/dist/textarea.cjs +191 -290
  57. package/dist/textarea.cjs.map +1 -1
  58. package/dist/textarea.d.cts +41 -58
  59. package/dist/textarea.d.ts +41 -58
  60. package/dist/textarea.js +191 -290
  61. package/dist/textarea.js.map +1 -1
  62. package/package.json +13 -12
package/dist/index.js CHANGED
@@ -1,9 +1,268 @@
1
- import { LitElement, nothing, html } from 'lit';
1
+ import { LitElement, html, nothing } from 'lit';
2
2
  import { ifDefined } from 'lit/directives/if-defined.js';
3
3
  import { getButtonClasses, getInputClasses } from '@phcdevworks/spectre-ui';
4
4
  import { live } from 'lit/directives/live.js';
5
5
 
6
6
  // src/components/button/sp-button.ts
7
+ var SpectreBaseElement = class extends LitElement {
8
+ constructor() {
9
+ super(...arguments);
10
+ this._ariaLabel = null;
11
+ this._ariaLabelledBy = null;
12
+ this._ariaDescribedBy = null;
13
+ this._title = "";
14
+ this._id = "";
15
+ }
16
+ get ariaLabel() {
17
+ return this._ariaLabel;
18
+ }
19
+ set ariaLabel(value) {
20
+ if (this._ariaLabel === value) {
21
+ return;
22
+ }
23
+ this._ariaLabel = value;
24
+ this._removeHostAttribute("aria-label");
25
+ this.requestUpdate();
26
+ }
27
+ get ariaLabelledBy() {
28
+ return this._ariaLabelledBy;
29
+ }
30
+ set ariaLabelledBy(value) {
31
+ if (this._ariaLabelledBy === value) {
32
+ return;
33
+ }
34
+ this._ariaLabelledBy = value;
35
+ this._removeHostAttribute("aria-labelledby");
36
+ this.requestUpdate();
37
+ }
38
+ get ariaDescribedBy() {
39
+ return this._ariaDescribedBy;
40
+ }
41
+ set ariaDescribedBy(value) {
42
+ if (this._ariaDescribedBy === value) {
43
+ return;
44
+ }
45
+ this._ariaDescribedBy = value;
46
+ this._removeHostAttribute("aria-describedby");
47
+ this.requestUpdate();
48
+ }
49
+ get title() {
50
+ return this._title;
51
+ }
52
+ set title(value) {
53
+ const normalizedValue = value ?? "";
54
+ if (this._title === normalizedValue) {
55
+ return;
56
+ }
57
+ this._title = normalizedValue;
58
+ this._removeHostAttribute("title");
59
+ this.requestUpdate();
60
+ }
61
+ get id() {
62
+ return this._id;
63
+ }
64
+ set id(value) {
65
+ const normalizedValue = value ?? "";
66
+ if (this._id === normalizedValue) {
67
+ return;
68
+ }
69
+ this._id = normalizedValue;
70
+ this._removeHostAttribute("id");
71
+ this.requestUpdate();
72
+ }
73
+ // Spectre components intentionally render in light DOM so the global
74
+ // `@phcdevworks/spectre-ui` styling contract can apply directly.
75
+ createRenderRoot() {
76
+ return this;
77
+ }
78
+ connectedCallback() {
79
+ super.connectedCallback();
80
+ const proxiedAttributes = [
81
+ "id",
82
+ "title",
83
+ "aria-label",
84
+ "aria-labelledby",
85
+ "aria-describedby"
86
+ ];
87
+ proxiedAttributes.forEach((attr) => {
88
+ const value = super.getAttribute(attr);
89
+ if (value !== null) {
90
+ this.setAttribute(attr, value);
91
+ }
92
+ });
93
+ }
94
+ getAttribute(qualifiedName) {
95
+ switch (qualifiedName) {
96
+ case "id":
97
+ return this.id || null;
98
+ case "title":
99
+ return this.title || null;
100
+ case "aria-label":
101
+ return this.ariaLabel;
102
+ case "aria-labelledby":
103
+ return this.ariaLabelledBy;
104
+ case "aria-describedby":
105
+ return this.ariaDescribedBy;
106
+ default:
107
+ return super.getAttribute(qualifiedName);
108
+ }
109
+ }
110
+ hasAttribute(qualifiedName) {
111
+ switch (qualifiedName) {
112
+ case "id":
113
+ return this.id !== "";
114
+ case "title":
115
+ return this.title !== "";
116
+ case "aria-label":
117
+ return this.ariaLabel !== null;
118
+ case "aria-labelledby":
119
+ return this.ariaLabelledBy !== null;
120
+ case "aria-describedby":
121
+ return this.ariaDescribedBy !== null;
122
+ default:
123
+ return super.hasAttribute(qualifiedName);
124
+ }
125
+ }
126
+ setAttribute(qualifiedName, value) {
127
+ switch (qualifiedName) {
128
+ case "id":
129
+ this.id = value;
130
+ break;
131
+ case "title":
132
+ this.title = value;
133
+ break;
134
+ case "aria-label":
135
+ this.ariaLabel = value;
136
+ break;
137
+ case "aria-labelledby":
138
+ this.ariaLabelledBy = value;
139
+ break;
140
+ case "aria-describedby":
141
+ this.ariaDescribedBy = value;
142
+ break;
143
+ default:
144
+ super.setAttribute(qualifiedName, value);
145
+ }
146
+ }
147
+ removeAttribute(qualifiedName) {
148
+ switch (qualifiedName) {
149
+ case "id":
150
+ this.id = "";
151
+ break;
152
+ case "title":
153
+ this.title = "";
154
+ break;
155
+ case "aria-label":
156
+ this.ariaLabel = null;
157
+ break;
158
+ case "aria-labelledby":
159
+ this.ariaLabelledBy = null;
160
+ break;
161
+ case "aria-describedby":
162
+ this.ariaDescribedBy = null;
163
+ break;
164
+ default:
165
+ super.removeAttribute(qualifiedName);
166
+ }
167
+ }
168
+ _removeHostAttribute(name) {
169
+ const host = this;
170
+ if (HTMLElement.prototype.hasAttribute.call(host, name)) {
171
+ HTMLElement.prototype.removeAttribute.call(host, name);
172
+ }
173
+ }
174
+ get forwardedAriaLabel() {
175
+ const value = this.ariaLabel?.trim();
176
+ return value ? value : void 0;
177
+ }
178
+ get forwardedAriaLabelledBy() {
179
+ const value = this.ariaLabelledBy?.trim();
180
+ return value ? value : void 0;
181
+ }
182
+ get forwardedAriaDescribedBy() {
183
+ const value = this.ariaDescribedBy?.trim();
184
+ return value ? value : void 0;
185
+ }
186
+ };
187
+ SpectreBaseElement.properties = {};
188
+
189
+ // src/utils/dom.ts
190
+ function hasMeaningfulContent(nodes) {
191
+ return nodes.some((node) => {
192
+ if (node.nodeType === Node.ELEMENT_NODE) {
193
+ return true;
194
+ }
195
+ if (node.nodeType === Node.TEXT_NODE) {
196
+ return (node.textContent?.trim().length ?? 0) > 0;
197
+ }
198
+ return false;
199
+ });
200
+ }
201
+
202
+ // src/utils/projectable.ts
203
+ var SpectreProjectableElement = class extends SpectreBaseElement {
204
+ constructor() {
205
+ super(...arguments);
206
+ this.projectedContent = [];
207
+ }
208
+ get hasProjectedContent() {
209
+ return hasMeaningfulContent(this.projectedContent);
210
+ }
211
+ connectedCallback() {
212
+ super.connectedCallback();
213
+ this.syncProjectedContent();
214
+ this.startContentObserver();
215
+ }
216
+ disconnectedCallback() {
217
+ this.stopContentObserver();
218
+ super.disconnectedCallback();
219
+ }
220
+ update(changedProperties) {
221
+ this.stopContentObserver();
222
+ super.update(changedProperties);
223
+ this.startContentObserver();
224
+ }
225
+ syncProjectedContent() {
226
+ const nextProjectedContent = [];
227
+ const sourceNodes = [
228
+ ...this.childNodes,
229
+ ...this.getContentContainer()?.childNodes ?? []
230
+ ];
231
+ sourceNodes.forEach((node) => {
232
+ if (!this.isInternalNode(node) && !nextProjectedContent.includes(node)) {
233
+ nextProjectedContent.push(node);
234
+ }
235
+ });
236
+ const hasChanged = nextProjectedContent.length !== this.projectedContent.length || nextProjectedContent.some((node, index) => node !== this.projectedContent[index]);
237
+ if (hasChanged) {
238
+ this.projectedContent = nextProjectedContent;
239
+ }
240
+ return hasChanged;
241
+ }
242
+ startContentObserver() {
243
+ if (this.contentObserver) {
244
+ return;
245
+ }
246
+ this.contentObserver = new MutationObserver((mutations) => {
247
+ const isInternalMovement = mutations.every(
248
+ (mutation) => [...mutation.removedNodes].every(
249
+ (node) => this.isInternalNode(node) || this.contains(node)
250
+ ) && [...mutation.addedNodes].every((node) => this.isInternalNode(node))
251
+ );
252
+ if (isInternalMovement) {
253
+ return;
254
+ }
255
+ if (this.syncProjectedContent()) {
256
+ this.requestUpdate();
257
+ }
258
+ });
259
+ this.contentObserver.observe(this, { childList: true });
260
+ }
261
+ stopContentObserver() {
262
+ this.contentObserver?.disconnect();
263
+ this.contentObserver = void 0;
264
+ }
265
+ };
7
266
  var spectreButtonVariants = [
8
267
  "primary",
9
268
  "secondary",
@@ -24,23 +283,9 @@ function isButtonSize(value) {
24
283
  function isButtonType(value) {
25
284
  return spectreButtonTypes.includes(value);
26
285
  }
27
- function hasMeaningfulContent(nodes) {
28
- return nodes.some((node) => {
29
- if (node.nodeType === Node.ELEMENT_NODE) {
30
- return true;
31
- }
32
- if (node.nodeType === Node.TEXT_NODE) {
33
- return (node.textContent?.trim().length ?? 0) > 0;
34
- }
35
- return false;
36
- });
37
- }
38
- var SpectreButtonElement = class extends LitElement {
286
+ var SpectreButtonElement = class extends SpectreProjectableElement {
39
287
  constructor() {
40
288
  super(...arguments);
41
- this.ariaLabel = null;
42
- this.ariaLabelledBy = null;
43
- this.ariaDescribedBy = null;
44
289
  this.autofocus = false;
45
290
  this.disabled = false;
46
291
  this.fullWidth = false;
@@ -48,68 +293,19 @@ var SpectreButtonElement = class extends LitElement {
48
293
  this.loadingLabel = "Loading";
49
294
  this.pill = false;
50
295
  this.size = "md";
51
- this.title = "";
52
296
  this.type = "button";
53
297
  this.variant = "primary";
54
- // Native slot projection is a Shadow DOM feature, so in light DOM we keep
55
- // host-provided content reactive by tracking and reusing the host nodes.
56
- this.projectedContent = [];
57
- }
58
- get id() {
59
- return this._id ?? "";
60
- }
61
- set id(value) {
62
- if ((this._id ?? "") === value) {
63
- return;
64
- }
65
- this._id = value;
66
- const host = this;
67
- if (HTMLElement.prototype.hasAttribute.call(host, "id")) {
68
- HTMLElement.prototype.removeAttribute.call(host, "id");
69
- }
70
- this.requestUpdate();
71
- }
72
- createRenderRoot() {
73
- return this;
74
- }
75
- connectedCallback() {
76
- super.connectedCallback();
77
- const hostId = super.getAttribute("id");
78
- if (hostId !== null) {
79
- this.id = hostId;
80
- }
81
- this.syncProjectedContent();
82
- this.startContentObserver();
83
- }
84
- getAttribute(qualifiedName) {
85
- if (qualifiedName === "id") {
86
- return this.id || null;
87
- }
88
- return super.getAttribute(qualifiedName);
89
- }
90
- hasAttribute(qualifiedName) {
91
- if (qualifiedName === "id") {
92
- return this.id !== "";
93
- }
94
- return super.hasAttribute(qualifiedName);
298
+ this.value = "";
95
299
  }
96
- setAttribute(qualifiedName, value) {
97
- if (qualifiedName === "id") {
98
- this.id = value;
99
- return;
100
- }
101
- super.setAttribute(qualifiedName, value);
300
+ getContentContainer() {
301
+ return this.querySelector("[data-sp-button-native]");
102
302
  }
103
- removeAttribute(qualifiedName) {
104
- if (qualifiedName === "id") {
105
- this.id = "";
106
- return;
303
+ isInternalNode(node) {
304
+ if (node.nodeType !== Node.ELEMENT_NODE) {
305
+ return false;
107
306
  }
108
- super.removeAttribute(qualifiedName);
109
- }
110
- disconnectedCallback() {
111
- this.stopContentObserver();
112
- super.disconnectedCallback();
307
+ const el = node;
308
+ return el.hasAttribute("data-sp-button-native") || el.hasAttribute("data-sp-button-loading-label") || el.hasAttribute("data-sp-button-label-fallback");
113
309
  }
114
310
  willUpdate(changedProperties) {
115
311
  if (changedProperties.has("variant") && !isButtonVariant(this.variant)) {
@@ -126,11 +322,9 @@ var SpectreButtonElement = class extends LitElement {
126
322
  this.loadingLabel = "Loading";
127
323
  }
128
324
  }
129
- }
130
- update(changedProperties) {
131
- this.stopContentObserver();
132
- super.update(changedProperties);
133
- this.startContentObserver();
325
+ if (changedProperties.has("value") && this.value == null) {
326
+ this.value = "";
327
+ }
134
328
  }
135
329
  get buttonClasses() {
136
330
  return getButtonClasses({
@@ -145,118 +339,50 @@ var SpectreButtonElement = class extends LitElement {
145
339
  get isDisabled() {
146
340
  return this.disabled || this.loading;
147
341
  }
148
- get hasProjectedContent() {
149
- return hasMeaningfulContent(this.projectedContent);
150
- }
151
342
  get visibleLabelFallback() {
152
343
  const trimmedLabel = this.label?.trim();
153
344
  return trimmedLabel ? trimmedLabel : void 0;
154
345
  }
155
- get forwardedAriaLabel() {
156
- const ariaLabel = this.ariaLabel?.trim();
157
- return ariaLabel ? ariaLabel : void 0;
158
- }
159
- get forwardedAriaLabelledBy() {
160
- const ariaLabelledBy = this.ariaLabelledBy?.trim();
161
- return ariaLabelledBy ? ariaLabelledBy : void 0;
162
- }
163
- get forwardedAriaDescribedBy() {
164
- const ariaDescribedBy = this.ariaDescribedBy?.trim();
165
- return ariaDescribedBy ? ariaDescribedBy : void 0;
166
- }
167
- startContentObserver() {
168
- if (this.contentObserver) {
169
- return;
170
- }
171
- this.contentObserver = new MutationObserver((mutations) => {
172
- const isInternalMovement = mutations.every((mutation) => {
173
- return Array.from(mutation.removedNodes).every(
174
- (node) => this.isInternalButtonNode(node) || this.contains(node)
175
- ) && Array.from(mutation.addedNodes).every((node) => this.isInternalButtonNode(node));
176
- });
177
- if (isInternalMovement) {
178
- return;
179
- }
180
- if (this.syncProjectedContent()) {
181
- this.requestUpdate();
182
- }
183
- });
184
- this.contentObserver.observe(this, {
185
- childList: true
186
- });
187
- }
188
- stopContentObserver() {
189
- this.contentObserver?.disconnect();
190
- this.contentObserver = void 0;
191
- }
192
- syncProjectedContent() {
193
- const nextProjectedContent = [];
194
- Array.from(this.childNodes).forEach((node) => {
195
- if (this.isInternalButtonNode(node)) {
196
- this.projectedContent.forEach((pNode) => {
197
- if (pNode.parentNode !== this && this.contains(pNode)) {
198
- nextProjectedContent.push(pNode);
199
- }
200
- });
201
- } else {
202
- nextProjectedContent.push(node);
203
- }
204
- });
205
- const hasChanged = nextProjectedContent.length !== this.projectedContent.length || nextProjectedContent.some((node, index) => node !== this.projectedContent[index]);
206
- if (hasChanged) {
207
- this.projectedContent = nextProjectedContent;
208
- }
209
- return hasChanged;
210
- }
211
- isInternalButtonNode(node) {
212
- return node.nodeType === Node.ELEMENT_NODE && node.hasAttribute("data-sp-button-native");
213
- }
214
- get nativeButton() {
215
- return this.querySelector("[data-sp-button-native]");
216
- }
217
346
  focus(options) {
218
- this.nativeButton?.focus(options);
347
+ this.getContentContainer()?.focus(options);
219
348
  }
220
349
  blur() {
221
- this.nativeButton?.blur();
350
+ this.getContentContainer()?.blur();
222
351
  }
223
352
  renderButtonContent() {
224
353
  if (this.loading) {
225
- return this.loadingLabel;
354
+ return html`<span data-sp-button-loading-label>${this.loadingLabel}</span>`;
226
355
  }
227
356
  if (this.hasProjectedContent) {
228
357
  return this.projectedContent;
229
358
  }
230
- return this.visibleLabelFallback ?? "";
359
+ return this.visibleLabelFallback ? html`<span data-sp-button-label-fallback>${this.visibleLabelFallback}</span>` : nothing;
231
360
  }
232
361
  render() {
233
- return html`
234
- <button
235
- aria-busy=${this.loading ? "true" : "false"}
236
- aria-describedby=${ifDefined(this.forwardedAriaDescribedBy)}
237
- aria-label=${ifDefined(this.forwardedAriaLabel)}
238
- aria-labelledby=${ifDefined(this.forwardedAriaLabelledBy)}
239
- ?autofocus=${this.autofocus}
240
- class=${this.buttonClasses}
241
- data-sp-button-native
242
- ?disabled=${this.isDisabled}
243
- id=${ifDefined(this.id || void 0)}
244
- name=${ifDefined(this.name)}
245
- title=${ifDefined(this.title || void 0)}
246
- type=${this.type}
247
- value=${ifDefined(this.value)}
248
- >
249
- ${this.renderButtonContent() || nothing}
250
- </button>
251
- `;
362
+ return html`<button
363
+ aria-busy="${this.loading ? "true" : "false"}"
364
+ aria-describedby="${ifDefined(this.forwardedAriaDescribedBy)}"
365
+ aria-label="${ifDefined(this.forwardedAriaLabel)}"
366
+ aria-labelledby="${ifDefined(this.forwardedAriaLabelledBy)}"
367
+ ?autofocus="${this.autofocus}"
368
+ class="${this.buttonClasses}"
369
+ data-sp-button-native
370
+ ?disabled="${this.isDisabled}"
371
+ form="${ifDefined(this.form || void 0)}"
372
+ id="${ifDefined(this.id || void 0)}"
373
+ name="${ifDefined(this.name || void 0)}"
374
+ title="${ifDefined(this.title || void 0)}"
375
+ type="${this.type}"
376
+ value="${ifDefined(this.value || void 0)}"
377
+ >
378
+ ${this.renderButtonContent()}
379
+ </button>`;
252
380
  }
253
381
  };
254
382
  SpectreButtonElement.properties = {
255
- ariaLabel: { attribute: "aria-label", type: String },
256
- ariaLabelledBy: { attribute: "aria-labelledby", type: String },
257
- ariaDescribedBy: { attribute: "aria-describedby", type: String },
258
383
  autofocus: { type: Boolean, reflect: true },
259
384
  disabled: { type: Boolean, reflect: true },
385
+ form: { type: String },
260
386
  fullWidth: { attribute: "full-width", type: Boolean, reflect: true },
261
387
  label: { type: String, reflect: true },
262
388
  loading: { type: Boolean, reflect: true },
@@ -264,10 +390,9 @@ SpectreButtonElement.properties = {
264
390
  name: { type: String, reflect: true },
265
391
  pill: { type: Boolean, reflect: true },
266
392
  size: { type: String, reflect: true },
267
- title: { type: String, reflect: true },
268
393
  type: { type: String, reflect: true },
269
394
  variant: { type: String, reflect: true },
270
- value: { type: String, reflect: true }
395
+ value: { type: String }
271
396
  };
272
397
  function defineSpectreButton(tagName = "sp-button") {
273
398
  const existingElement = customElements.get(tagName);
@@ -277,6 +402,10 @@ function defineSpectreButton(tagName = "sp-button") {
277
402
  customElements.define(tagName, SpectreButtonElement);
278
403
  return SpectreButtonElement;
279
404
  }
405
+ var spectreInputSizes = ["sm", "md", "lg"];
406
+ function isInputSize(value) {
407
+ return spectreInputSizes.includes(value);
408
+ }
280
409
  var spectreInputTypes = [
281
410
  "text",
282
411
  "email",
@@ -291,19 +420,18 @@ var spectreInputTypes = [
291
420
  "time",
292
421
  "week"
293
422
  ];
294
- var spectreInputSizes = ["sm", "md", "lg"];
295
423
  function isInputType(value) {
296
424
  return spectreInputTypes.includes(value);
297
425
  }
298
- function isInputSize(value) {
299
- return spectreInputSizes.includes(value);
426
+ function normalizeInt(value, fallback, min = 0) {
427
+ if (value == null || typeof value !== "number" || !Number.isInteger(value) || value < min) {
428
+ return fallback;
429
+ }
430
+ return value;
300
431
  }
301
- var SpectreInputElement = class extends LitElement {
432
+ var SpectreInputElement = class extends SpectreBaseElement {
302
433
  constructor() {
303
434
  super(...arguments);
304
- this.ariaLabel = null;
305
- this.ariaLabelledBy = null;
306
- this.ariaDescribedBy = null;
307
435
  this.autofocus = false;
308
436
  this.disabled = false;
309
437
  this.fullWidth = false;
@@ -314,60 +442,9 @@ var SpectreInputElement = class extends LitElement {
314
442
  this.required = false;
315
443
  this.size = "md";
316
444
  this.success = false;
317
- this.title = "";
318
445
  this.type = "text";
319
446
  this.value = "";
320
447
  }
321
- get id() {
322
- return this._id ?? "";
323
- }
324
- set id(value) {
325
- if ((this._id ?? "") === value) {
326
- return;
327
- }
328
- this._id = value;
329
- const host = this;
330
- if (HTMLElement.prototype.hasAttribute.call(host, "id")) {
331
- HTMLElement.prototype.removeAttribute.call(host, "id");
332
- }
333
- this.requestUpdate();
334
- }
335
- createRenderRoot() {
336
- return this;
337
- }
338
- connectedCallback() {
339
- super.connectedCallback();
340
- const hostId = super.getAttribute("id");
341
- if (hostId !== null) {
342
- this.id = hostId;
343
- }
344
- }
345
- getAttribute(qualifiedName) {
346
- if (qualifiedName === "id") {
347
- return this.id || null;
348
- }
349
- return super.getAttribute(qualifiedName);
350
- }
351
- hasAttribute(qualifiedName) {
352
- if (qualifiedName === "id") {
353
- return this.id !== "";
354
- }
355
- return super.hasAttribute(qualifiedName);
356
- }
357
- setAttribute(qualifiedName, value) {
358
- if (qualifiedName === "id") {
359
- this.id = value;
360
- return;
361
- }
362
- super.setAttribute(qualifiedName, value);
363
- }
364
- removeAttribute(qualifiedName) {
365
- if (qualifiedName === "id") {
366
- this.id = "";
367
- return;
368
- }
369
- super.removeAttribute(qualifiedName);
370
- }
371
448
  willUpdate(changedProperties) {
372
449
  if (changedProperties.has("size") && !isInputSize(this.size)) {
373
450
  this.size = "md";
@@ -379,14 +456,10 @@ var SpectreInputElement = class extends LitElement {
379
456
  this.value = "";
380
457
  }
381
458
  if (changedProperties.has("maxlength")) {
382
- if (this.maxlength == null || !Number.isInteger(this.maxlength) || this.maxlength < 0) {
383
- this.maxlength = void 0;
384
- }
459
+ this.maxlength = normalizeInt(this.maxlength, void 0);
385
460
  }
386
461
  if (changedProperties.has("minlength")) {
387
- if (this.minlength == null || !Number.isInteger(this.minlength) || this.minlength < 0) {
388
- this.minlength = void 0;
389
- }
462
+ this.minlength = normalizeInt(this.minlength, void 0);
390
463
  }
391
464
  }
392
465
  get inputClasses() {
@@ -403,18 +476,6 @@ var SpectreInputElement = class extends LitElement {
403
476
  get nativeInput() {
404
477
  return this.querySelector("[data-sp-input-native]");
405
478
  }
406
- get forwardedAriaLabel() {
407
- const ariaLabel = this.ariaLabel?.trim();
408
- return ariaLabel ? ariaLabel : void 0;
409
- }
410
- get forwardedAriaLabelledBy() {
411
- const ariaLabelledBy = this.ariaLabelledBy?.trim();
412
- return ariaLabelledBy ? ariaLabelledBy : void 0;
413
- }
414
- get forwardedAriaDescribedBy() {
415
- const ariaDescribedBy = this.ariaDescribedBy?.trim();
416
- return ariaDescribedBy ? ariaDescribedBy : void 0;
417
- }
418
479
  handleInput(event) {
419
480
  const input = event.currentTarget;
420
481
  this.value = input.value;
@@ -430,62 +491,58 @@ var SpectreInputElement = class extends LitElement {
430
491
  this.nativeInput?.blur();
431
492
  }
432
493
  render() {
433
- return html`
434
- <input
435
- aria-busy=${this.loading ? "true" : "false"}
436
- aria-describedby=${ifDefined(this.forwardedAriaDescribedBy)}
437
- aria-invalid=${ifDefined(this.invalid ? "true" : void 0)}
438
- aria-label=${ifDefined(this.forwardedAriaLabel)}
439
- aria-labelledby=${ifDefined(this.forwardedAriaLabelledBy)}
440
- autocomplete=${ifDefined(this.autocomplete)}
441
- ?autofocus=${this.autofocus}
442
- class=${this.inputClasses}
443
- data-sp-input-native
444
- ?disabled=${this.isDisabled}
445
- ?readonly=${this.readonly}
446
- ?required=${this.required}
447
- id=${ifDefined(this.id || void 0)}
448
- inputmode=${ifDefined(this.inputmode)}
449
- max=${ifDefined(this.max)}
450
- maxlength=${ifDefined(this.maxlength)}
451
- min=${ifDefined(this.min)}
452
- minlength=${ifDefined(this.minlength)}
453
- name=${ifDefined(this.name)}
454
- placeholder=${ifDefined(this.placeholder)}
455
- step=${ifDefined(this.step)}
456
- title=${ifDefined(this.title || void 0)}
457
- type=${this.type}
458
- .value=${live(this.value)}
459
- @change=${this.handleChange}
460
- @input=${this.handleInput}
461
- />
462
- `;
494
+ return html`<input
495
+ aria-busy="${this.loading ? "true" : "false"}"
496
+ aria-describedby="${ifDefined(this.forwardedAriaDescribedBy)}"
497
+ aria-invalid="${ifDefined(this.invalid ? "true" : void 0)}"
498
+ aria-label="${ifDefined(this.forwardedAriaLabel)}"
499
+ aria-labelledby="${ifDefined(this.forwardedAriaLabelledBy)}"
500
+ autocomplete="${ifDefined(this.autocomplete || void 0)}"
501
+ ?autofocus="${this.autofocus}"
502
+ class="${this.inputClasses}"
503
+ data-sp-input-native
504
+ ?disabled="${this.isDisabled}"
505
+ form="${ifDefined(this.form || void 0)}"
506
+ ?readonly="${this.readonly}"
507
+ ?required="${this.required}"
508
+ id="${ifDefined(this.id || void 0)}"
509
+ inputmode="${ifDefined(this.inputmode || void 0)}"
510
+ max="${ifDefined(this.max || void 0)}"
511
+ maxlength="${ifDefined(this.maxlength)}"
512
+ min="${ifDefined(this.min || void 0)}"
513
+ minlength="${ifDefined(this.minlength)}"
514
+ name="${ifDefined(this.name || void 0)}"
515
+ placeholder="${ifDefined(this.placeholder || void 0)}"
516
+ step="${ifDefined(this.step || void 0)}"
517
+ title="${ifDefined(this.title || void 0)}"
518
+ type="${this.type}"
519
+ .value="${live(this.value)}"
520
+ @change="${this.handleChange}"
521
+ @input="${this.handleInput}"
522
+ />`;
463
523
  }
464
524
  };
465
525
  SpectreInputElement.properties = {
466
- ariaLabel: { attribute: "aria-label", type: String },
467
- ariaLabelledBy: { attribute: "aria-labelledby", type: String },
468
- ariaDescribedBy: { attribute: "aria-describedby", type: String },
469
- autocomplete: { type: String },
526
+ autocomplete: { type: String, reflect: true },
470
527
  autofocus: { type: Boolean, reflect: true },
471
528
  disabled: { type: Boolean, reflect: true },
529
+ form: { type: String },
472
530
  fullWidth: { attribute: "full-width", type: Boolean, reflect: true },
473
- inputmode: { type: String },
531
+ inputmode: { type: String, reflect: true },
474
532
  invalid: { type: Boolean, reflect: true },
475
533
  loading: { type: Boolean, reflect: true },
476
- max: { type: String },
477
- maxlength: { type: Number },
478
- min: { type: String },
479
- minlength: { type: Number },
480
- name: { type: String },
534
+ max: { type: String, reflect: true },
535
+ maxlength: { type: Number, reflect: true },
536
+ min: { type: String, reflect: true },
537
+ minlength: { type: Number, reflect: true },
538
+ name: { type: String, reflect: true },
481
539
  pill: { type: Boolean, reflect: true },
482
- placeholder: { type: String },
540
+ placeholder: { type: String, reflect: true },
483
541
  readonly: { type: Boolean, reflect: true },
484
542
  required: { type: Boolean, reflect: true },
485
543
  size: { type: String, reflect: true },
486
- step: { type: String },
544
+ step: { type: String, reflect: true },
487
545
  success: { type: Boolean, reflect: true },
488
- title: { type: String, reflect: true },
489
546
  type: { type: String, reflect: true },
490
547
  value: { type: String }
491
548
  };
@@ -498,12 +555,9 @@ function defineSpectreInput(tagName = "sp-input") {
498
555
  return SpectreInputElement;
499
556
  }
500
557
  var DEFAULT_ROWS = 2;
501
- var SpectreTextareaElement = class extends LitElement {
558
+ var SpectreTextareaElement = class extends SpectreBaseElement {
502
559
  constructor() {
503
560
  super(...arguments);
504
- this.ariaLabel = null;
505
- this.ariaLabelledBy = null;
506
- this.ariaDescribedBy = null;
507
561
  this.autofocus = false;
508
562
  this.disabled = false;
509
563
  this.fullWidth = false;
@@ -515,80 +569,23 @@ var SpectreTextareaElement = class extends LitElement {
515
569
  this.rows = DEFAULT_ROWS;
516
570
  this.size = "md";
517
571
  this.success = false;
518
- this.title = "";
519
572
  this.value = "";
520
573
  }
521
- get id() {
522
- return this._id ?? "";
523
- }
524
- set id(value) {
525
- if ((this._id ?? "") === value) {
526
- return;
527
- }
528
- this._id = value;
529
- const host = this;
530
- if (HTMLElement.prototype.hasAttribute.call(host, "id")) {
531
- HTMLElement.prototype.removeAttribute.call(host, "id");
532
- }
533
- this.requestUpdate();
534
- }
535
- createRenderRoot() {
536
- return this;
537
- }
538
- connectedCallback() {
539
- super.connectedCallback();
540
- const hostId = super.getAttribute("id");
541
- if (hostId !== null) {
542
- this.id = hostId;
543
- }
544
- }
545
- getAttribute(qualifiedName) {
546
- if (qualifiedName === "id") {
547
- return this.id || null;
548
- }
549
- return super.getAttribute(qualifiedName);
550
- }
551
- hasAttribute(qualifiedName) {
552
- if (qualifiedName === "id") {
553
- return this.id !== "";
554
- }
555
- return super.hasAttribute(qualifiedName);
556
- }
557
- setAttribute(qualifiedName, value) {
558
- if (qualifiedName === "id") {
559
- this.id = value;
560
- return;
561
- }
562
- super.setAttribute(qualifiedName, value);
563
- }
564
- removeAttribute(qualifiedName) {
565
- if (qualifiedName === "id") {
566
- this.id = "";
567
- return;
568
- }
569
- super.removeAttribute(qualifiedName);
570
- }
571
574
  willUpdate(changedProperties) {
572
575
  if (changedProperties.has("size") && !isInputSize(this.size)) {
573
576
  this.size = "md";
574
577
  }
575
578
  if (changedProperties.has("rows")) {
576
- if (this.rows == null || !Number.isInteger(this.rows) || this.rows < 1) {
577
- this.rows = DEFAULT_ROWS;
578
- }
579
+ this.rows = normalizeInt(this.rows, DEFAULT_ROWS, 1);
579
580
  }
580
581
  if (changedProperties.has("value") && this.value == null) {
581
582
  this.value = "";
582
583
  }
583
584
  if (changedProperties.has("maxlength")) {
584
- if (this.maxlength == null || !Number.isInteger(this.maxlength) || this.maxlength < 0) {
585
- this.maxlength = void 0;
586
- }
585
+ this.maxlength = normalizeInt(this.maxlength, void 0);
587
586
  }
588
587
  if (changedProperties.has("minlength")) {
589
- if (this.minlength == null || !Number.isInteger(this.minlength) || this.minlength < 0) {
590
- this.minlength = void 0;
591
- }
588
+ this.minlength = normalizeInt(this.minlength, void 0);
592
589
  }
593
590
  }
594
591
  get textareaClasses() {
@@ -605,18 +602,6 @@ var SpectreTextareaElement = class extends LitElement {
605
602
  get nativeTextarea() {
606
603
  return this.querySelector("[data-sp-textarea-native]");
607
604
  }
608
- get forwardedAriaLabel() {
609
- const ariaLabel = this.ariaLabel?.trim();
610
- return ariaLabel ? ariaLabel : void 0;
611
- }
612
- get forwardedAriaLabelledBy() {
613
- const ariaLabelledBy = this.ariaLabelledBy?.trim();
614
- return ariaLabelledBy ? ariaLabelledBy : void 0;
615
- }
616
- get forwardedAriaDescribedBy() {
617
- const ariaDescribedBy = this.ariaDescribedBy?.trim();
618
- return ariaDescribedBy ? ariaDescribedBy : void 0;
619
- }
620
605
  handleInput(event) {
621
606
  const textarea = event.currentTarget;
622
607
  this.value = textarea.value;
@@ -632,57 +617,53 @@ var SpectreTextareaElement = class extends LitElement {
632
617
  this.nativeTextarea?.blur();
633
618
  }
634
619
  render() {
635
- return html`
636
- <textarea
637
- aria-busy=${this.loading ? "true" : "false"}
638
- aria-describedby=${ifDefined(this.forwardedAriaDescribedBy)}
639
- aria-invalid=${ifDefined(this.invalid ? "true" : void 0)}
640
- aria-label=${ifDefined(this.forwardedAriaLabel)}
641
- aria-labelledby=${ifDefined(this.forwardedAriaLabelledBy)}
642
- autocomplete=${ifDefined(this.autocomplete)}
643
- ?autofocus=${this.autofocus}
644
- class=${this.textareaClasses}
645
- data-sp-textarea-native
646
- ?disabled=${this.isDisabled}
647
- inputmode=${ifDefined(this.inputmode)}
648
- ?readonly=${this.readonly}
649
- ?required=${this.required}
650
- id=${ifDefined(this.id || void 0)}
651
- maxlength=${ifDefined(this.maxlength)}
652
- minlength=${ifDefined(this.minlength)}
653
- name=${ifDefined(this.name)}
654
- placeholder=${ifDefined(this.placeholder)}
655
- rows=${this.rows}
656
- title=${ifDefined(this.title || void 0)}
657
- .value=${live(this.value)}
658
- @change=${this.handleChange}
659
- @input=${this.handleInput}
660
- ></textarea>
661
- `;
620
+ return html`<textarea
621
+ aria-busy="${this.loading ? "true" : "false"}"
622
+ aria-describedby="${ifDefined(this.forwardedAriaDescribedBy)}"
623
+ aria-invalid="${ifDefined(this.invalid ? "true" : void 0)}"
624
+ aria-label="${ifDefined(this.forwardedAriaLabel)}"
625
+ aria-labelledby="${ifDefined(this.forwardedAriaLabelledBy)}"
626
+ autocomplete="${ifDefined(this.autocomplete || void 0)}"
627
+ ?autofocus="${this.autofocus}"
628
+ class="${this.textareaClasses}"
629
+ data-sp-textarea-native
630
+ ?disabled="${this.isDisabled}"
631
+ form="${ifDefined(this.form || void 0)}"
632
+ inputmode="${ifDefined(this.inputmode || void 0)}"
633
+ ?readonly="${this.readonly}"
634
+ ?required="${this.required}"
635
+ id="${ifDefined(this.id || void 0)}"
636
+ maxlength="${ifDefined(this.maxlength)}"
637
+ minlength="${ifDefined(this.minlength)}"
638
+ name="${ifDefined(this.name || void 0)}"
639
+ placeholder="${ifDefined(this.placeholder || void 0)}"
640
+ rows="${this.rows}"
641
+ title="${ifDefined(this.title || void 0)}"
642
+ .value="${live(this.value)}"
643
+ @change="${this.handleChange}"
644
+ @input="${this.handleInput}"
645
+ ></textarea>`;
662
646
  }
663
647
  };
664
648
  SpectreTextareaElement.properties = {
665
- ariaLabel: { attribute: "aria-label", type: String },
666
- ariaLabelledBy: { attribute: "aria-labelledby", type: String },
667
- ariaDescribedBy: { attribute: "aria-describedby", type: String },
668
- autocomplete: { type: String },
649
+ autocomplete: { type: String, reflect: true },
669
650
  autofocus: { type: Boolean, reflect: true },
670
651
  disabled: { type: Boolean, reflect: true },
652
+ form: { type: String },
671
653
  fullWidth: { attribute: "full-width", type: Boolean, reflect: true },
672
- inputmode: { type: String },
654
+ inputmode: { type: String, reflect: true },
673
655
  invalid: { type: Boolean, reflect: true },
674
656
  loading: { type: Boolean, reflect: true },
675
- maxlength: { type: Number },
676
- minlength: { type: Number },
677
- name: { type: String },
657
+ maxlength: { type: Number, reflect: true },
658
+ minlength: { type: Number, reflect: true },
659
+ name: { type: String, reflect: true },
678
660
  pill: { type: Boolean, reflect: true },
679
- placeholder: { type: String },
661
+ placeholder: { type: String, reflect: true },
680
662
  readonly: { type: Boolean, reflect: true },
681
663
  required: { type: Boolean, reflect: true },
682
664
  rows: { type: Number },
683
665
  size: { type: String, reflect: true },
684
666
  success: { type: Boolean, reflect: true },
685
- title: { type: String, reflect: true },
686
667
  value: { type: String }
687
668
  };
688
669
  function defineSpectreTextarea(tagName = "sp-textarea") {
@@ -693,26 +674,9 @@ function defineSpectreTextarea(tagName = "sp-textarea") {
693
674
  customElements.define(tagName, SpectreTextareaElement);
694
675
  return SpectreTextareaElement;
695
676
  }
696
- var spectreSelectSizes = ["sm", "md", "lg"];
697
- function isSelectSize(value) {
698
- return spectreSelectSizes.includes(value);
699
- }
700
- function isSelectableContent(node) {
701
- if (node.nodeType === Node.ELEMENT_NODE) {
702
- const tagName = node.tagName;
703
- return tagName === "OPTION" || tagName === "OPTGROUP";
704
- }
705
- if (node.nodeType === Node.TEXT_NODE) {
706
- return (node.textContent?.trim().length ?? 0) > 0;
707
- }
708
- return false;
709
- }
710
- var SpectreSelectElement = class extends LitElement {
677
+ var SpectreSelectElement = class extends SpectreProjectableElement {
711
678
  constructor() {
712
679
  super(...arguments);
713
- this.ariaLabel = null;
714
- this.ariaLabelledBy = null;
715
- this.ariaDescribedBy = null;
716
680
  this.autofocus = false;
717
681
  this.disabled = false;
718
682
  this.fullWidth = false;
@@ -722,93 +686,43 @@ var SpectreSelectElement = class extends LitElement {
722
686
  this.required = false;
723
687
  this.size = "md";
724
688
  this.success = false;
725
- this.title = "";
726
689
  this.value = "";
727
- this.projectedOptions = [];
728
- }
729
- get id() {
730
- return this._id ?? "";
731
690
  }
732
- set id(value) {
733
- if ((this._id ?? "") === value) {
734
- return;
735
- }
736
- this._id = value;
737
- const host = this;
738
- if (HTMLElement.prototype.hasAttribute.call(host, "id")) {
739
- HTMLElement.prototype.removeAttribute.call(host, "id");
740
- }
741
- this.requestUpdate();
742
- }
743
- createRenderRoot() {
744
- return this;
745
- }
746
- connectedCallback() {
747
- super.connectedCallback();
748
- const hostId = super.getAttribute("id");
749
- if (hostId !== null) {
750
- this.id = hostId;
751
- }
752
- this.syncProjectedOptions();
753
- this.startContentObserver();
754
- }
755
- disconnectedCallback() {
756
- this.stopContentObserver();
757
- super.disconnectedCallback();
758
- }
759
- getAttribute(qualifiedName) {
760
- if (qualifiedName === "id") {
761
- return this.id || null;
762
- }
763
- return super.getAttribute(qualifiedName);
764
- }
765
- hasAttribute(qualifiedName) {
766
- if (qualifiedName === "id") {
767
- return this.id !== "";
768
- }
769
- return super.hasAttribute(qualifiedName);
770
- }
771
- setAttribute(qualifiedName, value) {
772
- if (qualifiedName === "id") {
773
- this.id = value;
774
- return;
775
- }
776
- super.setAttribute(qualifiedName, value);
691
+ getContentContainer() {
692
+ return this.querySelector("[data-sp-select-native]");
777
693
  }
778
- removeAttribute(qualifiedName) {
779
- if (qualifiedName === "id") {
780
- this.id = "";
781
- return;
694
+ isInternalNode(node) {
695
+ if (node.nodeType !== Node.ELEMENT_NODE) {
696
+ return false;
782
697
  }
783
- super.removeAttribute(qualifiedName);
698
+ const el = node;
699
+ return el.hasAttribute("data-sp-select-native");
784
700
  }
785
701
  willUpdate(changedProperties) {
786
- if (changedProperties.has("size") && !isSelectSize(this.size)) {
702
+ if (changedProperties.has("size") && !isInputSize(this.size)) {
787
703
  this.size = "md";
788
704
  }
789
705
  if (changedProperties.has("value") && this.value == null) {
790
706
  this.value = "";
791
707
  }
792
708
  }
793
- update(changedProperties) {
794
- this.stopContentObserver();
795
- super.update(changedProperties);
796
- this.startContentObserver();
797
- }
798
709
  updated(changedProperties) {
799
710
  super.updated(changedProperties);
800
- const nativeSelect = this.nativeSelect;
711
+ const nativeSelect = this.getContentContainer();
801
712
  if (!nativeSelect) {
802
713
  return;
803
714
  }
804
715
  if (this.value !== "" && nativeSelect.value !== this.value) {
805
716
  nativeSelect.value = this.value;
806
- return;
807
717
  }
808
718
  if (this.value === "" && !this.hasAttribute("value")) {
809
719
  const nativeValue = nativeSelect.value ?? "";
810
- if (nativeValue !== "") {
811
- this.value = nativeValue;
720
+ if (nativeValue !== "" && nativeValue !== this.value) {
721
+ this.updateComplete.then(() => {
722
+ if (this.value === "" && !this.hasAttribute("value")) {
723
+ this.value = nativeValue;
724
+ }
725
+ });
812
726
  }
813
727
  }
814
728
  }
@@ -817,75 +731,11 @@ var SpectreSelectElement = class extends LitElement {
817
731
  fullWidth: this.fullWidth,
818
732
  pill: this.pill,
819
733
  size: this.size,
820
- state: this.isDisabled ? this.disabled ? "disabled" : "loading" : this.invalid ? "error" : this.success ? "success" : "default"
821
- });
822
- }
823
- get isDisabled() {
824
- return this.disabled || this.loading;
825
- }
826
- get nativeSelect() {
827
- return this.querySelector("[data-sp-select-native]");
828
- }
829
- get forwardedAriaLabel() {
830
- const ariaLabel = this.ariaLabel?.trim();
831
- return ariaLabel ? ariaLabel : void 0;
832
- }
833
- get forwardedAriaLabelledBy() {
834
- const ariaLabelledBy = this.ariaLabelledBy?.trim();
835
- return ariaLabelledBy ? ariaLabelledBy : void 0;
836
- }
837
- get forwardedAriaDescribedBy() {
838
- const ariaDescribedBy = this.ariaDescribedBy?.trim();
839
- return ariaDescribedBy ? ariaDescribedBy : void 0;
840
- }
841
- startContentObserver() {
842
- if (this.contentObserver) {
843
- return;
844
- }
845
- this.contentObserver = new MutationObserver((mutations) => {
846
- const isInternalMovement = mutations.every((mutation) => {
847
- return Array.from(mutation.removedNodes).every(
848
- (node) => this.isInternalSelectNode(node) || this.contains(node)
849
- ) && Array.from(mutation.addedNodes).every((node) => this.isInternalSelectNode(node));
850
- });
851
- if (isInternalMovement) {
852
- return;
853
- }
854
- if (this.syncProjectedOptions()) {
855
- this.requestUpdate();
856
- }
857
- });
858
- this.contentObserver.observe(this, {
859
- childList: true
860
- });
861
- }
862
- stopContentObserver() {
863
- this.contentObserver?.disconnect();
864
- this.contentObserver = void 0;
865
- }
866
- syncProjectedOptions() {
867
- const nextProjectedOptions = [];
868
- Array.from(this.childNodes).forEach((node) => {
869
- if (this.isInternalSelectNode(node)) {
870
- this.projectedOptions.forEach((projectedNode) => {
871
- if (projectedNode.parentNode !== this && this.contains(projectedNode)) {
872
- nextProjectedOptions.push(projectedNode);
873
- }
874
- });
875
- return;
876
- }
877
- if (isSelectableContent(node)) {
878
- nextProjectedOptions.push(node);
879
- }
880
- });
881
- const hasChanged = nextProjectedOptions.length !== this.projectedOptions.length || nextProjectedOptions.some((node, index) => node !== this.projectedOptions[index]);
882
- if (hasChanged) {
883
- this.projectedOptions = nextProjectedOptions;
884
- }
885
- return hasChanged;
734
+ state: this.isDisabled ? this.disabled ? "disabled" : "loading" : this.invalid ? "error" : this.success ? "success" : "default"
735
+ });
886
736
  }
887
- isInternalSelectNode(node) {
888
- return node.nodeType === Node.ELEMENT_NODE && node.hasAttribute("data-sp-select-native");
737
+ get isDisabled() {
738
+ return this.disabled || this.loading;
889
739
  }
890
740
  handleInput(event) {
891
741
  const select = event.currentTarget;
@@ -896,51 +746,49 @@ var SpectreSelectElement = class extends LitElement {
896
746
  this.value = select.value;
897
747
  }
898
748
  focus(options) {
899
- this.nativeSelect?.focus(options);
749
+ this.getContentContainer()?.focus(options);
900
750
  }
901
751
  blur() {
902
- this.nativeSelect?.blur();
752
+ this.getContentContainer()?.blur();
903
753
  }
904
754
  render() {
905
- return html`
906
- <select
907
- aria-busy=${this.loading ? "true" : "false"}
908
- aria-describedby=${ifDefined(this.forwardedAriaDescribedBy)}
909
- aria-invalid=${ifDefined(this.invalid ? "true" : void 0)}
910
- aria-label=${ifDefined(this.forwardedAriaLabel)}
911
- aria-labelledby=${ifDefined(this.forwardedAriaLabelledBy)}
912
- ?autofocus=${this.autofocus}
913
- class=${this.selectClasses}
914
- data-sp-select-native
915
- ?disabled=${this.isDisabled}
916
- id=${ifDefined(this.id || void 0)}
917
- name=${ifDefined(this.name)}
918
- ?required=${this.required}
919
- title=${ifDefined(this.title || void 0)}
920
- .value=${live(this.value)}
921
- @change=${this.handleChange}
922
- @input=${this.handleInput}
923
- >
924
- ${this.projectedOptions.length > 0 ? this.projectedOptions : nothing}
925
- </select>
926
- `;
755
+ return html`<select
756
+ aria-busy="${this.loading ? "true" : "false"}"
757
+ aria-describedby="${ifDefined(this.forwardedAriaDescribedBy)}"
758
+ aria-invalid="${ifDefined(this.invalid ? "true" : void 0)}"
759
+ aria-label="${ifDefined(this.forwardedAriaLabel)}"
760
+ aria-labelledby="${ifDefined(this.forwardedAriaLabelledBy)}"
761
+ autocomplete="${ifDefined(this.autocomplete || void 0)}"
762
+ ?autofocus="${this.autofocus}"
763
+ class="${this.selectClasses}"
764
+ data-sp-select-native
765
+ ?disabled="${this.isDisabled}"
766
+ form="${ifDefined(this.form || void 0)}"
767
+ id="${ifDefined(this.id || void 0)}"
768
+ name="${ifDefined(this.name || void 0)}"
769
+ ?required="${this.required}"
770
+ title="${ifDefined(this.title || void 0)}"
771
+ .value="${live(this.value)}"
772
+ @change="${this.handleChange}"
773
+ @input="${this.handleInput}"
774
+ >
775
+ ${this.hasProjectedContent ? this.projectedContent : nothing}
776
+ </select>`;
927
777
  }
928
778
  };
929
779
  SpectreSelectElement.properties = {
930
- ariaLabel: { attribute: "aria-label", type: String },
931
- ariaLabelledBy: { attribute: "aria-labelledby", type: String },
932
- ariaDescribedBy: { attribute: "aria-describedby", type: String },
780
+ autocomplete: { type: String, reflect: true },
933
781
  autofocus: { type: Boolean, reflect: true },
934
782
  disabled: { type: Boolean, reflect: true },
783
+ form: { type: String },
935
784
  fullWidth: { attribute: "full-width", type: Boolean, reflect: true },
936
785
  invalid: { type: Boolean, reflect: true },
937
786
  loading: { type: Boolean, reflect: true },
938
- name: { type: String },
787
+ name: { type: String, reflect: true },
939
788
  pill: { type: Boolean, reflect: true },
940
789
  required: { type: Boolean, reflect: true },
941
790
  size: { type: String, reflect: true },
942
791
  success: { type: Boolean, reflect: true },
943
- title: { type: String, reflect: true },
944
792
  value: { type: String }
945
793
  };
946
794
  function defineSpectreSelect(tagName = "sp-select") {
@@ -951,70 +799,27 @@ function defineSpectreSelect(tagName = "sp-select") {
951
799
  customElements.define(tagName, SpectreSelectElement);
952
800
  return SpectreSelectElement;
953
801
  }
954
- var SpectreCheckboxElement = class extends LitElement {
802
+ var SpectreCheckboxElement = class extends SpectreProjectableElement {
955
803
  constructor() {
956
804
  super(...arguments);
957
- this.ariaLabel = null;
958
- this.ariaLabelledBy = null;
959
- this.ariaDescribedBy = null;
960
805
  this.autofocus = false;
961
806
  this.checked = false;
962
807
  this.disabled = false;
963
808
  this.invalid = false;
964
- this.label = "";
809
+ this.loading = false;
965
810
  this.required = false;
966
- this.title = "";
811
+ this.success = false;
967
812
  this.value = "on";
968
813
  }
969
- get id() {
970
- return this._id ?? "";
971
- }
972
- set id(value) {
973
- if ((this._id ?? "") === value) {
974
- return;
975
- }
976
- this._id = value;
977
- const host = this;
978
- if (HTMLElement.prototype.hasAttribute.call(host, "id")) {
979
- HTMLElement.prototype.removeAttribute.call(host, "id");
980
- }
981
- this.requestUpdate();
982
- }
983
- createRenderRoot() {
984
- return this;
985
- }
986
- connectedCallback() {
987
- super.connectedCallback();
988
- const hostId = super.getAttribute("id");
989
- if (hostId !== null) {
990
- this.id = hostId;
991
- }
992
- }
993
- getAttribute(qualifiedName) {
994
- if (qualifiedName === "id") {
995
- return this.id || null;
996
- }
997
- return super.getAttribute(qualifiedName);
998
- }
999
- hasAttribute(qualifiedName) {
1000
- if (qualifiedName === "id") {
1001
- return this.id !== "";
1002
- }
1003
- return super.hasAttribute(qualifiedName);
1004
- }
1005
- setAttribute(qualifiedName, value) {
1006
- if (qualifiedName === "id") {
1007
- this.id = value;
1008
- return;
1009
- }
1010
- super.setAttribute(qualifiedName, value);
814
+ getContentContainer() {
815
+ return this.querySelector("[data-sp-checkbox-label]");
1011
816
  }
1012
- removeAttribute(qualifiedName) {
1013
- if (qualifiedName === "id") {
1014
- this.id = "";
1015
- return;
817
+ isInternalNode(node) {
818
+ if (node.nodeType !== Node.ELEMENT_NODE) {
819
+ return false;
1016
820
  }
1017
- super.removeAttribute(qualifiedName);
821
+ const el = node;
822
+ return el.hasAttribute("data-sp-checkbox-label") || el.hasAttribute("data-sp-checkbox-native") || el.hasAttribute("data-sp-checkbox-label-fallback") || el.hasAttribute("data-sp-checkbox-indicator");
1018
823
  }
1019
824
  willUpdate(changedProperties) {
1020
825
  if (changedProperties.has("value") && this.value == null) {
@@ -1024,17 +829,12 @@ var SpectreCheckboxElement = class extends LitElement {
1024
829
  get nativeInput() {
1025
830
  return this.querySelector("[data-sp-checkbox-native]");
1026
831
  }
1027
- get forwardedAriaLabel() {
1028
- const value = this.ariaLabel?.trim();
1029
- return value ? value : void 0;
1030
- }
1031
- get forwardedAriaLabelledBy() {
1032
- const value = this.ariaLabelledBy?.trim();
1033
- return value ? value : void 0;
832
+ get isDisabled() {
833
+ return this.disabled || this.loading;
1034
834
  }
1035
- get forwardedAriaDescribedBy() {
1036
- const value = this.ariaDescribedBy?.trim();
1037
- return value ? value : void 0;
835
+ get visibleLabelFallback() {
836
+ const trimmedLabel = this.label?.trim();
837
+ return trimmedLabel ? trimmedLabel : void 0;
1038
838
  }
1039
839
  handleInput(event) {
1040
840
  const input = event.currentTarget;
@@ -1051,43 +851,44 @@ var SpectreCheckboxElement = class extends LitElement {
1051
851
  this.nativeInput?.blur();
1052
852
  }
1053
853
  render() {
1054
- return html`
1055
- <label>
1056
- <input
1057
- aria-describedby=${ifDefined(this.forwardedAriaDescribedBy)}
1058
- aria-invalid=${ifDefined(this.invalid ? "true" : void 0)}
1059
- aria-label=${ifDefined(this.forwardedAriaLabel)}
1060
- aria-labelledby=${ifDefined(this.forwardedAriaLabelledBy)}
1061
- ?autofocus=${this.autofocus}
1062
- data-sp-checkbox-native
1063
- ?checked=${this.checked}
1064
- ?disabled=${this.disabled}
1065
- id=${ifDefined(this.id || void 0)}
1066
- name=${ifDefined(this.name)}
1067
- ?required=${this.required}
1068
- title=${ifDefined(this.title || void 0)}
1069
- type='checkbox'
1070
- value=${ifDefined(this.value || void 0)}
1071
- @change=${this.handleChange}
1072
- @input=${this.handleInput}
1073
- />
1074
- <span class='sp-label'>${this.label}</span>
1075
- </label>
1076
- `;
854
+ const labelContent = this.hasProjectedContent ? this.projectedContent : this.visibleLabelFallback ? html`<span class="sp-label" data-sp-checkbox-label-fallback>${this.visibleLabelFallback}</span>` : nothing;
855
+ return html`<label data-sp-checkbox-label>
856
+ <input
857
+ aria-busy="${this.loading ? "true" : "false"}"
858
+ aria-describedby="${ifDefined(this.forwardedAriaDescribedBy)}"
859
+ aria-invalid="${ifDefined(this.invalid ? "true" : void 0)}"
860
+ aria-label="${ifDefined(this.forwardedAriaLabel)}"
861
+ aria-labelledby="${ifDefined(this.forwardedAriaLabelledBy)}"
862
+ ?autofocus="${this.autofocus}"
863
+ data-sp-checkbox-native
864
+ .checked="${live(this.checked)}"
865
+ ?disabled="${this.isDisabled}"
866
+ form="${ifDefined(this.form || void 0)}"
867
+ id="${ifDefined(this.id || void 0)}"
868
+ name="${ifDefined(this.name || void 0)}"
869
+ ?required="${this.required}"
870
+ title="${ifDefined(this.title || void 0)}"
871
+ type="checkbox"
872
+ value="${ifDefined(this.value)}"
873
+ @change="${this.handleChange}"
874
+ @input="${this.handleInput}"
875
+ />
876
+ <span class="sp-checkbox-indicator" data-sp-checkbox-indicator></span>
877
+ ${labelContent}
878
+ </label>`;
1077
879
  }
1078
880
  };
1079
881
  SpectreCheckboxElement.properties = {
1080
- ariaLabel: { attribute: "aria-label", type: String },
1081
- ariaLabelledBy: { attribute: "aria-labelledby", type: String },
1082
- ariaDescribedBy: { attribute: "aria-describedby", type: String },
1083
882
  autofocus: { type: Boolean, reflect: true },
1084
883
  checked: { type: Boolean, reflect: true },
1085
884
  disabled: { type: Boolean, reflect: true },
885
+ form: { type: String },
1086
886
  invalid: { type: Boolean, reflect: true },
887
+ loading: { type: Boolean, reflect: true },
1087
888
  label: { type: String, reflect: true },
1088
- name: { type: String },
889
+ name: { type: String, reflect: true },
1089
890
  required: { type: Boolean, reflect: true },
1090
- title: { type: String, reflect: true },
891
+ success: { type: Boolean, reflect: true },
1091
892
  value: { type: String }
1092
893
  };
1093
894
  function defineSpectreCheckbox(tagName = "sp-checkbox") {
@@ -1098,70 +899,27 @@ function defineSpectreCheckbox(tagName = "sp-checkbox") {
1098
899
  customElements.define(tagName, SpectreCheckboxElement);
1099
900
  return SpectreCheckboxElement;
1100
901
  }
1101
- var SpectreRadioElement = class extends LitElement {
902
+ var SpectreRadioElement = class extends SpectreProjectableElement {
1102
903
  constructor() {
1103
904
  super(...arguments);
1104
- this.ariaLabel = null;
1105
- this.ariaLabelledBy = null;
1106
- this.ariaDescribedBy = null;
1107
905
  this.autofocus = false;
1108
906
  this.checked = false;
1109
907
  this.disabled = false;
1110
908
  this.invalid = false;
1111
- this.label = "";
909
+ this.loading = false;
1112
910
  this.required = false;
1113
- this.title = "";
911
+ this.success = false;
1114
912
  this.value = "on";
1115
913
  }
1116
- get id() {
1117
- return this._id ?? "";
1118
- }
1119
- set id(value) {
1120
- if ((this._id ?? "") === value) {
1121
- return;
1122
- }
1123
- this._id = value;
1124
- const host = this;
1125
- if (HTMLElement.prototype.hasAttribute.call(host, "id")) {
1126
- HTMLElement.prototype.removeAttribute.call(host, "id");
1127
- }
1128
- this.requestUpdate();
1129
- }
1130
- createRenderRoot() {
1131
- return this;
1132
- }
1133
- connectedCallback() {
1134
- super.connectedCallback();
1135
- const hostId = super.getAttribute("id");
1136
- if (hostId !== null) {
1137
- this.id = hostId;
1138
- }
1139
- }
1140
- getAttribute(qualifiedName) {
1141
- if (qualifiedName === "id") {
1142
- return this.id || null;
1143
- }
1144
- return super.getAttribute(qualifiedName);
1145
- }
1146
- hasAttribute(qualifiedName) {
1147
- if (qualifiedName === "id") {
1148
- return this.id !== "";
1149
- }
1150
- return super.hasAttribute(qualifiedName);
1151
- }
1152
- setAttribute(qualifiedName, value) {
1153
- if (qualifiedName === "id") {
1154
- this.id = value;
1155
- return;
1156
- }
1157
- super.setAttribute(qualifiedName, value);
914
+ getContentContainer() {
915
+ return this.querySelector("[data-sp-radio-label]");
1158
916
  }
1159
- removeAttribute(qualifiedName) {
1160
- if (qualifiedName === "id") {
1161
- this.id = "";
1162
- return;
917
+ isInternalNode(node) {
918
+ if (node.nodeType !== Node.ELEMENT_NODE) {
919
+ return false;
1163
920
  }
1164
- super.removeAttribute(qualifiedName);
921
+ const el = node;
922
+ return el.hasAttribute("data-sp-radio-label") || el.hasAttribute("data-sp-radio-native") || el.hasAttribute("data-sp-radio-label-fallback") || el.hasAttribute("data-sp-radio-indicator");
1165
923
  }
1166
924
  willUpdate(changedProperties) {
1167
925
  if (changedProperties.has("value") && this.value == null) {
@@ -1171,17 +929,12 @@ var SpectreRadioElement = class extends LitElement {
1171
929
  get nativeInput() {
1172
930
  return this.querySelector("[data-sp-radio-native]");
1173
931
  }
1174
- get forwardedAriaLabel() {
1175
- const value = this.ariaLabel?.trim();
1176
- return value ? value : void 0;
1177
- }
1178
- get forwardedAriaLabelledBy() {
1179
- const value = this.ariaLabelledBy?.trim();
1180
- return value ? value : void 0;
932
+ get isDisabled() {
933
+ return this.disabled || this.loading;
1181
934
  }
1182
- get forwardedAriaDescribedBy() {
1183
- const value = this.ariaDescribedBy?.trim();
1184
- return value ? value : void 0;
935
+ get visibleLabelFallback() {
936
+ const trimmedLabel = this.label?.trim();
937
+ return trimmedLabel ? trimmedLabel : void 0;
1185
938
  }
1186
939
  handleInput(event) {
1187
940
  const input = event.currentTarget;
@@ -1198,43 +951,44 @@ var SpectreRadioElement = class extends LitElement {
1198
951
  this.nativeInput?.blur();
1199
952
  }
1200
953
  render() {
1201
- return html`
1202
- <label>
1203
- <input
1204
- aria-describedby=${ifDefined(this.forwardedAriaDescribedBy)}
1205
- aria-invalid=${ifDefined(this.invalid ? "true" : void 0)}
1206
- aria-label=${ifDefined(this.forwardedAriaLabel)}
1207
- aria-labelledby=${ifDefined(this.forwardedAriaLabelledBy)}
1208
- ?autofocus=${this.autofocus}
1209
- data-sp-radio-native
1210
- ?checked=${this.checked}
1211
- ?disabled=${this.disabled}
1212
- id=${ifDefined(this.id || void 0)}
1213
- name=${ifDefined(this.name)}
1214
- ?required=${this.required}
1215
- title=${ifDefined(this.title || void 0)}
1216
- type='radio'
1217
- value=${ifDefined(this.value || void 0)}
1218
- @change=${this.handleChange}
1219
- @input=${this.handleInput}
1220
- />
1221
- <span class='sp-label'>${this.label}</span>
1222
- </label>
1223
- `;
954
+ const labelContent = this.hasProjectedContent ? this.projectedContent : this.visibleLabelFallback ? html`<span class="sp-label" data-sp-radio-label-fallback>${this.visibleLabelFallback}</span>` : nothing;
955
+ return html`<label data-sp-radio-label>
956
+ <input
957
+ aria-busy="${this.loading ? "true" : "false"}"
958
+ aria-describedby="${ifDefined(this.forwardedAriaDescribedBy)}"
959
+ aria-invalid="${ifDefined(this.invalid ? "true" : void 0)}"
960
+ aria-label="${ifDefined(this.forwardedAriaLabel)}"
961
+ aria-labelledby="${ifDefined(this.forwardedAriaLabelledBy)}"
962
+ ?autofocus="${this.autofocus}"
963
+ data-sp-radio-native
964
+ .checked="${live(this.checked)}"
965
+ ?disabled="${this.isDisabled}"
966
+ form="${ifDefined(this.form || void 0)}"
967
+ id="${ifDefined(this.id || void 0)}"
968
+ name="${ifDefined(this.name || void 0)}"
969
+ ?required="${this.required}"
970
+ title="${ifDefined(this.title || void 0)}"
971
+ type="radio"
972
+ value="${ifDefined(this.value)}"
973
+ @change="${this.handleChange}"
974
+ @input="${this.handleInput}"
975
+ />
976
+ <span class="sp-radio-indicator" data-sp-radio-indicator></span>
977
+ ${labelContent}
978
+ </label>`;
1224
979
  }
1225
980
  };
1226
981
  SpectreRadioElement.properties = {
1227
- ariaLabel: { attribute: "aria-label", type: String },
1228
- ariaLabelledBy: { attribute: "aria-labelledby", type: String },
1229
- ariaDescribedBy: { attribute: "aria-describedby", type: String },
1230
982
  autofocus: { type: Boolean, reflect: true },
1231
983
  checked: { type: Boolean, reflect: true },
1232
984
  disabled: { type: Boolean, reflect: true },
985
+ form: { type: String },
1233
986
  invalid: { type: Boolean, reflect: true },
987
+ loading: { type: Boolean, reflect: true },
1234
988
  label: { type: String, reflect: true },
1235
- name: { type: String },
989
+ name: { type: String, reflect: true },
1236
990
  required: { type: Boolean, reflect: true },
1237
- title: { type: String, reflect: true },
991
+ success: { type: Boolean, reflect: true },
1238
992
  value: { type: String }
1239
993
  };
1240
994
  function defineSpectreRadio(tagName = "sp-radio") {
@@ -1245,142 +999,40 @@ function defineSpectreRadio(tagName = "sp-radio") {
1245
999
  customElements.define(tagName, SpectreRadioElement);
1246
1000
  return SpectreRadioElement;
1247
1001
  }
1248
- var SpectreLabelElement = class extends LitElement {
1249
- constructor() {
1250
- super(...arguments);
1251
- this.projectedContent = [];
1252
- }
1253
- get id() {
1254
- return this._id ?? "";
1255
- }
1256
- set id(value) {
1257
- if ((this._id ?? "") === value) {
1258
- return;
1259
- }
1260
- this._id = value;
1261
- const host = this;
1262
- if (HTMLElement.prototype.hasAttribute.call(host, "id")) {
1263
- HTMLElement.prototype.removeAttribute.call(host, "id");
1264
- }
1265
- this.requestUpdate();
1266
- }
1267
- createRenderRoot() {
1268
- return this;
1269
- }
1270
- connectedCallback() {
1271
- super.connectedCallback();
1272
- const hostId = super.getAttribute("id");
1273
- if (hostId !== null) {
1274
- this.id = hostId;
1275
- }
1276
- this.syncProjectedContent();
1277
- this.startContentObserver();
1278
- }
1279
- disconnectedCallback() {
1280
- this.stopContentObserver();
1281
- super.disconnectedCallback();
1282
- }
1283
- getAttribute(qualifiedName) {
1284
- if (qualifiedName === "id") {
1285
- return this.id || null;
1286
- }
1287
- return super.getAttribute(qualifiedName);
1288
- }
1289
- hasAttribute(qualifiedName) {
1290
- if (qualifiedName === "id") {
1291
- return this.id !== "";
1292
- }
1293
- return super.hasAttribute(qualifiedName);
1294
- }
1295
- setAttribute(qualifiedName, value) {
1296
- if (qualifiedName === "id") {
1297
- this.id = value;
1298
- return;
1299
- }
1300
- super.setAttribute(qualifiedName, value);
1301
- }
1302
- removeAttribute(qualifiedName) {
1303
- if (qualifiedName === "id") {
1304
- this.id = "";
1305
- return;
1306
- }
1307
- super.removeAttribute(qualifiedName);
1308
- }
1309
- update(changedProperties) {
1310
- this.stopContentObserver();
1311
- super.update(changedProperties);
1312
- this.startContentObserver();
1313
- }
1314
- get nativeLabel() {
1002
+ var SpectreLabelElement = class extends SpectreProjectableElement {
1003
+ getContentContainer() {
1315
1004
  return this.querySelector("[data-sp-label-native]");
1316
1005
  }
1317
- startContentObserver() {
1318
- if (this.contentObserver) {
1319
- return;
1320
- }
1321
- this.contentObserver = new MutationObserver((mutations) => {
1322
- const isInternalMovement = mutations.every((mutation) => {
1323
- return Array.from(mutation.removedNodes).every(
1324
- (node) => this.isInternalLabelNode(node) || this.contains(node)
1325
- ) && Array.from(mutation.addedNodes).every((node) => this.isInternalLabelNode(node));
1326
- });
1327
- if (isInternalMovement) {
1328
- return;
1329
- }
1330
- if (this.syncProjectedContent()) {
1331
- this.requestUpdate();
1332
- }
1333
- });
1334
- this.contentObserver.observe(this, { childList: true });
1335
- }
1336
- stopContentObserver() {
1337
- this.contentObserver?.disconnect();
1338
- this.contentObserver = void 0;
1339
- }
1340
- syncProjectedContent() {
1341
- const nextProjectedContent = [];
1342
- Array.from(this.childNodes).forEach((node) => {
1343
- if (this.isInternalLabelNode(node)) {
1344
- this.projectedContent.forEach((projectedNode) => {
1345
- if (projectedNode.parentNode !== this && this.contains(projectedNode)) {
1346
- nextProjectedContent.push(projectedNode);
1347
- }
1348
- });
1349
- return;
1350
- }
1351
- nextProjectedContent.push(node);
1352
- });
1353
- if (nextProjectedContent.length === this.projectedContent.length && nextProjectedContent.every((node, index) => node === this.projectedContent[index])) {
1006
+ isInternalNode(node) {
1007
+ if (node.nodeType !== Node.ELEMENT_NODE) {
1354
1008
  return false;
1355
1009
  }
1356
- this.projectedContent = nextProjectedContent;
1357
- return true;
1358
- }
1359
- isInternalLabelNode(node) {
1360
- return node === this.nativeLabel;
1010
+ const el = node;
1011
+ return el.hasAttribute("data-sp-label-native");
1361
1012
  }
1362
1013
  focus(options) {
1363
- this.nativeLabel?.focus(options);
1014
+ this.getContentContainer()?.focus(options);
1364
1015
  }
1365
1016
  blur() {
1366
- this.nativeLabel?.blur();
1017
+ this.getContentContainer()?.blur();
1367
1018
  }
1368
1019
  render() {
1369
- return html`
1370
- <label
1371
- class='sp-label'
1372
- data-sp-label-native
1373
- for=${ifDefined(this.htmlFor)}
1374
- id=${ifDefined(this.id || void 0)}
1375
- tabindex='-1'
1376
- >
1377
- ${this.projectedContent.length > 0 ? this.projectedContent : nothing}
1378
- </label>
1379
- `;
1020
+ return html`<label
1021
+ aria-describedby="${ifDefined(this.forwardedAriaDescribedBy)}"
1022
+ aria-label="${ifDefined(this.forwardedAriaLabel)}"
1023
+ aria-labelledby="${ifDefined(this.forwardedAriaLabelledBy)}"
1024
+ class="sp-label"
1025
+ data-sp-label-native
1026
+ for="${ifDefined(this.htmlFor || void 0)}"
1027
+ id="${ifDefined(this.id || void 0)}"
1028
+ title="${ifDefined(this.title || void 0)}"
1029
+ >
1030
+ ${this.hasProjectedContent ? this.projectedContent : nothing}
1031
+ </label>`;
1380
1032
  }
1381
1033
  };
1382
1034
  SpectreLabelElement.properties = {
1383
- htmlFor: { attribute: "for", type: String }
1035
+ htmlFor: { attribute: "for", type: String, reflect: true }
1384
1036
  };
1385
1037
  function defineSpectreLabel(tagName = "sp-label") {
1386
1038
  const existingElement = customElements.get(tagName);
@@ -1390,165 +1042,49 @@ function defineSpectreLabel(tagName = "sp-label") {
1390
1042
  customElements.define(tagName, SpectreLabelElement);
1391
1043
  return SpectreLabelElement;
1392
1044
  }
1393
- var SpectreFieldsetElement = class extends LitElement {
1045
+ var SpectreFieldsetElement = class extends SpectreProjectableElement {
1394
1046
  constructor() {
1395
1047
  super(...arguments);
1396
- this.ariaLabel = null;
1397
- this.ariaLabelledBy = null;
1398
- this.ariaDescribedBy = null;
1399
1048
  this.disabled = false;
1400
- this.legend = "";
1401
- this.projectedContent = [];
1402
- }
1403
- get id() {
1404
- return this._id ?? "";
1405
- }
1406
- set id(value) {
1407
- if ((this._id ?? "") === value) {
1408
- return;
1409
- }
1410
- this._id = value;
1411
- const host = this;
1412
- if (HTMLElement.prototype.hasAttribute.call(host, "id")) {
1413
- HTMLElement.prototype.removeAttribute.call(host, "id");
1414
- }
1415
- this.requestUpdate();
1416
- }
1417
- createRenderRoot() {
1418
- return this;
1419
- }
1420
- connectedCallback() {
1421
- super.connectedCallback();
1422
- const hostId = super.getAttribute("id");
1423
- if (hostId !== null) {
1424
- this.id = hostId;
1425
- }
1426
- this.syncProjectedContent();
1427
- this.startContentObserver();
1428
- }
1429
- disconnectedCallback() {
1430
- this.stopContentObserver();
1431
- super.disconnectedCallback();
1432
- }
1433
- getAttribute(qualifiedName) {
1434
- if (qualifiedName === "id") {
1435
- return this.id || null;
1436
- }
1437
- return super.getAttribute(qualifiedName);
1438
- }
1439
- hasAttribute(qualifiedName) {
1440
- if (qualifiedName === "id") {
1441
- return this.id !== "";
1442
- }
1443
- return super.hasAttribute(qualifiedName);
1444
- }
1445
- setAttribute(qualifiedName, value) {
1446
- if (qualifiedName === "id") {
1447
- this.id = value;
1448
- return;
1449
- }
1450
- super.setAttribute(qualifiedName, value);
1451
- }
1452
- removeAttribute(qualifiedName) {
1453
- if (qualifiedName === "id") {
1454
- this.id = "";
1455
- return;
1456
- }
1457
- super.removeAttribute(qualifiedName);
1458
- }
1459
- update(changedProperties) {
1460
- this.stopContentObserver();
1461
- super.update(changedProperties);
1462
- this.startContentObserver();
1463
1049
  }
1464
- get nativeFieldset() {
1050
+ getContentContainer() {
1465
1051
  return this.querySelector("[data-sp-fieldset-native]");
1466
1052
  }
1467
- get forwardedAriaLabel() {
1468
- const value = this.ariaLabel?.trim();
1469
- return value ? value : void 0;
1470
- }
1471
- get forwardedAriaLabelledBy() {
1472
- const value = this.ariaLabelledBy?.trim();
1473
- return value ? value : void 0;
1474
- }
1475
- get forwardedAriaDescribedBy() {
1476
- const value = this.ariaDescribedBy?.trim();
1477
- return value ? value : void 0;
1478
- }
1479
- startContentObserver() {
1480
- if (this.contentObserver) {
1481
- return;
1482
- }
1483
- this.contentObserver = new MutationObserver((mutations) => {
1484
- const isInternalMovement = mutations.every((mutation) => {
1485
- return Array.from(mutation.removedNodes).every(
1486
- (node) => this.isInternalFieldsetNode(node) || this.contains(node)
1487
- ) && Array.from(mutation.addedNodes).every((node) => this.isInternalFieldsetNode(node));
1488
- });
1489
- if (isInternalMovement) {
1490
- return;
1491
- }
1492
- if (this.syncProjectedContent()) {
1493
- this.requestUpdate();
1494
- }
1495
- });
1496
- this.contentObserver.observe(this, { childList: true });
1497
- }
1498
- stopContentObserver() {
1499
- this.contentObserver?.disconnect();
1500
- this.contentObserver = void 0;
1501
- }
1502
- syncProjectedContent() {
1503
- const nextProjectedContent = [];
1504
- Array.from(this.childNodes).forEach((node) => {
1505
- if (this.isInternalFieldsetNode(node)) {
1506
- this.projectedContent.forEach((projectedNode) => {
1507
- if (projectedNode.parentNode !== this && this.contains(projectedNode)) {
1508
- nextProjectedContent.push(projectedNode);
1509
- }
1510
- });
1511
- return;
1512
- }
1513
- nextProjectedContent.push(node);
1514
- });
1515
- if (nextProjectedContent.length === this.projectedContent.length && nextProjectedContent.every((node, index) => node === this.projectedContent[index])) {
1053
+ isInternalNode(node) {
1054
+ if (node.nodeType !== Node.ELEMENT_NODE) {
1516
1055
  return false;
1517
1056
  }
1518
- this.projectedContent = nextProjectedContent;
1519
- return true;
1520
- }
1521
- isInternalFieldsetNode(node) {
1522
- return node === this.nativeFieldset;
1057
+ const el = node;
1058
+ return el.hasAttribute("data-sp-fieldset-native") || el.hasAttribute("data-sp-fieldset-legend");
1523
1059
  }
1524
1060
  focus(options) {
1525
- this.nativeFieldset?.focus(options);
1061
+ this.getContentContainer()?.focus(options);
1526
1062
  }
1527
1063
  blur() {
1528
- this.nativeFieldset?.blur();
1064
+ this.getContentContainer()?.blur();
1529
1065
  }
1530
1066
  render() {
1531
- return html`
1532
- <fieldset
1533
- aria-describedby=${ifDefined(this.forwardedAriaDescribedBy)}
1534
- aria-label=${ifDefined(this.forwardedAriaLabel)}
1535
- aria-labelledby=${ifDefined(this.forwardedAriaLabelledBy)}
1536
- data-sp-fieldset-native
1537
- ?disabled=${this.disabled}
1538
- id=${ifDefined(this.id || void 0)}
1539
- >
1540
- ${this.legend ? html`<legend class='sp-label'>${this.legend}</legend>` : nothing}
1541
- ${this.projectedContent.length > 0 ? this.projectedContent : nothing}
1542
- </fieldset>
1543
- `;
1067
+ return html`<fieldset
1068
+ aria-describedby="${ifDefined(this.forwardedAriaDescribedBy)}"
1069
+ aria-label="${ifDefined(this.forwardedAriaLabel)}"
1070
+ aria-labelledby="${ifDefined(this.forwardedAriaLabelledBy)}"
1071
+ data-sp-fieldset-native
1072
+ ?disabled="${this.disabled}"
1073
+ form="${ifDefined(this.form || void 0)}"
1074
+ id="${ifDefined(this.id || void 0)}"
1075
+ name="${ifDefined(this.name || void 0)}"
1076
+ title="${ifDefined(this.title || void 0)}"
1077
+ >
1078
+ ${this.legend ? html`<legend class="sp-label" data-sp-fieldset-legend>${this.legend}</legend>` : nothing}
1079
+ ${this.hasProjectedContent ? this.projectedContent : nothing}
1080
+ </fieldset>`;
1544
1081
  }
1545
1082
  };
1546
1083
  SpectreFieldsetElement.properties = {
1547
- ariaLabel: { attribute: "aria-label", type: String },
1548
- ariaLabelledBy: { attribute: "aria-labelledby", type: String },
1549
- ariaDescribedBy: { attribute: "aria-describedby", type: String },
1550
1084
  disabled: { type: Boolean, reflect: true },
1551
- legend: { type: String, reflect: true }
1085
+ form: { type: String },
1086
+ legend: { type: String, reflect: true },
1087
+ name: { type: String, reflect: true }
1552
1088
  };
1553
1089
  function defineSpectreFieldset(tagName = "sp-fieldset") {
1554
1090
  const existingElement = customElements.get(tagName);
@@ -1571,6 +1107,6 @@ function defineSpectreComponents() {
1571
1107
  defineSpectreFieldset();
1572
1108
  }
1573
1109
 
1574
- export { SpectreButtonElement, SpectreCheckboxElement, SpectreFieldsetElement, SpectreInputElement, SpectreLabelElement, SpectreRadioElement, SpectreSelectElement, SpectreTextareaElement, defineSpectreButton, defineSpectreCheckbox, defineSpectreComponents, defineSpectreFieldset, defineSpectreInput, defineSpectreLabel, defineSpectreRadio, defineSpectreSelect, defineSpectreTextarea, spectreButtonSizes, spectreButtonTypes, spectreButtonVariants, spectreInputSizes, spectreInputTypes, spectreSelectSizes };
1110
+ export { SpectreButtonElement, SpectreCheckboxElement, SpectreFieldsetElement, SpectreInputElement, SpectreLabelElement, SpectreRadioElement, SpectreSelectElement, SpectreTextareaElement, defineSpectreButton, defineSpectreCheckbox, defineSpectreComponents, defineSpectreFieldset, defineSpectreInput, defineSpectreLabel, defineSpectreRadio, defineSpectreSelect, defineSpectreTextarea, spectreButtonSizes, spectreButtonTypes, spectreButtonVariants, spectreInputSizes, spectreInputTypes };
1575
1111
  //# sourceMappingURL=index.js.map
1576
1112
  //# sourceMappingURL=index.js.map