@expressivecss/expressive 0.8.0 → 0.9.1

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 (51) hide show
  1. package/CHANGELOG.md +65 -1
  2. package/README.md +69 -26
  3. package/dist/css/expressive.css +657 -85
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/fonts/LICENSE-material-symbols +93 -0
  8. package/dist/fonts/LICENSE-noto-sans +93 -0
  9. package/dist/fonts/LICENSE-roboto +93 -0
  10. package/dist/fonts/material-symbols-outlined.woff2 +0 -0
  11. package/dist/fonts/material-symbols-rounded.woff2 +0 -0
  12. package/dist/fonts/material-symbols-sharp.woff2 +0 -0
  13. package/dist/fonts/noto-sans-latin-400.woff2 +0 -0
  14. package/dist/fonts/noto-sans-latin-500.woff2 +0 -0
  15. package/dist/fonts/roboto-latin-400.woff2 +0 -0
  16. package/dist/fonts/roboto-latin-500.woff2 +0 -0
  17. package/dist/js/expressive.cjs +555 -151
  18. package/dist/js/expressive.cjs.map +4 -4
  19. package/dist/js/expressive.js +555 -151
  20. package/dist/js/expressive.js.map +4 -4
  21. package/dist/js/expressive.min.js +10 -10
  22. package/dist/js/expressive.min.js.map +4 -4
  23. package/dist/js/expressive.mjs +555 -151
  24. package/dist/js/expressive.mjs.map +4 -4
  25. package/dist/types/components/appBar.d.ts +26 -0
  26. package/dist/types/components/buttonGroup.d.ts +39 -0
  27. package/dist/types/components/cards.d.ts +11 -13
  28. package/dist/types/components/datepicker.d.ts +4 -0
  29. package/dist/types/components/expandingCard.d.ts +1 -1
  30. package/dist/types/components/index.d.ts +2 -0
  31. package/dist/types/components/menu.d.ts +4 -0
  32. package/dist/types/components/registry.d.ts +10 -0
  33. package/dist/types/components/timepicker.d.ts +0 -1
  34. package/dist/types/components/tooltip.d.ts +1 -0
  35. package/dist/types/index.d.ts +1 -1
  36. package/package.json +25 -3
  37. package/src/sass/abstracts/_mixins.scss +52 -0
  38. package/src/sass/abstracts/_variables.scss +19 -0
  39. package/src/sass/base/_fonts.scss +63 -0
  40. package/src/sass/components/_badges.scss +1 -2
  41. package/src/sass/components/_button-groups.scss +252 -80
  42. package/src/sass/components/_cards.scss +108 -93
  43. package/src/sass/components/_expanding-card.scss +1 -35
  44. package/src/sass/components/_icon-buttons.scss +2 -24
  45. package/src/sass/components/_icons-material-design.scss +6 -6
  46. package/src/sass/components/_navbar.scss +180 -27
  47. package/src/sass/components/_navigation-bar.scss +9 -1
  48. package/src/sass/components/_navigation-rail.scss +9 -1
  49. package/src/sass/components/_transitions.scss +8 -1
  50. package/src/sass/components/forms/_select.scss +38 -0
  51. package/src/sass/expressive.scss +5 -0
@@ -380,103 +380,120 @@ var _defaults = {
380
380
  onOpen: null,
381
381
  onClose: null
382
382
  };
