@incursa/ui-kit 1.6.1 → 1.8.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 (47) hide show
  1. package/NOTICE +8 -0
  2. package/README.md +16 -0
  3. package/dist/icons/index.js +371 -0
  4. package/dist/icons/package.json +3 -0
  5. package/dist/inc-design-language.css +144 -51
  6. package/dist/inc-design-language.css.map +1 -1
  7. package/dist/inc-design-language.js +1627 -1206
  8. package/dist/inc-design-language.min.css +1 -1
  9. package/dist/inc-design-language.min.css.map +1 -1
  10. package/dist/mcp/components/buttons.json +3 -3
  11. package/dist/mcp/components/cards.json +3 -3
  12. package/dist/mcp/components/metrics.json +3 -3
  13. package/dist/mcp/components/states.json +3 -3
  14. package/dist/mcp/components/status.json +3 -3
  15. package/dist/mcp/examples/data-grid-advanced.json +2 -2
  16. package/dist/mcp/examples/demo.json +2 -2
  17. package/dist/mcp/examples/overlay-workflows.json +2 -2
  18. package/dist/mcp/examples/reference.json +2 -2
  19. package/dist/mcp/examples/states.json +2 -2
  20. package/dist/mcp/examples/web-components.json +2 -2
  21. package/dist/mcp/guides/latest.json +2 -2
  22. package/dist/mcp/guides/package-metadata.json +2 -2
  23. package/dist/mcp/guides/update.json +2 -2
  24. package/dist/mcp/install.json +1 -1
  25. package/dist/mcp/patterns/data-grid-advanced.json +2 -2
  26. package/dist/mcp/patterns/demo.json +2 -2
  27. package/dist/mcp/patterns/overlay-workflows.json +2 -2
  28. package/dist/mcp/patterns/reference.json +2 -2
  29. package/dist/mcp/patterns/states.json +2 -2
  30. package/dist/mcp/patterns/web-components.json +2 -2
  31. package/dist/mcp/resources.json +83 -80
  32. package/dist/mcp/search-index.json +25 -25
  33. package/dist/mcp/update.json +2 -2
  34. package/dist/mcp/worker.mjs +394 -391
  35. package/dist/mcp/worker.mjs.map +2 -2
  36. package/dist/web-components/README.md +4 -0
  37. package/dist/web-components/components/actions.js +237 -14
  38. package/dist/web-components/components/feedback.js +71 -7
  39. package/dist/web-components/index.js +583 -21
  40. package/package.json +10 -3
  41. package/src/icons/index.js +229 -0
  42. package/src/icons/package.json +3 -0
  43. package/src/inc-design-language.js +327 -1
  44. package/src/inc-design-language.scss +178 -55
  45. package/src/web-components/README.md +4 -0
  46. package/src/web-components/components/actions.js +237 -14
  47. package/src/web-components/components/feedback.js +71 -7
@@ -8,9 +8,11 @@ The CSS-first [`inc-*`](../../reference.html) class surface remains the canonica
8
8
 
9
9
  - Package entrypoint: `@incursa/ui-kit/web-components`
10
10
  - Style entrypoint: `@incursa/ui-kit/web-components/style.css`
11
+ - Icon entrypoint: `@incursa/ui-kit/icons`
11
12
  - Built output: `dist/web-components/`
12
13
  - Package export: `./web-components` resolves to `dist/web-components/index.js`
13
14
  - Package export: `./web-components/style.css` resolves to `dist/web-components/style.css`
15
+ - Package export: `./icons` resolves to `dist/icons/index.js`
14
16
  - Module boundary: `src/web-components/package.json` sets this subtree to `type: module`
15
17
 
16
18
  Load these entrypoints only when the consuming app wants the custom elements and their default look. CSS-only consumers should not pay for the runtime.
@@ -45,6 +47,8 @@ The runtime auto-defines the shipped elements on load. If a consumer needs expli
45
47
  The `IncElement` base class, including reflected attribute/property wiring and slot helpers.
46
48
  - [`registry.js`](registry.js)
47
49
  Idempotent registration helpers and the `IncWebComponents.registry` namespace.
50
+ - [`../icons/index.js`](../icons/index.js)
51
+ Semantic Incursa icon names, default Lucide-backed rendering, and the global renderer override used by component fallbacks.
48
52
  - [`components/dom-helpers.js`](components/dom-helpers.js)
49
53
  Shared DOM helpers used by the action and collection modules.
50
54
  - [`controllers/focus.js`](controllers/focus.js)
@@ -1,3 +1,7 @@
1
+ import {
2
+ normalizeIconName,
3
+ replaceIconContents,
4
+ } from "../../icons/index.js";
1
5
  import {
2
6
  addClass,
3
7
  ensureNode,
@@ -24,6 +28,18 @@ const BUTTON_VARIANTS = new Set([
24
28
  "outline-info",
25
29
  ]);
26
30
  const BUTTON_SIZES = new Set(["sm", "lg", "micro"]);
31
+ const ALERT_DEFAULT_ROLE_BY_TONE = new Map([
32
+ ["info", "status"],
33
+ ["secondary", "status"],
34
+ ]);
35
+ const ALERT_ICON_BY_TONE = new Map([
36
+ ["success", "success"],
37
+ ["danger", "error"],
38
+ ["warning", "warning"],
39
+ ["info", "info"],
40
+ ["secondary", "info"],
41
+ ["primary", "info"],
42
+ ]);
27
43
 
28
44
  const HostElement = typeof HTMLElement === "undefined" ? class {} : HTMLElement;
29
45
 
@@ -35,6 +51,11 @@ function toBoolean(value, fallback = false) {
35
51
  return !FALSE_TOKENS.has(String(value).toLowerCase());
36
52
  }
37
53
 
54
+ function toPositiveInt(value) {
55
+ const parsed = Number.parseInt(String(value ?? "").trim(), 10);
56
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
57
+ }
58
+
38
59
  function emit(host, type, detail = {}, options = {}) {
39
60
  return host.dispatchEvent(new CustomEvent(type, {
40
61
  detail,
@@ -44,6 +65,29 @@ function emit(host, type, detail = {}, options = {}) {
44
65
  }));
45
66
  }
46
67
 
68
+ function getDirectIconSlot(host) {
69
+ return Array.from(host.children || []).find((node) => (
70
+ node instanceof HTMLElement
71
+ && node.getAttribute("slot") === "icon"
72
+ )) || null;
73
+ }
74
+
75
+ function hasConsumerIcon(container) {
76
+ return Array.from(container.children || []).some((node) => (
77
+ node instanceof HTMLElement
78
+ && !node.hasAttribute("data-inc-generated-icon")
79
+ ));
80
+ }
81
+
82
+ function renderDecorativeIcon(container, name, options = {}) {
83
+ replaceIconContents(container, name, {
84
+ className: "inc-icon",
85
+ decorative: true,
86
+ size: options.size || 16,
87
+ });
88
+ container.hidden = false;
89
+ }
90
+
47
91
  class IncElement extends HostElement {
48
92
  emit(type, detail = {}, options = {}) {
49
93
  return emit(this, type, detail, options);
@@ -52,7 +96,7 @@ class IncElement extends HostElement {
52
96
 
53
97
  export class IncButtonElement extends IncElement {
54
98
  static get observedAttributes() {
55
- return ["tone", "variant", "size", "loading", "href", "type", "disabled", "label", "target", "rel", "download"];
99
+ return ["tone", "variant", "size", "loading", "href", "type", "disabled", "label", "target", "rel", "download", "icon"];
56
100
  }
57
101
 
58
102
  connectedCallback() {
@@ -157,6 +201,8 @@ export class IncButtonElement extends IncElement {
157
201
  this.removeLoadingSpinner(control);
158
202
  }
159
203
 
204
+ this.syncIcon(control);
205
+
160
206
  const label = this.getAttribute("label");
161
207
  if (label) {
162
208
  control.setAttribute("aria-label", label);
@@ -211,6 +257,53 @@ export class IncButtonElement extends IncElement {
211
257
 
212
258
  control.querySelectorAll(":scope > [data-inc-button-spinner]").forEach((node) => node.remove());
213
259
  }
260
+
261
+ syncIcon(control) {
262
+ if (!(control instanceof HTMLElement)) {
263
+ return;
264
+ }
265
+
266
+ const explicitIcon = normalizeIconName(this.getAttribute("icon"));
267
+ const inferredIcon = this.getAttribute("download") != null
268
+ ? "download"
269
+ : this.getAttribute("target") === "_blank"
270
+ ? "external-link"
271
+ : "";
272
+ const iconName = explicitIcon || inferredIcon;
273
+ let icon = control.querySelector(":scope > [data-inc-button-icon]");
274
+ const slotted = getDirectIconSlot(control);
275
+
276
+ if (!icon && (iconName || slotted)) {
277
+ icon = document.createElement("span");
278
+ icon.className = "inc-btn__icon";
279
+ icon.setAttribute("data-inc-button-icon", "true");
280
+ icon.setAttribute("aria-hidden", "true");
281
+ control.prepend(icon);
282
+ }
283
+
284
+ if (!(icon instanceof HTMLElement)) {
285
+ return;
286
+ }
287
+
288
+ if (slotted) {
289
+ slotted.removeAttribute("slot");
290
+ icon.replaceChildren(slotted);
291
+ icon.hidden = false;
292
+ return;
293
+ }
294
+
295
+ if (hasConsumerIcon(icon)) {
296
+ icon.hidden = false;
297
+ return;
298
+ }
299
+
300
+ if (iconName && iconName !== "none") {
301
+ renderDecorativeIcon(icon, iconName, { size: 16 });
302
+ return;
303
+ }
304
+
305
+ icon.remove();
306
+ }
214
307
  }
215
308
 
216
309
  export class IncButtonGroupElement extends IncElement {
@@ -292,7 +385,6 @@ export class IncCloseButtonElement extends IncElement {
292
385
  }
293
386
 
294
387
  connectedCallback() {
295
- addClass(this, "inc-close-button");
296
388
  this.sync();
297
389
  }
298
390
 
@@ -303,7 +395,7 @@ export class IncCloseButtonElement extends IncElement {
303
395
  }
304
396
 
305
397
  sync() {
306
- addClass(this, "inc-close-button");
398
+ this.classList.remove("inc-close-button", "inc-close-button--white");
307
399
  this.setAttribute("part", "close-button");
308
400
  const control = this.ensureControl();
309
401
  const variant = normalizeToken(this.getAttribute("variant"));
@@ -316,9 +408,7 @@ export class IncCloseButtonElement extends IncElement {
316
408
 
317
409
  control.type = "button";
318
410
  control.setAttribute("aria-label", this.getAttribute("label") || "Close");
319
- if (!control.childNodes.length) {
320
- control.textContent = "×";
321
- }
411
+ control.textContent = "";
322
412
  }
323
413
 
324
414
  ensureControl() {
@@ -344,7 +434,7 @@ export class IncCloseButtonElement extends IncElement {
344
434
 
345
435
  export class IncAlertElement extends IncElement {
346
436
  static get observedAttributes() {
347
- return ["tone", "variant", "dismissible", "dismiss-label"];
437
+ return ["tone", "variant", "dismissible", "dismiss-label", "timeout", "icon"];
348
438
  }
349
439
 
350
440
  connectedCallback() {
@@ -354,6 +444,7 @@ export class IncAlertElement extends IncElement {
354
444
  }
355
445
 
356
446
  disconnectedCallback() {
447
+ this.stopDismissTimer();
357
448
  if (this._boundClick) {
358
449
  this.removeEventListener("click", this._boundClick);
359
450
  }
@@ -377,7 +468,7 @@ export class IncAlertElement extends IncElement {
377
468
  }
378
469
 
379
470
  event.preventDefault();
380
- this.hide();
471
+ this.dismiss("manual");
381
472
  };
382
473
 
383
474
  this.addEventListener("click", this._boundClick);
@@ -391,6 +482,7 @@ export class IncAlertElement extends IncElement {
391
482
  const tone = normalizeToken(this.getAttribute("tone") || this.getAttribute("variant")) || "info";
392
483
  const resolvedTone = BADGE_TONES.has(tone) ? tone : "info";
393
484
  this.classList.add(`inc-alert--${resolvedTone}`);
485
+ this.syncIcon(resolvedTone);
394
486
 
395
487
  if (toBoolean(this.getAttribute("dismissible"))) {
396
488
  this.classList.add("inc-alert--dismissible");
@@ -400,12 +492,25 @@ export class IncAlertElement extends IncElement {
400
492
  }
401
493
 
402
494
  if (!this.hasAttribute("role")) {
403
- this.setAttribute("role", resolvedTone === "info" || resolvedTone === "secondary" ? "status" : "alert");
495
+ this.setAttribute("role", ALERT_DEFAULT_ROLE_BY_TONE.get(resolvedTone) || "alert");
404
496
  }
405
497
  if (!this.hasAttribute("aria-live")) {
406
498
  this.setAttribute("aria-live", this.getAttribute("role") === "alert" ? "assertive" : "polite");
407
499
  }
408
500
  this.setAttribute("aria-atomic", "true");
501
+
502
+ const timeoutMs = toPositiveInt(this.getAttribute("timeout"));
503
+ if (timeoutMs) {
504
+ this.ensureProgressBar();
505
+ if (!this.hidden && this.getAttribute("aria-hidden") !== "true") {
506
+ this.startDismissTimer(timeoutMs);
507
+ } else {
508
+ this.stopDismissTimer();
509
+ }
510
+ } else {
511
+ this.stopDismissTimer();
512
+ this.removeProgressBar();
513
+ }
409
514
  }
410
515
 
411
516
  ensureDismissButton() {
@@ -420,9 +525,7 @@ export class IncAlertElement extends IncElement {
420
525
  button.className = "inc-close-button";
421
526
  button.setAttribute("part", "dismiss");
422
527
  button.setAttribute("aria-label", this.getAttribute("dismiss-label") || "Dismiss alert");
423
- if (!button.childNodes.length) {
424
- button.textContent = "×";
425
- }
528
+ button.textContent = "";
426
529
  return button;
427
530
  }
428
531
 
@@ -430,19 +533,125 @@ export class IncAlertElement extends IncElement {
430
533
  this.querySelectorAll(":scope > [data-inc-alert-dismiss]").forEach((node) => node.remove());
431
534
  }
432
535
 
433
- hide() {
536
+ ensureProgressBar() {
537
+ let progress = this.querySelector(":scope > .inc-alert__progress");
538
+ if (!progress) {
539
+ progress = document.createElement("div");
540
+ progress.className = "inc-alert__progress";
541
+ progress.setAttribute("part", "progress");
542
+ progress.setAttribute("aria-hidden", "true");
543
+ this.append(progress);
544
+ }
545
+
546
+ return progress;
547
+ }
548
+
549
+ removeProgressBar() {
550
+ this.querySelectorAll(":scope > .inc-alert__progress").forEach((node) => node.remove());
551
+ }
552
+
553
+ syncIcon(tone) {
554
+ const explicitIcon = normalizeIconName(this.getAttribute("icon"));
555
+ const iconName = explicitIcon || ALERT_ICON_BY_TONE.get(tone) || "info";
556
+ let icon = this.querySelector(":scope > .inc-alert__icon");
557
+ const slotted = getDirectIconSlot(this);
558
+
559
+ if (!icon && (iconName !== "none" || slotted)) {
560
+ icon = document.createElement("span");
561
+ icon.className = "inc-alert__icon";
562
+ icon.setAttribute("part", "icon");
563
+ icon.setAttribute("aria-hidden", "true");
564
+ this.prepend(icon);
565
+ }
566
+
567
+ if (!(icon instanceof HTMLElement)) {
568
+ return;
569
+ }
570
+
571
+ if (slotted) {
572
+ slotted.removeAttribute("slot");
573
+ icon.replaceChildren(slotted);
574
+ icon.hidden = false;
575
+ return;
576
+ }
577
+
578
+ if (hasConsumerIcon(icon)) {
579
+ icon.hidden = false;
580
+ return;
581
+ }
582
+
583
+ if (iconName === "none") {
584
+ icon.remove();
585
+ return;
586
+ }
587
+
588
+ renderDecorativeIcon(icon, iconName, { size: 18 });
589
+ }
590
+
591
+ startDismissTimer(timeoutMs) {
592
+ const progress = this.ensureProgressBar();
593
+ this.stopDismissTimer();
594
+ this._dismissTimeoutMs = timeoutMs;
595
+ this._dismissStartedAt = performance.now();
596
+
597
+ const tick = (now) => {
598
+ if (this.hidden || this.getAttribute("aria-hidden") === "true") {
599
+ this.stopDismissTimer();
600
+ return;
601
+ }
602
+
603
+ const elapsed = Math.max(0, now - this._dismissStartedAt);
604
+ const remaining = Math.max(0, timeoutMs - elapsed);
605
+ const ratio = timeoutMs > 0 ? remaining / timeoutMs : 0;
606
+ progress.style.transform = `scaleX(${ratio})`;
607
+
608
+ if (remaining <= 0) {
609
+ this.dismiss("timeout");
610
+ return;
611
+ }
612
+
613
+ this._dismissFrame = window.requestAnimationFrame(tick);
614
+ };
615
+
616
+ progress.style.transform = "scaleX(1)";
617
+ this._dismissFrame = window.requestAnimationFrame(tick);
618
+ }
619
+
620
+ stopDismissTimer() {
621
+ if (this._dismissFrame) {
622
+ window.cancelAnimationFrame(this._dismissFrame);
623
+ this._dismissFrame = 0;
624
+ }
625
+ }
626
+
627
+ dismiss(reason = "manual") {
628
+ this.hide(reason);
629
+ }
630
+
631
+ hide(reason = "manual") {
632
+ this.stopDismissTimer();
434
633
  this.hidden = true;
435
634
  this.setAttribute("aria-hidden", "true");
436
- this.emit("dismiss", { hidden: true });
635
+ this.emit("dismiss", { hidden: true, reason });
437
636
  }
438
637
  }
439
638
 
440
639
  export class IncEmptyStateElement extends IncElement {
640
+ static get observedAttributes() {
641
+ return ["icon"];
642
+ }
643
+
441
644
  connectedCallback() {
442
645
  addClass(this, "inc-empty-state");
443
646
  this.sync();
444
647
  }
445
648
 
649
+ attributeChangedCallback() {
650
+ if (this.isConnected) {
651
+ this.sync();
652
+ }
653
+ }
654
+
446
655
  sync() {
447
656
  addClass(this, "inc-empty-state");
448
657
  this.setAttribute("part", "empty-state content icon body actions");
@@ -496,6 +705,20 @@ export class IncEmptyStateElement extends IncElement {
496
705
 
497
706
  body.append(node);
498
707
  });
708
+
709
+ if (hasConsumerIcon(icon)) {
710
+ icon.hidden = false;
711
+ return;
712
+ }
713
+
714
+ const iconName = normalizeIconName(this.getAttribute("icon")) || "empty";
715
+ if (iconName === "none") {
716
+ icon.replaceChildren();
717
+ icon.hidden = true;
718
+ return;
719
+ }
720
+
721
+ renderDecorativeIcon(icon, iconName, { size: 34 });
499
722
  }
500
723
  }
501
724
 
@@ -1,7 +1,30 @@
1
+ import {
2
+ incIconNames,
3
+ normalizeIconName,
4
+ replaceIconContents,
5
+ } from "../../icons/index.js";
6
+
1
7
  const THEME_MODES = ["light", "dark", "system"];
2
8
  const DEFAULT_THEME_STORAGE_KEY = "inc-theme-mode";
3
9
  const BADGE_TONES = new Set(["primary", "secondary", "success", "danger", "warning", "info"]);
4
10
  const SPINNER_VARIANTS = new Set(["border", "grow"]);
11
+ const ICON_NAME_SET = new Set(incIconNames);
12
+ const STATE_ICON_BY_VARIANT = new Map([
13
+ ["empty", "empty"],
14
+ ["results", "no-results"],
15
+ ["loading", "loading"],
16
+ ["error", "error"],
17
+ ["danger", "error"],
18
+ ["warning", "warning"],
19
+ ["success", "success"],
20
+ ["info", "info"],
21
+ ]);
22
+ const STATE_ICON_BY_STATUS = new Map([
23
+ ["+", "empty"],
24
+ ["?", "no-results"],
25
+ ["!", "error"],
26
+ ["...", "loading"],
27
+ ]);
5
28
  const HostElement = typeof HTMLElement === "undefined" ? class {} : HTMLElement;
6
29
  const themeSubscribers = new Set();
7
30
 
@@ -38,6 +61,33 @@ function normalizeToken(value) {
38
61
  return String(value ?? "").trim().toLowerCase();
39
62
  }
40
63
 
64
+ function resolveStateIconName(icon, status, variant) {
65
+ const explicitIcon = normalizeIconName(icon);
66
+ if (explicitIcon) {
67
+ return explicitIcon;
68
+ }
69
+
70
+ const normalizedStatus = normalizeIconName(status);
71
+ if (STATE_ICON_BY_STATUS.has(String(status || "").trim())) {
72
+ return STATE_ICON_BY_STATUS.get(String(status || "").trim());
73
+ }
74
+
75
+ if (ICON_NAME_SET.has(normalizedStatus)) {
76
+ return normalizedStatus;
77
+ }
78
+
79
+ return STATE_ICON_BY_VARIANT.get(normalizeToken(variant)) || "info";
80
+ }
81
+
82
+ function renderDecorativeIcon(container, name, size = 18) {
83
+ replaceIconContents(container, name, {
84
+ className: "inc-icon",
85
+ decorative: true,
86
+ size,
87
+ });
88
+ container.hidden = false;
89
+ }
90
+
41
91
  function getSystemTheme() {
42
92
  if (!window.matchMedia) {
43
93
  return "light";
@@ -198,7 +248,7 @@ function formatRemaining(totalSeconds) {
198
248
  }
199
249
 
200
250
  export class IncStatePanel extends HostElement {
201
- static observedAttributes = ["tone", "variant", "title", "body", "status", "open"];
251
+ static observedAttributes = ["tone", "variant", "title", "body", "status", "icon", "open"];
202
252
 
203
253
  #fallback = null;
204
254
  #appliedVariantClass = "";
@@ -238,6 +288,7 @@ export class IncStatePanel extends HostElement {
238
288
  actions.className = "inc-state-panel__actions";
239
289
 
240
290
  icon.setAttribute("part", "icon");
291
+ icon.setAttribute("aria-hidden", "true");
241
292
  title.setAttribute("part", "title");
242
293
  body.setAttribute("part", "body");
243
294
  actions.setAttribute("part", "actions");
@@ -274,11 +325,19 @@ export class IncStatePanel extends HostElement {
274
325
  const title = this.getAttribute("title") || "";
275
326
  const body = this.getAttribute("body") || "";
276
327
  const status = this.getAttribute("status") || "";
328
+ const iconName = resolveStateIconName(this.getAttribute("icon"), status, nextVariant);
277
329
 
278
330
  this.#fallback.title.textContent = title;
279
331
  this.#fallback.body.textContent = body;
280
- this.#fallback.icon.textContent = status;
281
- this.#fallback.icon.hidden = !status;
332
+ if (iconName === "none") {
333
+ this.#fallback.icon.replaceChildren();
334
+ this.#fallback.icon.hidden = true;
335
+ } else if (ICON_NAME_SET.has(iconName)) {
336
+ renderDecorativeIcon(this.#fallback.icon, iconName, 22);
337
+ } else {
338
+ this.#fallback.icon.textContent = status;
339
+ this.#fallback.icon.hidden = !status;
340
+ }
282
341
  this.#fallback.actions.hidden = true;
283
342
  }
284
343
 
@@ -677,6 +736,10 @@ export class IncAutoRefresh extends HostElement {
677
736
  }
678
737
 
679
738
  this.innerHTML = `
739
+ <button type="button" class="inc-auto-refresh__toggle inc-btn inc-btn--outline-secondary inc-btn--micro" part="toggle">
740
+ <span class="inc-auto-refresh__toggle-icon" aria-hidden="true"></span>
741
+ <span class="inc-auto-refresh__toggle-text"></span>
742
+ </button>
680
743
  <span class="inc-auto-refresh__countdown" part="countdown">
681
744
  <span class="inc-auto-refresh__label" part="label"></span>
682
745
  <span class="inc-auto-refresh__value" part="value"></span>
@@ -684,10 +747,6 @@ export class IncAutoRefresh extends HostElement {
684
747
  <span class="inc-auto-refresh__status" part="status" hidden>
685
748
  <span class="inc-auto-refresh__status-text"></span>
686
749
  </span>
687
- <button type="button" class="inc-auto-refresh__toggle inc-btn inc-btn--outline-secondary inc-btn--micro" part="toggle">
688
- <span class="inc-auto-refresh__toggle-icon" aria-hidden="true"></span>
689
- <span class="inc-auto-refresh__toggle-text"></span>
690
- </button>
691
750
  `.trim();
692
751
 
693
752
  this.#parts = this.#getParts();
@@ -701,6 +760,7 @@ export class IncAutoRefresh extends HostElement {
701
760
  status: this.querySelector(".inc-auto-refresh__status"),
702
761
  statusText: this.querySelector(".inc-auto-refresh__status-text"),
703
762
  toggle: this.querySelector(".inc-auto-refresh__toggle"),
763
+ toggleIcon: this.querySelector(".inc-auto-refresh__toggle-icon"),
704
764
  toggleText: this.querySelector(".inc-auto-refresh__toggle-text"),
705
765
  };
706
766
  }
@@ -861,6 +921,10 @@ export class IncAutoRefresh extends HostElement {
861
921
  if (this.#parts.toggleText) {
862
922
  this.#parts.toggleText.textContent = actionLabel;
863
923
  }
924
+
925
+ if (this.#parts.toggleIcon instanceof HTMLElement) {
926
+ renderDecorativeIcon(this.#parts.toggleIcon, this.#isPaused ? "play" : "pause", 16);
927
+ }
864
928
  }
865
929
 
866
930
  #stop() {