383
- var CARDS_SELECTOR = "article:has(> aside)";
383
+ var CARDS_SELECTOR = 'article:has(> aside[id]:not([id=""])):not(:has(> aside ~ aside)):has(> button.card-reveal-trigger[type="button"], > :not(aside) button.card-reveal-trigger[type="button"])';
384
+ var REVEAL_INITIALIZED_CLASS = "card-reveal-initialized";
385
+ function hasAccessibleName(trigger) {
386
+ const label = trigger.getAttribute("aria-label");
387
+ if (label?.trim()) return true;
388
+ const labelledBy = trigger.getAttribute("aria-labelledby");
389
+ if (labelledBy) {
390
+ const referencedText = labelledBy.trim().split(/\s+/).map((id) => trigger.ownerDocument.getElementById(id)?.textContent ?? "").join(" ").trim();
391
+ if (referencedText) return true;
392
+ }
393
+ const content = trigger.cloneNode(true);
394
+ content.querySelectorAll('[aria-hidden="true"]').forEach((node) => node.remove());
395
+ return Boolean(content.textContent?.trim());
396
+ }
397
+ function isUsableCardTrigger(trigger, card, panel) {
398
+ return trigger.closest("article") === card && !panel.contains(trigger) && !trigger.disabled && trigger.getAttribute("aria-disabled") !== "true" && hasAccessibleName(trigger);
399
+ }
400
+ function isCardReveal(el) {
401
+ if (el.tagName !== "ARTICLE") return false;
402
+ const panels = el.querySelectorAll(":scope > aside");
403
+ if (panels.length !== 1 || !panels[0].id) return false;
404
+ return Array.from(
405
+ el.querySelectorAll('button.card-reveal-trigger[type="button"]')
406
+ ).some((trigger) => isUsableCardTrigger(trigger, el, panels[0]));
407
+ }
384
408
  var Cards = class _Cards extends Component {
385
- constructor(el, options) {
409
+ constructor(el, options = {}) {
386
410
  super(el, options, _Cards);
387
411
  this.isOpen = false;
412
+ this._ownsRevealInitializedClass = false;
413
+ this._initialAriaControls = /* @__PURE__ */ new Map();
414
+ this._lastActivator = null;
388
415
  this._setupEventHandlers = () => {
389
- this._activators.forEach((el) => {
390
- el.addEventListener("click", this._handleClickInteraction);
391
- el.addEventListener("keypress", this._handleKeypressEvent);
416
+ this._activators.forEach((activator) => {
417
+ activator.addEventListener("click", this._handleClickInteraction);
392
418
  });
419
+ this.el.addEventListener("keydown", this._handleKeydownEvent);
393
420
  };
394
421
  this._removeEventHandlers = () => {
395
- this._activators.forEach((el) => {
396
- el.removeEventListener("click", this._handleClickInteraction);
397
- el.removeEventListener("keypress", this._handleKeypressEvent);
422
+ this._activators.forEach((activator) => {
423
+ activator.removeEventListener("click", this._handleClickInteraction);
398
424
  });
425
+ this.el.removeEventListener("keydown", this._handleKeydownEvent);
399
426
  };
400
- this._handleClickInteraction = () => {
427
+ this._handleClickInteraction = (event) => {
428
+ this._lastActivator = event.currentTarget;
401
429
  this._handleRevealEvent();
402
430
  };
403
- this._handleKeypressEvent = (e) => {
404
- if (e.key === Utils.keys.ENTER || e.key === " ") {
405
- e.preventDefault();
406
- this._handleRevealEvent();
407
- }
431
+ this._handleKeydownEvent = (event) => {
432
+ const target = event.target instanceof Element ? event.target.closest("article") : null;
433
+ if (target !== this.el || event.key !== "Escape" || !this.isOpen) return;
434
+ event.preventDefault();
435
+ this.close();
408
436
  };
409
437
  this._handleRevealEvent = () => {
410
438
  if (this.isOpen) this.close();
411
439
  else this.open();
412
440
  };
413
- this._setupRevealCloseEventHandlers = () => {
414
- this.cardRevealClose.addEventListener("click", this.close);
415
- this.cardRevealClose.addEventListener("keypress", this._handleKeypressCloseEvent);
416
- };
417
- this._removeRevealCloseEventHandlers = () => {
418
- this.cardRevealClose.removeEventListener("click", this.close);
419
- this.cardRevealClose.removeEventListener("keypress", this._handleKeypressCloseEvent);
420
- };
421
- this._handleKeypressCloseEvent = (e) => {
422
- if (e.key === Utils.keys.ENTER) {
423
- this.close();
424
- }
425
- };
426
441
  /**
427
442
  * Show card reveal.
428
443
  */
429
444
  this.open = () => {
430
445
  if (this.isOpen || !this.cardReveal) return;
431
446
  this.isOpen = true;
432
- this.cardReveal.ariaExpanded = "true";
433
- this._activators.forEach((el) => el.ariaExpanded = "true");
434
- if (this.cardRevealClose) this.cardRevealClose.tabIndex = 0;
447
+ this.cardReveal.classList.add("open");
448
+ this.cardReveal.inert = false;
449
+ this._activators.forEach((activator) => activator.ariaExpanded = "true");
435
450
  if (typeof this.options.onOpen === "function") {
436
- this.options.onOpen.call(this);
451
+ this.options.onOpen.call(this, this.el);
437
452
  }
438
- if (this.cardRevealClose) this._setupRevealCloseEventHandlers();
439
453
  };
440
454
  /**
441
455
  * Hide card reveal.
442
456
  */
443
457
  this.close = () => {
444
458
  if (!this.isOpen || !this.cardReveal) return;
459
+ const focusWasInside = this.cardReveal.contains(document.activeElement);
445
460
  this.isOpen = false;
446
- this.cardReveal.ariaExpanded = "false";
447
- this._activators.forEach((el) => el.ariaExpanded = "false");
448
- if (this.cardRevealClose) this.cardRevealClose.tabIndex = -1;
461
+ this.cardReveal.classList.remove("open");
462
+ this.cardReveal.inert = true;
463
+ this._activators.forEach((activator) => activator.ariaExpanded = "false");
449
464
  if (typeof this.options.onClose === "function") {
450
- this.options.onClose.call(this);
465
+ this.options.onClose.call(this, this.el);
466
+ }
467
+ if (focusWasInside) {
468
+ (this._lastActivator ?? this._activators[0])?.focus();
451
469
  }
452
- if (this.cardRevealClose) this._removeRevealCloseEventHandlers();
453
470
  };
454
471
  this.el["Expressive_Cards"] = this;
455
472
  this.options = {
456
473
  ..._Cards.defaults,
457
474
  ...options
458
475
  };
459
- this._activators = [];
460
- this.cardReveal = this.el.querySelector(":scope > aside");
476
+ const cardReveals = this.el.querySelectorAll(":scope > aside");
477
+ const cardReveal = cardReveals.length === 1 ? cardReveals[0] : null;
478
+ const activators = cardReveal ? Array.from(
479
+ this.el.querySelectorAll(
480
+ 'button.card-reveal-trigger[type="button"]'
481
+ )
482
+ ).filter(
483
+ (activator) => isUsableCardTrigger(activator, this.el, cardReveal)
484
+ ) : [];
485
+ this.cardReveal = cardReveal?.id && activators.length > 0 ? cardReveal : null;
486
+ this._activators = this.cardReveal ? activators : [];
461
487
  if (this.cardReveal) {
462
- this._activators = Array.from(
463
- this.el.querySelectorAll(".activator")
464
- ).filter(
465
- (activator) => !this.cardReveal.contains(activator)
466
- );
467
- this._activators.forEach((el2) => {
468
- if (!el2) return;
469
- el2.tabIndex = 0;
470
- el2.ariaExpanded = "false";
471
- if (!el2.matches("a[href], button, input, select, textarea")) {
472
- el2.setAttribute("role", "button");
473
- }
488
+ this._ownsRevealInitializedClass = !this.el.classList.contains(REVEAL_INITIALIZED_CLASS);
489
+ this.el.classList.add(REVEAL_INITIALIZED_CLASS);
490
+ this._activators.forEach((activator) => {
491
+ this._initialAriaControls.set(activator, activator.getAttribute("aria-controls"));
492
+ activator.ariaExpanded = "false";
493
+ activator.setAttribute("aria-controls", this.cardReveal.id);
474
494
  });
475
- this.cardRevealClose = this.cardReveal.querySelector(
476
- ":scope > :is(h1, h2, h3, h4, h5, h6)"
477
- );
478
- if (this.cardRevealClose) this.cardRevealClose.tabIndex = -1;
479
- this.cardReveal.ariaExpanded = "false";
495
+ this.cardReveal.classList.remove("open");
496
+ this.cardReveal.inert = true;
480
497
  this._setupEventHandlers();
481
498
  }
482
499
  }
@@ -499,13 +516,28 @@ var Cards = class _Cards extends Component {
499
516
  */
500
517
  destroy() {
501
518
  this._removeEventHandlers();
502
- if (this.cardRevealClose) this._removeRevealCloseEventHandlers();
519
+ if (this.cardReveal) {
520
+ this.cardReveal.classList.remove("open");
521
+ this.cardReveal.inert = false;
522
+ if (this._ownsRevealInitializedClass) this.el.classList.remove(REVEAL_INITIALIZED_CLASS);
523
+ }
524
+ this._activators.forEach((activator) => {
525
+ activator.removeAttribute("aria-expanded");
526
+ const initialAriaControls = this._initialAriaControls.get(activator);
527
+ if (initialAriaControls === null) activator.removeAttribute("aria-controls");
528
+ else if (initialAriaControls !== void 0) {
529
+ activator.setAttribute("aria-controls", initialAriaControls);
530
+ }
531
+ });
532
+ this.isOpen = false;
503
533
  this._activators = [];
534
+ this._initialAriaControls.clear();
535
+ this._lastActivator = null;
504
536
  this.el["Expressive_Cards"] = void 0;
505
537
  }
506
538
  static Init() {
507
539
  Utils.onDocumentReady(() => {
508
- const cards = document.querySelectorAll(CARDS_SELECTOR);
540
+ const cards = Array.from(document.querySelectorAll(CARDS_SELECTOR)).filter(isCardReveal);
509
541
  cards.forEach((el) => {
510
542
  if (el && el["Expressive_Cards"] == void 0) this.init(el);
511
543
  });
@@ -518,13 +550,12 @@ var _defaults2 = {
518
550
  onOpen: null,
519
551
  onClose: null
520
552
  };
521
- var MOTION_DURATION = 500;
522
553
  var EXPANDING_CARD_SELECTOR = "article.expanding-card:has(> dialog.expanding-card-dialog)";
523
554
  var ExpandingCard = class _ExpandingCard extends Component {
524
555
  constructor(el, options) {
525
556
  super(el, options, _ExpandingCard);
526
557
  this.isOpen = false;
527
- this.closeTimer = null;
558
+ this.closeGeneration = 0;
528
559
  this._setupEventHandlers = () => {
529
560
  this.trigger?.addEventListener("click", this.open);
530
561
  this.trigger?.addEventListener("keypress", this._handleTriggerKeypress);
@@ -550,7 +581,8 @@ var ExpandingCard = class _ExpandingCard extends Component {
550
581
  this.close();
551
582
  };
552
583
  this._handleNativeClose = () => {
553
- if (!this.isOpen) return;
584
+ if (this.dialog.open) return;
585
+ this.closeGeneration++;
554
586
  this.isOpen = false;
555
587
  this.trigger.ariaExpanded = "false";
556
588
  this.dialog.ariaExpanded = "false";
@@ -586,10 +618,7 @@ var ExpandingCard = class _ExpandingCard extends Component {
586
618
  };
587
619
  this.open = () => {
588
620
  if (this.isOpen || !this.dialog || !this.trigger) return;
589
- if (this.closeTimer !== null) {
590
- window.clearTimeout(this.closeTimer);
591
- this.closeTimer = null;
592
- }
621
+ this.closeGeneration++;
593
622
  this._setTransitionOrigin();
594
623
  this.isOpen = true;
595
624
  this.trigger.ariaExpanded = "true";
@@ -604,6 +633,7 @@ var ExpandingCard = class _ExpandingCard extends Component {
604
633
  };
605
634
  this.close = () => {
606
635
  if (!this.isOpen || !this.dialog || !this.trigger) return;
636
+ const generation = ++this.closeGeneration;
607
637
  this._setTransitionOrigin();
608
638
  this.isOpen = false;
609
639
  this.trigger.ariaExpanded = "false";
@@ -613,15 +643,16 @@ var ExpandingCard = class _ExpandingCard extends Component {
613
643
  this.options.onClose.call(this, this.el);
614
644
  }
615
645
  const finish = () => {
616
- this.closeTimer = null;
617
- if (this.dialog.open) this.dialog.close();
618
- this.trigger.focus();
646
+ if (generation !== this.closeGeneration || this.isOpen || !this.dialog.open) return;
647
+ this.dialog.close();
648
+ if (this.dialog.isConnected && this.trigger.isConnected) this.trigger.focus();
619
649
  };
620
650
  const reducedMotion = window.matchMedia(
621
651
  "(prefers-reduced-motion: reduce)"
622
652
  ).matches;
623
- if (reducedMotion) finish();
624
- else this.closeTimer = window.setTimeout(finish, MOTION_DURATION);
653
+ const transitions = reducedMotion ? [] : (this.dialog.getAnimations?.() ?? []).filter((animation) => animation.transitionProperty === "clip-path");
654
+ if (transitions.length === 0) finish();
655
+ else void Promise.allSettled(transitions.map((animation) => animation.finished)).then(finish);
625
656
  };
626
657
  this.el["Expressive_ExpandingCard"] = this;
627
658
  this.options = {
@@ -658,7 +689,10 @@ var ExpandingCard = class _ExpandingCard extends Component {
658
689
  }
659
690
  destroy() {
660
691
  this._removeEventHandlers();
661
- if (this.closeTimer !== null) window.clearTimeout(this.closeTimer);
692
+ this.closeGeneration++;
693
+ this.isOpen = false;
694
+ if (this.trigger) this.trigger.ariaExpanded = "false";
695
+ if (this.dialog) this.dialog.ariaExpanded = "false";
662
696
  this.dialog?.classList.remove("expanded");
663
697
  if (this.dialog?.open) this.dialog.close();
664
698
  this.el["Expressive_ExpandingCard"] = void 0;
@@ -697,6 +731,8 @@ var SPACE = " ";
697
731
  var _Menu = class _Menu extends Component {
698
732
  constructor(el, options) {
699
733
  super(el, options, _Menu);
734
+ this._pendingTimers = /* @__PURE__ */ new Set();
735
+ this._transition = 0;
700
736
  this._handleClick = (e) => {
701
737
  e.preventDefault();
702
738
  if (this.isOpen) {
@@ -730,7 +766,7 @@ var _Menu = class _Menu extends Component {
730
766
  if (this.options.closeOnClick && this.menuEl?.contains(target) && !this.isTouchMoving) {
731
767
  this.close();
732
768
  } else if (!this.menuEl?.contains(target)) {
733
- setTimeout(() => {
769
+ this._schedule(() => {
734
770
  if (this.isOpen) {
735
771
  this.close();
736
772
  }
@@ -884,16 +920,18 @@ var _Menu = class _Menu extends Component {
884
920
  * Open menu.
885
921
  */
886
922
  this.open = () => {
887
- if (this.isOpen) return;
923
+ if (this.isOpen || this.el["Expressive_Menu"] !== this) return;
924
+ const transition = this._clearPendingTimers();
888
925
  this.isOpen = true;
889
926
  if (typeof this.options.onOpenStart === "function") {
890
927
  this.options.onOpenStart.call(this, this.el);
891
928
  }
929
+ if (transition !== this._transition) return;
892
930
  this._resetMenuStyles();
893
931
  this.menuEl.style.display = "block";
894
932
  this._placeMenu();
933
+ this._schedule(() => this._setupTemporaryEventHandlers(), 0);
895
934
  this._animateIn();
896
- setTimeout(() => this._setupTemporaryEventHandlers(), 0);
897
935
  this.el.ariaExpanded = "true";
898
936
  };
899
937
  /**
@@ -901,18 +939,20 @@ var _Menu = class _Menu extends Component {
901
939
  */
902
940
  this.close = () => {
903
941
  if (!this.isOpen) return;
942
+ const transition = this._clearPendingTimers();
904
943
  this.isOpen = false;
905
944
  this.focusedIndex = -1;
906
945
  this._closeSubmenus();
907
946
  if (typeof this.options.onCloseStart === "function") {
908
947
  this.options.onCloseStart.call(this, this.el);
909
948
  }
949
+ if (transition !== this._transition) return;
910
950
  this._animateOut();
911
951
  this._removeTemporaryEventHandlers();
952
+ this.el.ariaExpanded = "false";
912
953
  if (this.options.autoFocus) {
913
954
  this.el.focus();
914
955
  }
915
- this.el.ariaExpanded = "false";
916
956
  };
917
957
  /**
918
958
  * While menu is open, you can recalculate its dimensions if its contents have changed.
@@ -961,6 +1001,9 @@ var _Menu = class _Menu extends Component {
961
1001
  return el["Expressive_Menu"];
962
1002
  }
963
1003
  destroy() {
1004
+ this._clearPendingTimers();
1005
+ this.isOpen = false;
1006
+ this.el.ariaExpanded = "false";
964
1007
  clearTimeout(this.filterTimeout);
965
1008
  this._resetMenuStyles();
966
1009
  this._removeEventHandlers();
@@ -1223,18 +1266,32 @@ var _Menu = class _Menu extends Component {
1223
1266
  width: idealWidth
1224
1267
  };
1225
1268
  }
1269
+ _schedule(callback, delay) {
1270
+ const timer = setTimeout(() => {
1271
+ this._pendingTimers.delete(timer);
1272
+ callback();
1273
+ }, delay);
1274
+ this._pendingTimers.add(timer);
1275
+ }
1276
+ _clearPendingTimers() {
1277
+ for (const timer of this._pendingTimers) clearTimeout(timer);
1278
+ this._pendingTimers.clear();
1279
+ return ++this._transition;
1280
+ }
1226
1281
  _animateIn() {
1282
+ const transition = this._transition;
1227
1283
  const duration = this.options.inDuration;
1228
1284
  this.menuEl.style.transition = "none";
1229
1285
  this.menuEl.style.opacity = "0";
1230
1286
  this.menuEl.style.transform = "scale(0.85)";
1231
- setTimeout(() => {
1287
+ this._schedule(() => {
1232
1288
  this.menuEl.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
1233
1289
  this.menuEl.style.opacity = "1";
1234
1290
  this.menuEl.style.transform = "scale(1)";
1235
1291
  }, 1);
1236
- setTimeout(() => {
1292
+ this._schedule(() => {
1237
1293
  this._focusInitialItem();
1294
+ if (transition !== this._transition) return;
1238
1295
  if (typeof this.options.onOpenEnd === "function") this.options.onOpenEnd.call(this, this.el);
1239
1296
  }, duration);
1240
1297
  }
@@ -1243,7 +1300,7 @@ var _Menu = class _Menu extends Component {
1243
1300
  this.menuEl.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
1244
1301
  this.menuEl.style.opacity = "0";
1245
1302
  this.menuEl.style.transform = "scale(0.85)";
1246
- setTimeout(() => {
1303
+ this._schedule(() => {
1247
1304
  this._resetMenuStyles();
1248
1305
  if (typeof this.options.onCloseEnd === "function")
1249
1306
  this.options.onCloseEnd.call(this, this.el);
@@ -2512,8 +2569,89 @@ var Forms = class _Forms {
2512
2569
  }
2513
2570
  };
2514
2571
 
2572
+ // src/ts/components/appBar.ts
2573
+ var _defaults7 = {};
2574
+ var AppBar = class _AppBar extends Component {
2575
+ constructor(el, options) {
2576
+ super(el, options, _AppBar);
2577
+ this._observer = null;
2578
+ this._sentinel = null;
2579
+ this._input = null;
2580
+ this._view = null;
2581
+ this._suppressSearchOpen = false;
2582
+ this._onIntersect = (entries) => {
2583
+ const entry = entries[0];
2584
+ if (!entry) return;
2585
+ this.el.classList.toggle("collapsed", !entry.isIntersecting);
2586
+ };
2587
+ this._onSearchViewClose = () => {
2588
+ this._suppressSearchOpen = true;
2589
+ };
2590
+ this._onSearchFocus = () => {
2591
+ if (!this._view) return;
2592
+ if (this._suppressSearchOpen) {
2593
+ this._suppressSearchOpen = false;
2594
+ return;
2595
+ }
2596
+ if (this._view instanceof HTMLDialogElement) {
2597
+ if (!this._view.open) this._view.showModal();
2598
+ return;
2599
+ }
2600
+ this._view.hidden = false;
2601
+ this._input?.setAttribute("aria-expanded", "true");
2602
+ };
2603
+ this.el["Expressive_AppBar"] = this;
2604
+ this.options = { ..._AppBar.defaults, ...options };
2605
+ this._setupCollapse();
2606
+ this._setupSearch();
2607
+ }
2608
+ static get defaults() {
2609
+ return _defaults7;
2610
+ }
2611
+ static init(els, options = {}) {
2612
+ return super.init(els, options, _AppBar);
2613
+ }
2614
+ static getInstance(el) {
2615
+ return el["Expressive_AppBar"];
2616
+ }
2617
+ destroy() {
2618
+ this._observer?.disconnect();
2619
+ this._observer = null;
2620
+ this._sentinel?.remove();
2621
+ this._sentinel = null;
2622
+ this._input?.removeEventListener("focus", this._onSearchFocus);
2623
+ this._view?.removeEventListener("close", this._onSearchViewClose);
2624
+ this.el.classList.remove("collapsed");
2625
+ this.el["Expressive_AppBar"] = void 0;
2626
+ }
2627
+ _setupCollapse() {
2628
+ if (!this.el.classList.contains("medium") && !this.el.classList.contains("large")) {
2629
+ return;
2630
+ }
2631
+ if (typeof IntersectionObserver === "undefined") return;
2632
+ this._sentinel = document.createElement("span");
2633
+ this._sentinel.setAttribute("aria-hidden", "true");
2634
+ this._sentinel.style.cssText = "display:block;width:100%;height:1px;margin-top:-1px;pointer-events:none;visibility:hidden";
2635
+ this.el.insertAdjacentElement("beforebegin", this._sentinel);
2636
+ this._observer = new IntersectionObserver(this._onIntersect);
2637
+ this._observer.observe(this._sentinel);
2638
+ }
2639
+ _setupSearch() {
2640
+ this._input = this.el.querySelector('input[type="search"]');
2641
+ if (!this._input) return;
2642
+ const id = this._input.getAttribute("aria-controls");
2643
+ if (id) this._view = document.getElementById(id);
2644
+ if (!this._view) this._view = this.el.querySelector(".search-view");
2645
+ if (!this._view) return;
2646
+ this._input.addEventListener("focus", this._onSearchFocus);
2647
+ if (this._view instanceof HTMLDialogElement) {
2648
+ this._view.addEventListener("close", this._onSearchViewClose);
2649
+ }
2650
+ }
2651
+ };
2652
+
2515
2653
  // src/ts/components/buttons.ts
2516
- var _defaults7 = {
2654
+ var _defaults8 = {
2517
2655
  direction: "top",
2518
2656
  hoverEnabled: true,
2519
2657
  toolbarEnabled: false
@@ -2586,7 +2724,7 @@ var FloatingActionButton = class _FloatingActionButton extends Component {
2586
2724
  this._setupEventHandlers();
2587
2725
  }
2588
2726
  static get defaults() {
2589
- return _defaults7;
2727
+ return _defaults8;
2590
2728
  }
2591
2729
  static init(els, options = {}) {
2592
2730
  return super.init(els, options, _FloatingActionButton);
@@ -2623,8 +2761,226 @@ var FloatingActionButton = class _FloatingActionButton extends Component {
2623
2761
  }
2624
2762
  };
2625
2763
 
2764
+ // src/ts/components/buttonGroup.ts
2765
+ var _defaults9 = {};
2766
+ var VALID_SELECTION_MODES = /* @__PURE__ */ new Set(["single", "multiple"]);
2767
+ var PRESSED_CLASS = "button-group-pressed";
2768
+ var MAX_ICON_COMPRESSION = 0.15;
2769
+ var ButtonGroup = class _ButtonGroup extends Component {
2770
+ constructor(el, options = {}) {
2771
+ super(el, options, _ButtonGroup);
2772
+ this._pressedStyles = /* @__PURE__ */ new Map();
2773
+ this._releaseStyles = /* @__PURE__ */ new Map();
2774
+ this._releaseTimer = null;
2775
+ this._originalPressed = /* @__PURE__ */ new Map();
2776
+ this._activeItem = null;
2777
+ this._handleClick = (event) => {
2778
+ if (!this._selectionMode) return;
2779
+ const item = this._itemFromEvent(event);
2780
+ if (!(item instanceof HTMLButtonElement) || this._isDisabled(item)) return;
2781
+ this._rememberPressed(item);
2782
+ const willSelect = item.getAttribute("aria-pressed") !== "true";
2783
+ const selectedCount = this._toggleItems.filter(
2784
+ (button) => button.getAttribute("aria-pressed") === "true"
2785
+ ).length;
2786
+ if (!willSelect && this.el.hasAttribute("data-selection-required") && selectedCount === 1) {
2787
+ return;
2788
+ }
2789
+ if (this._selectionMode === "single" && willSelect) {
2790
+ this._toggleItems.forEach((button) => {
2791
+ this._rememberPressed(button);
2792
+ button.setAttribute("aria-pressed", "false");
2793
+ });
2794
+ }
2795
+ item.setAttribute("aria-pressed", willSelect ? "true" : "false");
2796
+ };
2797
+ this._handlePointerDown = (event) => {
2798
+ if (event.button !== 0) return;
2799
+ const item = this._itemFromEvent(event);
2800
+ if (item && !this._isDisabled(item)) this._startPress(item);
2801
+ };
2802
+ this._handleKeyDown = (event) => {
2803
+ if (event.repeat || event.key !== " " && event.key !== "Enter") return;
2804
+ const item = this._itemFromEvent(event);
2805
+ if (item && !this._isDisabled(item)) this._startPress(item);
2806
+ };
2807
+ this._handleKeyUp = (event) => {
2808
+ if (event.key === " " || event.key === "Enter") this._resetPress();
2809
+ };
2810
+ this._handleFocusOut = (event) => {
2811
+ if (event.target === this._activeItem) this._resetPress();
2812
+ };
2813
+ this._resetPress = () => {
2814
+ this._activeItem?.classList.remove(PRESSED_CLASS);
2815
+ this._activeItem = null;
2816
+ if (!this._pressedStyles.size) return;
2817
+ this._finishRelease();
2818
+ this._releaseStyles = new Map(this._pressedStyles);
2819
+ for (const [item, style] of this._releaseStyles) {
2820
+ item.style.width = `${style.baseWidth}px`;
2821
+ }
2822
+ this._pressedStyles.clear();
2823
+ this._releaseTimer = window.setTimeout(this._finishRelease, 200);
2824
+ };
2825
+ this._finishRelease = () => {
2826
+ if (this._releaseTimer !== null) window.clearTimeout(this._releaseTimer);
2827
+ this._releaseTimer = null;
2828
+ for (const [item, style] of this._releaseStyles) item.style.width = style.width;
2829
+ this._releaseStyles.clear();
2830
+ };
2831
+ this.el["Expressive_ButtonGroup"] = this;
2832
+ this.options = { ..._ButtonGroup.defaults, ...options };
2833
+ if (this._selectionMode) {
2834
+ this._normalizeSelection();
2835
+ this.el.addEventListener("click", this._handleClick, true);
2836
+ }
2837
+ this.el.addEventListener("pointerdown", this._handlePointerDown);
2838
+ this.el.addEventListener("keydown", this._handleKeyDown);
2839
+ this.el.addEventListener("focusout", this._handleFocusOut);
2840
+ document.addEventListener("pointerup", this._resetPress);
2841
+ document.addEventListener("pointercancel", this._resetPress);
2842
+ document.addEventListener("keyup", this._handleKeyUp);
2843
+ window.addEventListener("blur", this._resetPress);
2844
+ }
2845
+ static get defaults() {
2846
+ return _defaults9;
2847
+ }
2848
+ static init(els, options = {}) {
2849
+ return super.init(els, options, _ButtonGroup);
2850
+ }
2851
+ static getInstance(el) {
2852
+ return el["Expressive_ButtonGroup"];
2853
+ }
2854
+ destroy() {
2855
+ this._resetPress();
2856
+ this._finishRelease();
2857
+ this.el.removeEventListener("click", this._handleClick, true);
2858
+ this.el.removeEventListener("pointerdown", this._handlePointerDown);
2859
+ this.el.removeEventListener("keydown", this._handleKeyDown);
2860
+ this.el.removeEventListener("focusout", this._handleFocusOut);
2861
+ document.removeEventListener("pointerup", this._resetPress);
2862
+ document.removeEventListener("pointercancel", this._resetPress);
2863
+ document.removeEventListener("keyup", this._handleKeyUp);
2864
+ window.removeEventListener("blur", this._resetPress);
2865
+ for (const [item, value] of this._originalPressed) {
2866
+ if (value === null) item.removeAttribute("aria-pressed");
2867
+ else item.setAttribute("aria-pressed", value);
2868
+ }
2869
+ this._originalPressed.clear();
2870
+ this.el["Expressive_ButtonGroup"] = void 0;
2871
+ }
2872
+ get _selectionMode() {
2873
+ const mode = this.el.dataset.selection;
2874
+ return VALID_SELECTION_MODES.has(mode) ? mode : null;
2875
+ }
2876
+ get _items() {
2877
+ return Array.from(this.el.children).filter(
2878
+ (child) => child instanceof HTMLButtonElement || child instanceof HTMLAnchorElement && child.classList.contains("button")
2879
+ );
2880
+ }
2881
+ get _toggleItems() {
2882
+ return this._items.filter(
2883
+ (item) => item instanceof HTMLButtonElement
2884
+ );
2885
+ }
2886
+ _normalizeSelection() {
2887
+ const items = this._toggleItems;
2888
+ items.forEach((item) => {
2889
+ this._rememberPressed(item);
2890
+ if (!["true", "false"].includes(item.getAttribute("aria-pressed") || "")) {
2891
+ item.setAttribute("aria-pressed", "false");
2892
+ }
2893
+ });
2894
+ if (this._selectionMode === "single") {
2895
+ const selected = items.filter((item) => item.getAttribute("aria-pressed") === "true");
2896
+ selected.slice(1).forEach((item) => item.setAttribute("aria-pressed", "false"));
2897
+ }
2898
+ if (this.el.hasAttribute("data-selection-required") && !items.some((item) => item.getAttribute("aria-pressed") === "true")) {
2899
+ items.find((item) => !this._isDisabled(item))?.setAttribute("aria-pressed", "true");
2900
+ }
2901
+ }
2902
+ _itemFromEvent(event) {
2903
+ if (!(event.target instanceof Element)) return null;
2904
+ const item = event.target.closest("button, a.button");
2905
+ return item instanceof HTMLElement && item.parentElement === this.el ? item : null;
2906
+ }
2907
+ _isDisabled(item) {
2908
+ return item instanceof HTMLButtonElement && item.disabled || item.getAttribute("aria-disabled") === "true" || item.classList.contains("disabled");
2909
+ }
2910
+ _rememberPressed(item) {
2911
+ if (!this._originalPressed.has(item)) {
2912
+ this._originalPressed.set(item, item.getAttribute("aria-pressed"));
2913
+ }
2914
+ }
2915
+ _startPress(item) {
2916
+ if (this._activeItem) return;
2917
+ this._finishRelease();
2918
+ const items = this._items;
2919
+ const activeIndex = items.indexOf(item);
2920
+ if (activeIndex < 0) return;
2921
+ this._activeItem = item;
2922
+ item.classList.add(PRESSED_CLASS);
2923
+ if (this.el.classList.contains("connected") || items.length < 2) return;
2924
+ const rects = items.map((candidate) => candidate.getBoundingClientRect());
2925
+ const activeWidth = rects[activeIndex].width;
2926
+ if (activeWidth <= 0) return;
2927
+ const parsedMultiplier = Number.parseFloat(
2928
+ getComputedStyle(this.el).getPropertyValue(
2929
+ "--md-comp-button-group-pressed-item-width-multiplier"
2930
+ )
2931
+ );
2932
+ const multiplier = Number.isFinite(parsedMultiplier) ? parsedMultiplier : 0.15;
2933
+ const desiredGrowth = activeWidth * multiplier;
2934
+ const neighborIndexes = [activeIndex - 1, activeIndex + 1].filter(
2935
+ (index) => index >= 0 && index < items.length
2936
+ );
2937
+ const compression = /* @__PURE__ */ new Map();
2938
+ if (neighborIndexes.length === 1) {
2939
+ const index = neighborIndexes[0];
2940
+ compression.set(
2941
+ index,
2942
+ Math.min(desiredGrowth, this._compressionCapacity(items[index], rects[index]))
2943
+ );
2944
+ } else {
2945
+ const half = desiredGrowth / 2;
2946
+ for (const index of neighborIndexes) {
2947
+ compression.set(
2948
+ index,
2949
+ Math.min(half, this._compressionCapacity(items[index], rects[index]))
2950
+ );
2951
+ }
2952
+ let remaining = desiredGrowth - [...compression.values()].reduce((sum, value) => sum + value, 0);
2953
+ for (const index of neighborIndexes) {
2954
+ if (remaining <= 0) break;
2955
+ const capacity = this._compressionCapacity(items[index], rects[index]) - compression.get(index);
2956
+ const extra = Math.min(remaining, capacity);
2957
+ compression.set(index, compression.get(index) + extra);
2958
+ remaining -= extra;
2959
+ }
2960
+ }
2961
+ const actualGrowth = [...compression.values()].reduce((sum, value) => sum + value, 0);
2962
+ if (actualGrowth <= 0) return;
2963
+ items.forEach((candidate, index) => {
2964
+ this._pressedStyles.set(candidate, {
2965
+ width: candidate.style.width,
2966
+ baseWidth: rects[index].width
2967
+ });
2968
+ candidate.style.width = `${rects[index].width}px`;
2969
+ });
2970
+ void this.el.offsetWidth;
2971
+ items.forEach((candidate, index) => {
2972
+ const adjustment = index === activeIndex ? actualGrowth : -(compression.get(index) || 0);
2973
+ candidate.style.width = `${rects[index].width + adjustment}px`;
2974
+ });
2975
+ }
2976
+ _compressionCapacity(item, rect) {
2977
+ const minimumWidth = item.classList.contains("icon-button") ? rect.width * (1 - MAX_ICON_COMPRESSION) : rect.height;
2978
+ return Math.max(0, rect.width - minimumWidth);
2979
+ }
2980
+ };
2981
+
2626
2982
  // src/ts/components/carousel.ts
2627
- var _defaults8 = {
2983
+ var _defaults10 = {
2628
2984
  duration: 200,
2629
2985
  fullWidth: false,
2630
2986
  indicators: false,
@@ -2867,7 +3223,7 @@ var _Carousel = class _Carousel extends Component {
2867
3223
  return this._trackEl ?? this.el;
2868
3224
  }
2869
3225
  static get defaults() {
2870
- return _defaults8;
3226
+ return _defaults10;
2871
3227
  }
2872
3228
  static init(els, options = {}) {
2873
3229
  return super.init(els, options, _Carousel);
@@ -3263,7 +3619,7 @@ _Carousel._onResize = Utils.throttle(() => {
3263
3619
  var Carousel = _Carousel;
3264
3620
 
3265
3621
  // src/ts/components/characterCounter.ts
3266
- var _defaults9 = Object.freeze({});
3622
+ var _defaults11 = Object.freeze({});
3267
3623
  var CharacterCounter = class _CharacterCounter extends Component {
3268
3624
  constructor(el, options) {
3269
3625
  super(el, {}, _CharacterCounter);
@@ -3288,7 +3644,7 @@ var CharacterCounter = class _CharacterCounter extends Component {
3288
3644
  this._setupEventHandlers();
3289
3645
  }
3290
3646
  static get defaults() {
3291
- return _defaults9;
3647
+ return _defaults11;
3292
3648
  }
3293
3649
  /**
3294
3650
  * Initializes instances of CharacterCounter.
@@ -3340,7 +3696,7 @@ var CharacterCounter = class _CharacterCounter extends Component {
3340
3696
  };
3341
3697
 
3342
3698
  // src/ts/components/select.ts
3343
- var _defaults10 = {
3699
+ var _defaults12 = {
3344
3700
  classes: "",
3345
3701
  menuOptions: {}
3346
3702
  };
@@ -3389,7 +3745,7 @@ var FormSelect = class _FormSelect extends Component {
3389
3745
  this._setupEventHandlers();
3390
3746
  }
3391
3747
  static get defaults() {
3392
- return _defaults10;
3748
+ return _defaults12;
3393
3749
  }
3394
3750
  /**
3395
3751
  * Initializes instances of FormSelect.
@@ -3520,7 +3876,7 @@ var FormSelect = class _FormSelect extends Component {
3520
3876
  _buildInput() {
3521
3877
  this.input = document.createElement("input");
3522
3878
  this.input.id = "select-input-" + Utils.guid();
3523
- this.input.classList.add("menu-trigger");
3879
+ this.input.classList.add("menu-trigger", "no-autoinit");
3524
3880
  this.input.type = "text";
3525
3881
  this.input.readOnly = true;
3526
3882
  this.input.setAttribute("data-target", this.menuEl.id);
@@ -3675,11 +4031,21 @@ var FormSelect = class _FormSelect extends Component {
3675
4031
  }
3676
4032
  li.appendChild(span);
3677
4033
  const iconUrl = realOption.getAttribute("data-icon");
3678
- const classes = realOption.getAttribute("class")?.split(" ").filter(Boolean);
4034
+ let imageUrl;
3679
4035
  if (iconUrl) {
4036
+ try {
4037
+ const url = new URL(iconUrl, this.el.ownerDocument.baseURI);
4038
+ if (["http:", "https:", "blob:"].includes(url.protocol) || /^data:image\/(?:avif|bmp|gif|jpeg|png|svg\+xml|webp|x-icon)(?:;|,)/i.test(url.href)) {
4039
+ imageUrl = url;
4040
+ }
4041
+ } catch {
4042
+ }
4043
+ }
4044
+ const classes = realOption.getAttribute("class")?.split(" ").filter(Boolean);
4045
+ if (imageUrl) {
3680
4046
  const img = document.createElement("img");
3681
4047
  if (classes?.length) img.classList.add(...classes);
3682
- img.src = iconUrl;
4048
+ img.src = imageUrl.href;
3683
4049
  img.ariaHidden = "true";
3684
4050
  li.prepend(img);
3685
4051
  }
@@ -3761,7 +4127,7 @@ var FormSelect = class _FormSelect extends Component {
3761
4127
  };
3762
4128
 
3763
4129
  // src/ts/plugins/dockedDisplayPlugin.ts
3764
- var _defaults11 = {
4130
+ var _defaults13 = {
3765
4131
  margin: 5,
3766
4132
  transition: 10,
3767
4133
  duration: 250,
@@ -3810,7 +4176,7 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
3810
4176
  };
3811
4177
  this.el = el;
3812
4178
  this.options = {
3813
- ..._defaults11,
4179
+ ..._defaults13,
3814
4180
  ...options
3815
4181
  };
3816
4182
  this.container = document.createElement("div");
@@ -3831,7 +4197,7 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
3831
4197
  };
3832
4198
 
3833
4199
  // src/ts/components/datepicker.ts
3834
- var _defaults12 = {
4200
+ var _defaults14 = {
3835
4201
  // the default output format for the input field value
3836
4202
  format: "mmm dd, yyyy",
3837
4203
  // Used to create date object from current input string
@@ -4096,7 +4462,7 @@ var _Datepicker = class _Datepicker extends Component {
4096
4462
  }
4097
4463
  }
4098
4464
  static get defaults() {
4099
- return _defaults12;
4465
+ return _defaults14;
4100
4466
  }
4101
4467
  /**
4102
4468
  * Initializes instances of Datepicker.
@@ -4510,13 +4876,18 @@ var _Datepicker = class _Datepicker extends Component {
4510
4876
  arr.push(className);
4511
4877
  }
4512
4878
  }
4513
- return `<td data-day="${opts.day}" class="${arr.join(" ")}" aria-selected="${ariaSelected}"><button class="datepicker-day-button" type="button" data-year="${opts.year}" data-month="${opts.month}" data-day="${opts.day}">${opts.day}</button></td>`;
4879
+ const day = _Datepicker._escape(opts.day);
4880
+ const month = _Datepicker._escape(opts.month);
4881
+ const year = _Datepicker._escape(opts.year);
4882
+ return `<td data-day="${day}" class="${arr.join(" ")}" aria-selected="${ariaSelected}"><button class="datepicker-day-button" type="button" data-year="${year}" data-month="${month}" data-day="${day}">${day}</button></td>`;
4514
4883
  }
4884
+ /** Assemble calendar cells; reject unsupported elements and attributes. */
4515
4885
  renderRow(days, isRTL, isRowSelected) {
4516
- return '<tr class="datepicker-row' + (isRowSelected ? " is-selected" : "") + '">' + (isRTL ? days.reverse() : days).join("") + "</tr>";
4886
+ return '<tr class="datepicker-row' + (isRowSelected ? " is-selected" : "") + '">' + _Datepicker._calendarMarkup((isRTL ? days.reverse() : days).join(""), "tr") + "</tr>";
4517
4887
  }
4888
+ /** Assemble calendar rows; reject unsupported elements and attributes. */
4518
4889
  renderTable(opts, data, randId) {
4519
- return '<div class="datepicker-table-wrapper"><table cellpadding="0" cellspacing="0" class="datepicker-table" role="grid" aria-labelledby="' + randId + '">' + this.renderHead(opts) + this.renderBody(data) + "</table></div>";
4890
+ return '<div class="datepicker-table-wrapper"><table cellpadding="0" cellspacing="0" class="datepicker-table" role="grid" aria-labelledby="' + _Datepicker._escape(randId) + '">' + this.renderHead(opts) + this.renderBody(data) + "</table></div>";
4520
4891
  }
4521
4892
  renderHead(opts) {
4522
4893
  const arr = [];
@@ -4528,8 +4899,40 @@ var _Datepicker = class _Datepicker extends Component {
4528
4899
  }
4529
4900
  return "<thead><tr>" + (opts.isRTL ? arr.reverse() : arr).join("") + "</tr></thead>";
4530
4901
  }
4902
+ /** Wrap calendar rows after validating and encoding their contents. */
4531
4903
  renderBody(rows) {
4532
- return "<tbody>" + rows.join("") + "</tbody>";
4904
+ return "<tbody>" + _Datepicker._calendarMarkup(rows.join(""), "tbody") + "</tbody>";
4905
+ }
4906
+ static _calendarMarkup(html, parent) {
4907
+ const template = document.createElement("template");
4908
+ template.innerHTML = html;
4909
+ const attributes = {
4910
+ tr: ["class"],
4911
+ td: ["class", "data-day", "aria-selected"],
4912
+ button: ["class", "type", "data-year", "data-month", "data-day", "aria-label", "disabled", "tabindex"]
4913
+ };
4914
+ const render = (node, container) => {
4915
+ if (node.nodeType === Node.TEXT_NODE) {
4916
+ if (container === "tbody" || container === "tr") {
4917
+ if (node.textContent.trim()) throw new TypeError("Unsupported calendar row content");
4918
+ }
4919
+ return _Datepicker._escape(node.textContent);
4920
+ }
4921
+ if (!(node instanceof HTMLElement)) throw new TypeError("Unsupported calendar row content");
4922
+ const tag = node.localName === "tr" ? "tr" : node.localName === "td" ? "td" : node.localName === "button" ? "button" : null;
4923
+ if (!tag || !(container === "tbody" && tag === "tr" || container === "tr" && tag === "td" || container === "td" && tag === "button")) {
4924
+ throw new TypeError("Unsupported calendar row element");
4925
+ }
4926
+ if ([...node.attributes].some((attr) => !attributes[tag].includes(attr.name))) {
4927
+ throw new TypeError("Unsupported calendar row attribute");
4928
+ }
4929
+ if (tag === "button" && node.getAttribute("type") !== "button") {
4930
+ throw new TypeError('Calendar day buttons require type="button"');
4931
+ }
4932
+ const attrs = attributes[tag].filter((name) => node.hasAttribute(name)).map((name) => ` ${name}="${_Datepicker._escape(node.getAttribute(name))}"`).join("");
4933
+ return `<${tag}${attrs}>${[...node.childNodes].map((child) => render(child, tag)).join("")}</${tag}>`;
4934
+ };
4935
+ return [...template.content.childNodes].map((node) => render(node, parent)).join("");
4533
4936
  }
4534
4937
  /**
4535
4938
  * Whether each month arrow is usable at this position in the allowed range.
@@ -4569,7 +4972,7 @@ var _Datepicker = class _Datepicker extends Component {
4569
4972
  }
4570
4973
  renderTitle(instance, c, year, month, refYear, randId) {
4571
4974
  const opts = this.options, isMinYear = year === opts.minYear, isMaxYear = year === opts.maxYear;
4572
- let i, j, arr = [], html = '<div id="' + randId + '" class="datepicker-controls" role="heading" aria-live="assertive">';
4975
+ let i, j, arr = [], html = '<div id="' + _Datepicker._escape(randId) + '" class="datepicker-controls" role="heading" aria-live="assertive">';
4573
4976
  const { prev, next } = this._arrowState(year, month);
4574
4977
  for (i = 0; i < 12; i++) {
4575
4978
  arr.push(
@@ -4814,7 +5217,7 @@ _Datepicker._template = `
4814
5217
  var Datepicker = _Datepicker;
4815
5218
 
4816
5219
  // src/ts/components/lightbox.ts
4817
- var _defaults13 = {
5220
+ var _defaults15 = {
4818
5221
  inDuration: 275,
4819
5222
  outDuration: 200,
4820
5223
  onOpenStart: null,
@@ -4933,7 +5336,7 @@ var Lightbox = class _Lightbox extends Component {
4933
5336
  this._setupEventHandlers();
4934
5337
  }
4935
5338
  static get defaults() {
4936
- return _defaults13;
5339
+ return _defaults15;
4937
5340
  }
4938
5341
  /**
4939
5342
  * Initializes instances of Lightbox.
@@ -5099,7 +5502,7 @@ var Lightbox = class _Lightbox extends Component {
5099
5502
 
5100
5503
  // src/ts/components/scrollspy.ts
5101
5504
  var _defaultGetActiveElement = (id) => `a[href="#${id}"]`;
5102
- var _defaults14 = {
5505
+ var _defaults16 = {
5103
5506
  scrollOffset: 200,
5104
5507
  activeClass: "active",
5105
5508
  getActiveElement: _defaultGetActiveElement,
@@ -5122,7 +5525,7 @@ var _ScrollSpy = class _ScrollSpy extends Component {
5122
5525
  _ScrollSpy._observer?.observe(this.el);
5123
5526
  }
5124
5527
  static get defaults() {
5125
- return _defaults14;
5528
+ return _defaults16;
5126
5529
  }
5127
5530
  static init(els, options = {}) {
5128
5531
  return super.init(els, options, _ScrollSpy);
@@ -5244,7 +5647,7 @@ _ScrollSpy._onIntersect = (entries) => {
5244
5647
  var ScrollSpy = _ScrollSpy;
5245
5648
 
5246
5649
  // src/ts/components/navigationDrawer.ts
5247
- var _defaults15 = {
5650
+ var _defaults17 = {
5248
5651
  edge: "left",
5249
5652
  draggable: true,
5250
5653
  dragTargetWidth: "10px",
@@ -5387,7 +5790,7 @@ var _NavigationDrawer = class _NavigationDrawer extends Component {
5387
5790
  _NavigationDrawer._sidenavs.push(this);
5388
5791
  }
5389
5792
  static get defaults() {
5390
- return _defaults15;
5793
+ return _defaults17;
5391
5794
  }
5392
5795
  /**
5393
5796
  * Initializes instances of NavigationDrawer.
@@ -5578,7 +5981,7 @@ _NavigationDrawer._sidenavs = [];
5578
5981
  var NavigationDrawer = _NavigationDrawer;
5579
5982
 
5580
5983
  // src/ts/components/navigationRail.ts
5581
- var _defaults16 = {
5984
+ var _defaults18 = {
5582
5985
  onOpenStart: null,
5583
5986
  onOpenEnd: null,
5584
5987
  onCloseStart: null,
@@ -5654,7 +6057,7 @@ var NavigationRail = class _NavigationRail extends Component {
5654
6057
  document.addEventListener("keydown", this._onKeyDown);
5655
6058
  }
5656
6059
  static get defaults() {
5657
- return _defaults16;
6060
+ return _defaults18;
5658
6061
  }
5659
6062
  static init(els, options = {}) {
5660
6063
  return super.init(els, options, _NavigationRail);
@@ -5684,7 +6087,7 @@ var NavigationRail = class _NavigationRail extends Component {
5684
6087
  };
5685
6088
 
5686
6089
  // src/ts/components/tabs.ts
5687
- var _defaults17 = {
6090
+ var _defaults19 = {
5688
6091
  duration: 300,
5689
6092
  onShow: null,
5690
6093
  swipeable: false,
@@ -5760,7 +6163,7 @@ var Tabs = class _Tabs extends Component {
5760
6163
  this._setupEventHandlers();
5761
6164
  }
5762
6165
  static get defaults() {
5763
- return _defaults17;
6166
+ return _defaults19;
5764
6167
  }
5765
6168
  /**
5766
6169
  * Initializes instances of Tabs.
@@ -5947,7 +6350,7 @@ var Tabs = class _Tabs extends Component {
5947
6350
  };
5948
6351
 
5949
6352
  // src/ts/components/timepicker.ts
5950
- var _defaults18 = {
6353
+ var _defaults20 = {
5951
6354
  dialRadius: 135,
5952
6355
  outerRadius: 105,
5953
6356
  innerRadius: 70,
@@ -6000,12 +6403,6 @@ var _Timepicker = class _Timepicker extends Component {
6000
6403
  if (this.displayPlugin) this.displayPlugin.show();
6001
6404
  }
6002
6405
  };
6003
- this._handleTimeInputEnterKey = (e) => {
6004
- if (e.key === Utils.keys.ENTER) {
6005
- e.preventDefault();
6006
- this._inputFromTextField();
6007
- }
6008
- };
6009
6406
  this._handleClockClickStart = (e) => {
6010
6407
  if (!e.isPrimary) return;
6011
6408
  if (e.pointerType === "mouse" && e.button !== 0) return;
@@ -6160,7 +6557,7 @@ var _Timepicker = class _Timepicker extends Component {
6160
6557
  }
6161
6558
  }
6162
6559
  static get defaults() {
6163
- return _defaults18;
6560
+ return _defaults20;
6164
6561
  }
6165
6562
  /**
6166
6563
  * Initializes instances of Timepicker.
@@ -6510,7 +6907,7 @@ _Timepicker._template = `<div class="timepicker-container">
6510
6907
  var Timepicker = _Timepicker;
6511
6908
 
6512
6909
  // src/ts/components/snackbar.ts
6513
- var _defaults19 = {
6910
+ var _defaults21 = {
6514
6911
  text: "",
6515
6912
  action: "",
6516
6913
  onAction: null,
@@ -6557,7 +6954,7 @@ var _Snackbar = class _Snackbar {
6557
6954
  this._setTimer();
6558
6955
  }
6559
6956
  static get defaults() {
6560
- return _defaults19;
6957
+ return _defaults21;
6561
6958
  }
6562
6959
  static getInstance(el) {
6563
6960
  return el["Expressive_Snackbar"];
@@ -6792,7 +7189,7 @@ _Snackbar._draggedSnackbar = null;
6792
7189
  var Snackbar = _Snackbar;
6793
7190
 
6794
7191
  // src/ts/components/tooltip.ts
6795
- var _defaults20 = {
7192
+ var _defaults22 = {
6796
7193
  exitDelay: 200,
6797
7194
  enterDelay: 0,
6798
7195
  text: "",
@@ -6862,7 +7259,7 @@ var Tooltip = class _Tooltip extends Component {
6862
7259
  this._setupEventHandlers();
6863
7260
  }
6864
7261
  static get defaults() {
6865
- return _defaults20;
7262
+ return _defaults22;
6866
7263
  }
6867
7264
  /**
6868
7265
  * Initializes instances of Tooltip.
@@ -6876,6 +7273,12 @@ var Tooltip = class _Tooltip extends Component {
6876
7273
  return el["Expressive_Tooltip"];
6877
7274
  }
6878
7275
  destroy() {
7276
+ clearTimeout(this._enterDelayTimeout);
7277
+ clearTimeout(this._exitDelayTimeout);
7278
+ clearTimeout(this._animationTimeout);
7279
+ this.isOpen = false;
7280
+ this.isHovered = false;
7281
+ this.isFocused = false;
6879
7282
  if (this.el.getAttribute("aria-describedby") === this.tooltipEl.id) {
6880
7283
  this.el.removeAttribute("aria-describedby");
6881
7284
  }
@@ -6967,32 +7370,15 @@ var Tooltip = class _Tooltip extends Component {
6967
7370
  tooltip.style.left = newCoordinates.x + "px";
6968
7371
  }
6969
7372
  _repositionWithinScreen(x, y, width, height) {
6970
- const scrollLeft = Utils.getDocumentScrollLeft();
6971
- const scrollTop = Utils.getDocumentScrollTop();
6972
- let newX = x - scrollLeft;
6973
- let newY = y - scrollTop;
6974
- const bounding = {
6975
- left: newX,
6976
- top: newY,
7373
+ return Utils._repositionWithinScreen(
7374
+ x,
7375
+ y,
6977
7376
  width,
6978
- height
6979
- };
6980
- const offset = this.options.margin + this.options.transitionMovement;
6981
- const edges = Utils.checkWithinContainer(document.body, bounding, offset);
6982
- if (edges.left) {
6983
- newX = offset;
6984
- } else if (edges.right) {
6985
- newX -= newX + width - window.innerWidth;
6986
- }
6987
- if (edges.top) {
6988
- newY = offset;
6989
- } else if (edges.bottom) {
6990
- newY -= newY + height - window.innerHeight;
6991
- }
6992
- return {
6993
- x: newX + scrollLeft,
6994
- y: newY + scrollTop
6995
- };
7377
+ height,
7378
+ this.options.margin,
7379
+ this.options.transitionMovement,
7380
+ "center"
7381
+ );
6996
7382
  }
6997
7383
  _animateIn() {
6998
7384
  this._positionTooltip();
@@ -7001,7 +7387,8 @@ var Tooltip = class _Tooltip extends Component {
7001
7387
  this.tooltipEl.style.transition = `
7002
7388
  transform ${duration}ms ease-out,
7003
7389
  opacity ${duration}ms ease-out`;
7004
- setTimeout(() => {
7390
+ clearTimeout(this._animationTimeout);
7391
+ this._animationTimeout = setTimeout(() => {
7005
7392
  this.tooltipEl.style.transform = `translateX(${this.xMovement}px) translateY(${this.yMovement}px)`;
7006
7393
  this.tooltipEl.style.opacity = (this.options.opacity || 1).toString();
7007
7394
  }, 1);
@@ -7011,7 +7398,8 @@ var Tooltip = class _Tooltip extends Component {
7011
7398
  this.tooltipEl.style.transition = `
7012
7399
  transform ${duration}ms ease-out,
7013
7400
  opacity ${duration}ms ease-out`;
7014
- setTimeout(() => {
7401
+ clearTimeout(this._animationTimeout);
7402
+ this._animationTimeout = setTimeout(() => {
7015
7403
  this.tooltipEl.style.transform = `translateX(0px) translateY(0px)`;
7016
7404
  this.tooltipEl.style.opacity = "0";
7017
7405
  }, 1);
@@ -7036,13 +7424,21 @@ var Tooltip = class _Tooltip extends Component {
7036
7424
 
7037
7425
  // src/ts/components/registry.ts
7038
7426
  var AUTO_INIT_COMPONENTS = {
7427
+ AppBar: {
7428
+ component: AppBar,
7429
+ selector: "header.medium, header.large, header:has(> :is(nav:not(.tabs, .navigation-bar, .navigation-rail), .bar) > :is(.search-bar, .searchbar))"
7430
+ },
7039
7431
  Autocomplete: {
7040
7432
  component: Autocomplete,
7041
7433
  selector: ".autocomplete"
7042
7434
  },
7435
+ ButtonGroup: {
7436
+ component: ButtonGroup,
7437
+ selector: ".button-group"
7438
+ },
7043
7439
  // Shared with Cards.Init() - see CARDS_SELECTOR. This entry used to read
7044
7440
  // '.cards', which no card markup uses.
7045
- Cards: { component: Cards, selector: CARDS_SELECTOR },
7441
+ Cards: { component: Cards, selector: CARDS_SELECTOR, filter: isCardReveal },
7046
7442
  ExpandingCard: {
7047
7443
  component: ExpandingCard,
7048
7444
  selector: EXPANDING_CARD_SELECTOR
@@ -7078,14 +7474,20 @@ var AUTO_INIT_COMPONENTS = {
7078
7474
  };
7079
7475
  function AutoInit(context = document.body, options) {
7080
7476
  for (const name of Object.keys(AUTO_INIT_COMPONENTS)) {
7081
- const { component, selector } = AUTO_INIT_COMPONENTS[name];
7477
+ const { component, selector, filter } = AUTO_INIT_COMPONENTS[name];
7082
7478
  const els = context.querySelectorAll(`:is(${selector}):not(.no-autoinit)`);
7083
- component.init(els, options?.[name] ?? {});
7479
+ if (filter) {
7480
+ els.forEach((el) => {
7481
+ if (filter(el)) component.init(el, options?.[name] ?? {});
7482
+ });
7483
+ } else {
7484
+ component.init(els, options?.[name] ?? {});
7485
+ }
7084
7486
  }
7085
7487
  }
7086
7488
 
7087
7489
  // src/ts/index.ts
7088
- var version = "0.8.0";
7490
+ var version = "0.9.1";
7089
7491
  Forms.Init();
7090
7492
  Chips.Init();
7091
7493
  Slider.Init();
@@ -7095,9 +7497,11 @@ Dialogs.Init();
7095
7497
  BottomSheets.Init();
7096
7498
  SideSheets.Init();
7097
7499
  export {
7500
+ AppBar,
7098
7501
  AutoInit,
7099
7502
  Autocomplete,
7100
7503
  BottomSheets,
7504
+ ButtonGroup,
7101
7505
  Cards,
7102
7506
  Carousel,
7103
7507
  CharacterCounter,