@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
@@ -20,9 +20,11 @@ var Expressive = (() => {
20
20
  // src/ts/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ AppBar: () => AppBar,
23
24
  AutoInit: () => AutoInit,
24
25
  Autocomplete: () => Autocomplete,
25
26
  BottomSheets: () => BottomSheets,
27
+ ButtonGroup: () => ButtonGroup,
26
28
  Cards: () => Cards,
27
29
  Carousel: () => Carousel,
28
30
  CharacterCounter: () => CharacterCounter,
@@ -431,103 +433,120 @@ var Expressive = (() => {
431
433
  onOpen: null,
432
434
  onClose: null
433
435
  };
434
- var CARDS_SELECTOR = "article:has(> aside)";
436
+ 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"])';
437
+ var REVEAL_INITIALIZED_CLASS = "card-reveal-initialized";
438
+ function hasAccessibleName(trigger) {
439
+ const label = trigger.getAttribute("aria-label");
440
+ if (label?.trim()) return true;
441
+ const labelledBy = trigger.getAttribute("aria-labelledby");
442
+ if (labelledBy) {
443
+ const referencedText = labelledBy.trim().split(/\s+/).map((id) => trigger.ownerDocument.getElementById(id)?.textContent ?? "").join(" ").trim();
444
+ if (referencedText) return true;
445
+ }
446
+ const content = trigger.cloneNode(true);
447
+ content.querySelectorAll('[aria-hidden="true"]').forEach((node) => node.remove());
448
+ return Boolean(content.textContent?.trim());
449
+ }
450
+ function isUsableCardTrigger(trigger, card, panel) {
451
+ return trigger.closest("article") === card && !panel.contains(trigger) && !trigger.disabled && trigger.getAttribute("aria-disabled") !== "true" && hasAccessibleName(trigger);
452
+ }
453
+ function isCardReveal(el) {
454
+ if (el.tagName !== "ARTICLE") return false;
455
+ const panels = el.querySelectorAll(":scope > aside");
456
+ if (panels.length !== 1 || !panels[0].id) return false;
457
+ return Array.from(
458
+ el.querySelectorAll('button.card-reveal-trigger[type="button"]')
459
+ ).some((trigger) => isUsableCardTrigger(trigger, el, panels[0]));
460
+ }
435
461
  var Cards = class _Cards extends Component {
436
- constructor(el, options) {
462
+ constructor(el, options = {}) {
437
463
  super(el, options, _Cards);
438
464
  this.isOpen = false;
465
+ this._ownsRevealInitializedClass = false;
466
+ this._initialAriaControls = /* @__PURE__ */ new Map();
467
+ this._lastActivator = null;
439
468
  this._setupEventHandlers = () => {
440
- this._activators.forEach((el) => {
441
- el.addEventListener("click", this._handleClickInteraction);
442
- el.addEventListener("keypress", this._handleKeypressEvent);
469
+ this._activators.forEach((activator) => {
470
+ activator.addEventListener("click", this._handleClickInteraction);
443
471
  });
472
+ this.el.addEventListener("keydown", this._handleKeydownEvent);
444
473
  };
445
474
  this._removeEventHandlers = () => {
446
- this._activators.forEach((el) => {
447
- el.removeEventListener("click", this._handleClickInteraction);
448
- el.removeEventListener("keypress", this._handleKeypressEvent);
475
+ this._activators.forEach((activator) => {
476
+ activator.removeEventListener("click", this._handleClickInteraction);
449
477
  });
478
+ this.el.removeEventListener("keydown", this._handleKeydownEvent);
450
479
  };
451
- this._handleClickInteraction = () => {
480
+ this._handleClickInteraction = (event) => {
481
+ this._lastActivator = event.currentTarget;
452
482
  this._handleRevealEvent();
453
483
  };
454
- this._handleKeypressEvent = (e) => {
455
- if (e.key === Utils.keys.ENTER || e.key === " ") {
456
- e.preventDefault();
457
- this._handleRevealEvent();
458
- }
484
+ this._handleKeydownEvent = (event) => {
485
+ const target = event.target instanceof Element ? event.target.closest("article") : null;
486
+ if (target !== this.el || event.key !== "Escape" || !this.isOpen) return;
487
+ event.preventDefault();
488
+ this.close();
459
489
  };
460
490
  this._handleRevealEvent = () => {
461
491
  if (this.isOpen) this.close();
462
492
  else this.open();
463
493
  };
464
- this._setupRevealCloseEventHandlers = () => {
465
- this.cardRevealClose.addEventListener("click", this.close);
466
- this.cardRevealClose.addEventListener("keypress", this._handleKeypressCloseEvent);
467
- };
468
- this._removeRevealCloseEventHandlers = () => {
469
- this.cardRevealClose.removeEventListener("click", this.close);
470
- this.cardRevealClose.removeEventListener("keypress", this._handleKeypressCloseEvent);
471
- };
472
- this._handleKeypressCloseEvent = (e) => {
473
- if (e.key === Utils.keys.ENTER) {
474
- this.close();
475
- }
476
- };
477
494
  /**
478
495
  * Show card reveal.
479
496
  */
480
497
  this.open = () => {
481
498
  if (this.isOpen || !this.cardReveal) return;
482
499
  this.isOpen = true;
483
- this.cardReveal.ariaExpanded = "true";
484
- this._activators.forEach((el) => el.ariaExpanded = "true");
485
- if (this.cardRevealClose) this.cardRevealClose.tabIndex = 0;
500
+ this.cardReveal.classList.add("open");
501
+ this.cardReveal.inert = false;
502
+ this._activators.forEach((activator) => activator.ariaExpanded = "true");
486
503
  if (typeof this.options.onOpen === "function") {
487
- this.options.onOpen.call(this);
504
+ this.options.onOpen.call(this, this.el);
488
505
  }
489
- if (this.cardRevealClose) this._setupRevealCloseEventHandlers();
490
506
  };
491
507
  /**
492
508
  * Hide card reveal.
493
509
  */
494
510
  this.close = () => {
495
511
  if (!this.isOpen || !this.cardReveal) return;
512
+ const focusWasInside = this.cardReveal.contains(document.activeElement);
496
513
  this.isOpen = false;
497
- this.cardReveal.ariaExpanded = "false";
498
- this._activators.forEach((el) => el.ariaExpanded = "false");
499
- if (this.cardRevealClose) this.cardRevealClose.tabIndex = -1;
514
+ this.cardReveal.classList.remove("open");
515
+ this.cardReveal.inert = true;
516
+ this._activators.forEach((activator) => activator.ariaExpanded = "false");
500
517
  if (typeof this.options.onClose === "function") {
501
- this.options.onClose.call(this);
518
+ this.options.onClose.call(this, this.el);
519
+ }
520
+ if (focusWasInside) {
521
+ (this._lastActivator ?? this._activators[0])?.focus();
502
522
  }
503
- if (this.cardRevealClose) this._removeRevealCloseEventHandlers();
504
523
  };
505
524
  this.el["Expressive_Cards"] = this;
506
525
  this.options = {
507
526
  ..._Cards.defaults,
508
527
  ...options
509
528
  };
510
- this._activators = [];
511
- this.cardReveal = this.el.querySelector(":scope > aside");
529
+ const cardReveals = this.el.querySelectorAll(":scope > aside");
530
+ const cardReveal = cardReveals.length === 1 ? cardReveals[0] : null;
531
+ const activators = cardReveal ? Array.from(
532
+ this.el.querySelectorAll(
533
+ 'button.card-reveal-trigger[type="button"]'
534
+ )
535
+ ).filter(
536
+ (activator) => isUsableCardTrigger(activator, this.el, cardReveal)
537
+ ) : [];
538
+ this.cardReveal = cardReveal?.id && activators.length > 0 ? cardReveal : null;
539
+ this._activators = this.cardReveal ? activators : [];
512
540
  if (this.cardReveal) {
513
- this._activators = Array.from(
514
- this.el.querySelectorAll(".activator")
515
- ).filter(
516
- (activator) => !this.cardReveal.contains(activator)
517
- );
518
- this._activators.forEach((el2) => {
519
- if (!el2) return;
520
- el2.tabIndex = 0;
521
- el2.ariaExpanded = "false";
522
- if (!el2.matches("a[href], button, input, select, textarea")) {
523
- el2.setAttribute("role", "button");
524
- }
541
+ this._ownsRevealInitializedClass = !this.el.classList.contains(REVEAL_INITIALIZED_CLASS);
542
+ this.el.classList.add(REVEAL_INITIALIZED_CLASS);
543
+ this._activators.forEach((activator) => {
544
+ this._initialAriaControls.set(activator, activator.getAttribute("aria-controls"));
545
+ activator.ariaExpanded = "false";
546
+ activator.setAttribute("aria-controls", this.cardReveal.id);
525
547
  });
526
- this.cardRevealClose = this.cardReveal.querySelector(
527
- ":scope > :is(h1, h2, h3, h4, h5, h6)"
528
- );
529
- if (this.cardRevealClose) this.cardRevealClose.tabIndex = -1;
530
- this.cardReveal.ariaExpanded = "false";
548
+ this.cardReveal.classList.remove("open");
549
+ this.cardReveal.inert = true;
531
550
  this._setupEventHandlers();
532
551
  }
533
552
  }
@@ -550,13 +569,28 @@ var Expressive = (() => {
550
569
  */
551
570
  destroy() {
552
571
  this._removeEventHandlers();
553
- if (this.cardRevealClose) this._removeRevealCloseEventHandlers();
572
+ if (this.cardReveal) {
573
+ this.cardReveal.classList.remove("open");
574
+ this.cardReveal.inert = false;
575
+ if (this._ownsRevealInitializedClass) this.el.classList.remove(REVEAL_INITIALIZED_CLASS);
576
+ }
577
+ this._activators.forEach((activator) => {
578
+ activator.removeAttribute("aria-expanded");
579
+ const initialAriaControls = this._initialAriaControls.get(activator);
580
+ if (initialAriaControls === null) activator.removeAttribute("aria-controls");
581
+ else if (initialAriaControls !== void 0) {
582
+ activator.setAttribute("aria-controls", initialAriaControls);
583
+ }
584
+ });
585
+ this.isOpen = false;
554
586
  this._activators = [];
587
+ this._initialAriaControls.clear();
588
+ this._lastActivator = null;
555
589
  this.el["Expressive_Cards"] = void 0;
556
590
  }
557
591
  static Init() {
558
592
  Utils.onDocumentReady(() => {
559
- const cards = document.querySelectorAll(CARDS_SELECTOR);
593
+ const cards = Array.from(document.querySelectorAll(CARDS_SELECTOR)).filter(isCardReveal);
560
594
  cards.forEach((el) => {
561
595
  if (el && el["Expressive_Cards"] == void 0) this.init(el);
562
596
  });
@@ -569,13 +603,12 @@ var Expressive = (() => {
569
603
  onOpen: null,
570
604
  onClose: null
571
605
  };
572
- var MOTION_DURATION = 500;
573
606
  var EXPANDING_CARD_SELECTOR = "article.expanding-card:has(> dialog.expanding-card-dialog)";
574
607
  var ExpandingCard = class _ExpandingCard extends Component {
575
608
  constructor(el, options) {
576
609
  super(el, options, _ExpandingCard);
577
610
  this.isOpen = false;
578
- this.closeTimer = null;
611
+ this.closeGeneration = 0;
579
612
  this._setupEventHandlers = () => {
580
613
  this.trigger?.addEventListener("click", this.open);
581
614
  this.trigger?.addEventListener("keypress", this._handleTriggerKeypress);
@@ -601,7 +634,8 @@ var Expressive = (() => {
601
634
  this.close();
602
635
  };
603
636
  this._handleNativeClose = () => {
604
- if (!this.isOpen) return;
637
+ if (this.dialog.open) return;
638
+ this.closeGeneration++;
605
639
  this.isOpen = false;
606
640
  this.trigger.ariaExpanded = "false";
607
641
  this.dialog.ariaExpanded = "false";
@@ -637,10 +671,7 @@ var Expressive = (() => {
637
671
  };
638
672
  this.open = () => {
639
673
  if (this.isOpen || !this.dialog || !this.trigger) return;
640
- if (this.closeTimer !== null) {
641
- window.clearTimeout(this.closeTimer);
642
- this.closeTimer = null;
643
- }
674
+ this.closeGeneration++;
644
675
  this._setTransitionOrigin();
645
676
  this.isOpen = true;
646
677
  this.trigger.ariaExpanded = "true";
@@ -655,6 +686,7 @@ var Expressive = (() => {
655
686
  };
656
687
  this.close = () => {
657
688
  if (!this.isOpen || !this.dialog || !this.trigger) return;
689
+ const generation = ++this.closeGeneration;
658
690
  this._setTransitionOrigin();
659
691
  this.isOpen = false;
660
692
  this.trigger.ariaExpanded = "false";
@@ -664,15 +696,16 @@ var Expressive = (() => {
664
696
  this.options.onClose.call(this, this.el);
665
697
  }
666
698
  const finish = () => {
667
- this.closeTimer = null;
668
- if (this.dialog.open) this.dialog.close();
669
- this.trigger.focus();
699
+ if (generation !== this.closeGeneration || this.isOpen || !this.dialog.open) return;
700
+ this.dialog.close();
701
+ if (this.dialog.isConnected && this.trigger.isConnected) this.trigger.focus();
670
702
  };
671
703
  const reducedMotion = window.matchMedia(
672
704
  "(prefers-reduced-motion: reduce)"
673
705
  ).matches;
674
- if (reducedMotion) finish();
675
- else this.closeTimer = window.setTimeout(finish, MOTION_DURATION);
706
+ const transitions = reducedMotion ? [] : (this.dialog.getAnimations?.() ?? []).filter((animation) => animation.transitionProperty === "clip-path");
707
+ if (transitions.length === 0) finish();
708
+ else void Promise.allSettled(transitions.map((animation) => animation.finished)).then(finish);
676
709
  };
677
710
  this.el["Expressive_ExpandingCard"] = this;
678
711
  this.options = {
@@ -709,7 +742,10 @@ var Expressive = (() => {
709
742
  }
710
743
  destroy() {
711
744
  this._removeEventHandlers();
712
- if (this.closeTimer !== null) window.clearTimeout(this.closeTimer);
745
+ this.closeGeneration++;
746
+ this.isOpen = false;
747
+ if (this.trigger) this.trigger.ariaExpanded = "false";
748
+ if (this.dialog) this.dialog.ariaExpanded = "false";
713
749
  this.dialog?.classList.remove("expanded");
714
750
  if (this.dialog?.open) this.dialog.close();
715
751
  this.el["Expressive_ExpandingCard"] = void 0;
@@ -748,6 +784,8 @@ var Expressive = (() => {
748
784
  var _Menu = class _Menu extends Component {
749
785
  constructor(el, options) {
750
786
  super(el, options, _Menu);
787
+ this._pendingTimers = /* @__PURE__ */ new Set();
788
+ this._transition = 0;
751
789
  this._handleClick = (e) => {
752
790
  e.preventDefault();
753
791
  if (this.isOpen) {
@@ -781,7 +819,7 @@ var Expressive = (() => {
781
819
  if (this.options.closeOnClick && this.menuEl?.contains(target) && !this.isTouchMoving) {
782
820
  this.close();
783
821
  } else if (!this.menuEl?.contains(target)) {
784
- setTimeout(() => {
822
+ this._schedule(() => {
785
823
  if (this.isOpen) {
786
824
  this.close();
787
825
  }
@@ -935,16 +973,18 @@ var Expressive = (() => {
935
973
  * Open menu.
936
974
  */
937
975
  this.open = () => {
938
- if (this.isOpen) return;
976
+ if (this.isOpen || this.el["Expressive_Menu"] !== this) return;
977
+ const transition = this._clearPendingTimers();
939
978
  this.isOpen = true;
940
979
  if (typeof this.options.onOpenStart === "function") {
941
980
  this.options.onOpenStart.call(this, this.el);
942
981
  }
982
+ if (transition !== this._transition) return;
943
983
  this._resetMenuStyles();
944
984
  this.menuEl.style.display = "block";
945
985
  this._placeMenu();
986
+ this._schedule(() => this._setupTemporaryEventHandlers(), 0);
946
987
  this._animateIn();
947
- setTimeout(() => this._setupTemporaryEventHandlers(), 0);
948
988
  this.el.ariaExpanded = "true";
949
989
  };
950
990
  /**
@@ -952,18 +992,20 @@ var Expressive = (() => {
952
992
  */
953
993
  this.close = () => {
954
994
  if (!this.isOpen) return;
995
+ const transition = this._clearPendingTimers();
955
996
  this.isOpen = false;
956
997
  this.focusedIndex = -1;
957
998
  this._closeSubmenus();
958
999
  if (typeof this.options.onCloseStart === "function") {
959
1000
  this.options.onCloseStart.call(this, this.el);
960
1001
  }
1002
+ if (transition !== this._transition) return;
961
1003
  this._animateOut();
962
1004
  this._removeTemporaryEventHandlers();
1005
+ this.el.ariaExpanded = "false";
963
1006
  if (this.options.autoFocus) {
964
1007
  this.el.focus();
965
1008
  }
966
- this.el.ariaExpanded = "false";
967
1009
  };
968
1010
  /**
969
1011
  * While menu is open, you can recalculate its dimensions if its contents have changed.
@@ -1012,6 +1054,9 @@ var Expressive = (() => {
1012
1054
  return el["Expressive_Menu"];
1013
1055
  }
1014
1056
  destroy() {
1057
+ this._clearPendingTimers();
1058
+ this.isOpen = false;
1059
+ this.el.ariaExpanded = "false";
1015
1060
  clearTimeout(this.filterTimeout);
1016
1061
  this._resetMenuStyles();
1017
1062
  this._removeEventHandlers();
@@ -1274,18 +1319,32 @@ var Expressive = (() => {
1274
1319
  width: idealWidth
1275
1320
  };
1276
1321
  }
1322
+ _schedule(callback, delay) {
1323
+ const timer = setTimeout(() => {
1324
+ this._pendingTimers.delete(timer);
1325
+ callback();
1326
+ }, delay);
1327
+ this._pendingTimers.add(timer);
1328
+ }
1329
+ _clearPendingTimers() {
1330
+ for (const timer of this._pendingTimers) clearTimeout(timer);
1331
+ this._pendingTimers.clear();
1332
+ return ++this._transition;
1333
+ }
1277
1334
  _animateIn() {
1335
+ const transition = this._transition;
1278
1336
  const duration = this.options.inDuration;
1279
1337
  this.menuEl.style.transition = "none";
1280
1338
  this.menuEl.style.opacity = "0";
1281
1339
  this.menuEl.style.transform = "scale(0.85)";
1282
- setTimeout(() => {
1340
+ this._schedule(() => {
1283
1341
  this.menuEl.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
1284
1342
  this.menuEl.style.opacity = "1";
1285
1343
  this.menuEl.style.transform = "scale(1)";
1286
1344
  }, 1);
1287
- setTimeout(() => {
1345
+ this._schedule(() => {
1288
1346
  this._focusInitialItem();
1347
+ if (transition !== this._transition) return;
1289
1348
  if (typeof this.options.onOpenEnd === "function") this.options.onOpenEnd.call(this, this.el);
1290
1349
  }, duration);
1291
1350
  }
@@ -1294,7 +1353,7 @@ var Expressive = (() => {
1294
1353
  this.menuEl.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
1295
1354
  this.menuEl.style.opacity = "0";
1296
1355
  this.menuEl.style.transform = "scale(0.85)";
1297
- setTimeout(() => {
1356
+ this._schedule(() => {
1298
1357
  this._resetMenuStyles();
1299
1358
  if (typeof this.options.onCloseEnd === "function")
1300
1359
  this.options.onCloseEnd.call(this, this.el);
@@ -2563,8 +2622,89 @@ var Expressive = (() => {
2563
2622
  }
2564
2623
  };
2565
2624
 
2625
+ // src/ts/components/appBar.ts
2626
+ var _defaults7 = {};
2627
+ var AppBar = class _AppBar extends Component {
2628
+ constructor(el, options) {
2629
+ super(el, options, _AppBar);
2630
+ this._observer = null;
2631
+ this._sentinel = null;
2632
+ this._input = null;
2633
+ this._view = null;
2634
+ this._suppressSearchOpen = false;
2635
+ this._onIntersect = (entries) => {
2636
+ const entry = entries[0];
2637
+ if (!entry) return;
2638
+ this.el.classList.toggle("collapsed", !entry.isIntersecting);
2639
+ };
2640
+ this._onSearchViewClose = () => {
2641
+ this._suppressSearchOpen = true;
2642
+ };
2643
+ this._onSearchFocus = () => {
2644
+ if (!this._view) return;
2645
+ if (this._suppressSearchOpen) {
2646
+ this._suppressSearchOpen = false;
2647
+ return;
2648
+ }
2649
+ if (this._view instanceof HTMLDialogElement) {
2650
+ if (!this._view.open) this._view.showModal();
2651
+ return;
2652
+ }
2653
+ this._view.hidden = false;
2654
+ this._input?.setAttribute("aria-expanded", "true");
2655
+ };
2656
+ this.el["Expressive_AppBar"] = this;
2657
+ this.options = { ..._AppBar.defaults, ...options };
2658
+ this._setupCollapse();
2659
+ this._setupSearch();
2660
+ }
2661
+ static get defaults() {
2662
+ return _defaults7;
2663
+ }
2664
+ static init(els, options = {}) {
2665
+ return super.init(els, options, _AppBar);
2666
+ }
2667
+ static getInstance(el) {
2668
+ return el["Expressive_AppBar"];
2669
+ }
2670
+ destroy() {
2671
+ this._observer?.disconnect();
2672
+ this._observer = null;
2673
+ this._sentinel?.remove();
2674
+ this._sentinel = null;
2675
+ this._input?.removeEventListener("focus", this._onSearchFocus);
2676
+ this._view?.removeEventListener("close", this._onSearchViewClose);
2677
+ this.el.classList.remove("collapsed");
2678
+ this.el["Expressive_AppBar"] = void 0;
2679
+ }
2680
+ _setupCollapse() {
2681
+ if (!this.el.classList.contains("medium") && !this.el.classList.contains("large")) {
2682
+ return;
2683
+ }
2684
+ if (typeof IntersectionObserver === "undefined") return;
2685
+ this._sentinel = document.createElement("span");
2686
+ this._sentinel.setAttribute("aria-hidden", "true");
2687
+ this._sentinel.style.cssText = "display:block;width:100%;height:1px;margin-top:-1px;pointer-events:none;visibility:hidden";
2688
+ this.el.insertAdjacentElement("beforebegin", this._sentinel);
2689
+ this._observer = new IntersectionObserver(this._onIntersect);
2690
+ this._observer.observe(this._sentinel);
2691
+ }
2692
+ _setupSearch() {
2693
+ this._input = this.el.querySelector('input[type="search"]');
2694
+ if (!this._input) return;
2695
+ const id = this._input.getAttribute("aria-controls");
2696
+ if (id) this._view = document.getElementById(id);
2697
+ if (!this._view) this._view = this.el.querySelector(".search-view");
2698
+ if (!this._view) return;
2699
+ this._input.addEventListener("focus", this._onSearchFocus);
2700
+ if (this._view instanceof HTMLDialogElement) {
2701
+ this._view.addEventListener("close", this._onSearchViewClose);
2702
+ }
2703
+ }
2704
+ };
2705
+
2566
2706
  // src/ts/components/buttons.ts
2567
- var _defaults7 = {
2707
+ var _defaults8 = {
2568
2708
  direction: "top",
2569
2709
  hoverEnabled: true,
2570
2710
  toolbarEnabled: false
@@ -2637,7 +2777,7 @@ var Expressive = (() => {
2637
2777
  this._setupEventHandlers();
2638
2778
  }
2639
2779
  static get defaults() {
2640
- return _defaults7;
2780
+ return _defaults8;
2641
2781
  }
2642
2782
  static init(els, options = {}) {
2643
2783
  return super.init(els, options, _FloatingActionButton);
@@ -2674,8 +2814,226 @@ var Expressive = (() => {
2674
2814
  }
2675
2815
  };
2676
2816
 
2817
+ // src/ts/components/buttonGroup.ts
2818
+ var _defaults9 = {};
2819
+ var VALID_SELECTION_MODES = /* @__PURE__ */ new Set(["single", "multiple"]);
2820
+ var PRESSED_CLASS = "button-group-pressed";
2821
+ var MAX_ICON_COMPRESSION = 0.15;
2822
+ var ButtonGroup = class _ButtonGroup extends Component {
2823
+ constructor(el, options = {}) {
2824
+ super(el, options, _ButtonGroup);
2825
+ this._pressedStyles = /* @__PURE__ */ new Map();
2826
+ this._releaseStyles = /* @__PURE__ */ new Map();
2827
+ this._releaseTimer = null;
2828
+ this._originalPressed = /* @__PURE__ */ new Map();
2829
+ this._activeItem = null;
2830
+ this._handleClick = (event) => {
2831
+ if (!this._selectionMode) return;
2832
+ const item = this._itemFromEvent(event);
2833
+ if (!(item instanceof HTMLButtonElement) || this._isDisabled(item)) return;
2834
+ this._rememberPressed(item);
2835
+ const willSelect = item.getAttribute("aria-pressed") !== "true";
2836
+ const selectedCount = this._toggleItems.filter(
2837
+ (button) => button.getAttribute("aria-pressed") === "true"
2838
+ ).length;
2839
+ if (!willSelect && this.el.hasAttribute("data-selection-required") && selectedCount === 1) {
2840
+ return;
2841
+ }
2842
+ if (this._selectionMode === "single" && willSelect) {
2843
+ this._toggleItems.forEach((button) => {
2844
+ this._rememberPressed(button);
2845
+ button.setAttribute("aria-pressed", "false");
2846
+ });
2847
+ }
2848
+ item.setAttribute("aria-pressed", willSelect ? "true" : "false");
2849
+ };
2850
+ this._handlePointerDown = (event) => {
2851
+ if (event.button !== 0) return;
2852
+ const item = this._itemFromEvent(event);
2853
+ if (item && !this._isDisabled(item)) this._startPress(item);
2854
+ };
2855
+ this._handleKeyDown = (event) => {
2856
+ if (event.repeat || event.key !== " " && event.key !== "Enter") return;
2857
+ const item = this._itemFromEvent(event);
2858
+ if (item && !this._isDisabled(item)) this._startPress(item);
2859
+ };
2860
+ this._handleKeyUp = (event) => {
2861
+ if (event.key === " " || event.key === "Enter") this._resetPress();
2862
+ };
2863
+ this._handleFocusOut = (event) => {
2864
+ if (event.target === this._activeItem) this._resetPress();
2865
+ };
2866
+ this._resetPress = () => {
2867
+ this._activeItem?.classList.remove(PRESSED_CLASS);
2868
+ this._activeItem = null;
2869
+ if (!this._pressedStyles.size) return;
2870
+ this._finishRelease();
2871
+ this._releaseStyles = new Map(this._pressedStyles);
2872
+ for (const [item, style] of this._releaseStyles) {
2873
+ item.style.width = `${style.baseWidth}px`;
2874
+ }
2875
+ this._pressedStyles.clear();
2876
+ this._releaseTimer = window.setTimeout(this._finishRelease, 200);
2877
+ };
2878
+ this._finishRelease = () => {
2879
+ if (this._releaseTimer !== null) window.clearTimeout(this._releaseTimer);
2880
+ this._releaseTimer = null;
2881
+ for (const [item, style] of this._releaseStyles) item.style.width = style.width;
2882
+ this._releaseStyles.clear();
2883
+ };
2884
+ this.el["Expressive_ButtonGroup"] = this;
2885
+ this.options = { ..._ButtonGroup.defaults, ...options };
2886
+ if (this._selectionMode) {
2887
+ this._normalizeSelection();
2888
+ this.el.addEventListener("click", this._handleClick, true);
2889
+ }
2890
+ this.el.addEventListener("pointerdown", this._handlePointerDown);
2891
+ this.el.addEventListener("keydown", this._handleKeyDown);
2892
+ this.el.addEventListener("focusout", this._handleFocusOut);
2893
+ document.addEventListener("pointerup", this._resetPress);
2894
+ document.addEventListener("pointercancel", this._resetPress);
2895
+ document.addEventListener("keyup", this._handleKeyUp);
2896
+ window.addEventListener("blur", this._resetPress);
2897
+ }
2898
+ static get defaults() {
2899
+ return _defaults9;
2900
+ }
2901
+ static init(els, options = {}) {
2902
+ return super.init(els, options, _ButtonGroup);
2903
+ }
2904
+ static getInstance(el) {
2905
+ return el["Expressive_ButtonGroup"];
2906
+ }
2907
+ destroy() {
2908
+ this._resetPress();
2909
+ this._finishRelease();
2910
+ this.el.removeEventListener("click", this._handleClick, true);
2911
+ this.el.removeEventListener("pointerdown", this._handlePointerDown);
2912
+ this.el.removeEventListener("keydown", this._handleKeyDown);
2913
+ this.el.removeEventListener("focusout", this._handleFocusOut);
2914
+ document.removeEventListener("pointerup", this._resetPress);
2915
+ document.removeEventListener("pointercancel", this._resetPress);
2916
+ document.removeEventListener("keyup", this._handleKeyUp);
2917
+ window.removeEventListener("blur", this._resetPress);
2918
+ for (const [item, value] of this._originalPressed) {
2919
+ if (value === null) item.removeAttribute("aria-pressed");
2920
+ else item.setAttribute("aria-pressed", value);
2921
+ }
2922
+ this._originalPressed.clear();
2923
+ this.el["Expressive_ButtonGroup"] = void 0;
2924
+ }
2925
+ get _selectionMode() {
2926
+ const mode = this.el.dataset.selection;
2927
+ return VALID_SELECTION_MODES.has(mode) ? mode : null;
2928
+ }
2929
+ get _items() {
2930
+ return Array.from(this.el.children).filter(
2931
+ (child) => child instanceof HTMLButtonElement || child instanceof HTMLAnchorElement && child.classList.contains("button")
2932
+ );
2933
+ }
2934
+ get _toggleItems() {
2935
+ return this._items.filter(
2936
+ (item) => item instanceof HTMLButtonElement
2937
+ );
2938
+ }
2939
+ _normalizeSelection() {
2940
+ const items = this._toggleItems;
2941
+ items.forEach((item) => {
2942
+ this._rememberPressed(item);
2943
+ if (!["true", "false"].includes(item.getAttribute("aria-pressed") || "")) {
2944
+ item.setAttribute("aria-pressed", "false");
2945
+ }
2946
+ });
2947
+ if (this._selectionMode === "single") {
2948
+ const selected = items.filter((item) => item.getAttribute("aria-pressed") === "true");
2949
+ selected.slice(1).forEach((item) => item.setAttribute("aria-pressed", "false"));
2950
+ }
2951
+ if (this.el.hasAttribute("data-selection-required") && !items.some((item) => item.getAttribute("aria-pressed") === "true")) {
2952
+ items.find((item) => !this._isDisabled(item))?.setAttribute("aria-pressed", "true");
2953
+ }
2954
+ }
2955
+ _itemFromEvent(event) {
2956
+ if (!(event.target instanceof Element)) return null;
2957
+ const item = event.target.closest("button, a.button");
2958
+ return item instanceof HTMLElement && item.parentElement === this.el ? item : null;
2959
+ }
2960
+ _isDisabled(item) {
2961
+ return item instanceof HTMLButtonElement && item.disabled || item.getAttribute("aria-disabled") === "true" || item.classList.contains("disabled");
2962
+ }
2963
+ _rememberPressed(item) {
2964
+ if (!this._originalPressed.has(item)) {
2965
+ this._originalPressed.set(item, item.getAttribute("aria-pressed"));
2966
+ }
2967
+ }
2968
+ _startPress(item) {
2969
+ if (this._activeItem) return;
2970
+ this._finishRelease();
2971
+ const items = this._items;
2972
+ const activeIndex = items.indexOf(item);
2973
+ if (activeIndex < 0) return;
2974
+ this._activeItem = item;
2975
+ item.classList.add(PRESSED_CLASS);
2976
+ if (this.el.classList.contains("connected") || items.length < 2) return;
2977
+ const rects = items.map((candidate) => candidate.getBoundingClientRect());
2978
+ const activeWidth = rects[activeIndex].width;
2979
+ if (activeWidth <= 0) return;
2980
+ const parsedMultiplier = Number.parseFloat(
2981
+ getComputedStyle(this.el).getPropertyValue(
2982
+ "--md-comp-button-group-pressed-item-width-multiplier"
2983
+ )
2984
+ );
2985
+ const multiplier = Number.isFinite(parsedMultiplier) ? parsedMultiplier : 0.15;
2986
+ const desiredGrowth = activeWidth * multiplier;
2987
+ const neighborIndexes = [activeIndex - 1, activeIndex + 1].filter(
2988
+ (index) => index >= 0 && index < items.length
2989
+ );
2990
+ const compression = /* @__PURE__ */ new Map();
2991
+ if (neighborIndexes.length === 1) {
2992
+ const index = neighborIndexes[0];
2993
+ compression.set(
2994
+ index,
2995
+ Math.min(desiredGrowth, this._compressionCapacity(items[index], rects[index]))
2996
+ );
2997
+ } else {
2998
+ const half = desiredGrowth / 2;
2999
+ for (const index of neighborIndexes) {
3000
+ compression.set(
3001
+ index,
3002
+ Math.min(half, this._compressionCapacity(items[index], rects[index]))
3003
+ );
3004
+ }
3005
+ let remaining = desiredGrowth - [...compression.values()].reduce((sum, value) => sum + value, 0);
3006
+ for (const index of neighborIndexes) {
3007
+ if (remaining <= 0) break;
3008
+ const capacity = this._compressionCapacity(items[index], rects[index]) - compression.get(index);
3009
+ const extra = Math.min(remaining, capacity);
3010
+ compression.set(index, compression.get(index) + extra);
3011
+ remaining -= extra;
3012
+ }
3013
+ }
3014
+ const actualGrowth = [...compression.values()].reduce((sum, value) => sum + value, 0);
3015
+ if (actualGrowth <= 0) return;
3016
+ items.forEach((candidate, index) => {
3017
+ this._pressedStyles.set(candidate, {
3018
+ width: candidate.style.width,
3019
+ baseWidth: rects[index].width
3020
+ });
3021
+ candidate.style.width = `${rects[index].width}px`;
3022
+ });
3023
+ void this.el.offsetWidth;
3024
+ items.forEach((candidate, index) => {
3025
+ const adjustment = index === activeIndex ? actualGrowth : -(compression.get(index) || 0);
3026
+ candidate.style.width = `${rects[index].width + adjustment}px`;
3027
+ });
3028
+ }
3029
+ _compressionCapacity(item, rect) {
3030
+ const minimumWidth = item.classList.contains("icon-button") ? rect.width * (1 - MAX_ICON_COMPRESSION) : rect.height;
3031
+ return Math.max(0, rect.width - minimumWidth);
3032
+ }
3033
+ };
3034
+
2677
3035
  // src/ts/components/carousel.ts
2678
- var _defaults8 = {
3036
+ var _defaults10 = {
2679
3037
  duration: 200,
2680
3038
  fullWidth: false,
2681
3039
  indicators: false,
@@ -2918,7 +3276,7 @@ var Expressive = (() => {
2918
3276
  return this._trackEl ?? this.el;
2919
3277
  }
2920
3278
  static get defaults() {
2921
- return _defaults8;
3279
+ return _defaults10;
2922
3280
  }
2923
3281
  static init(els, options = {}) {
2924
3282
  return super.init(els, options, _Carousel);
@@ -3314,7 +3672,7 @@ var Expressive = (() => {
3314
3672
  var Carousel = _Carousel;
3315
3673
 
3316
3674
  // src/ts/components/characterCounter.ts
3317
- var _defaults9 = Object.freeze({});
3675
+ var _defaults11 = Object.freeze({});
3318
3676
  var CharacterCounter = class _CharacterCounter extends Component {
3319
3677
  constructor(el, options) {
3320
3678
  super(el, {}, _CharacterCounter);
@@ -3339,7 +3697,7 @@ var Expressive = (() => {
3339
3697
  this._setupEventHandlers();
3340
3698
  }
3341
3699
  static get defaults() {
3342
- return _defaults9;
3700
+ return _defaults11;
3343
3701
  }
3344
3702
  /**
3345
3703
  * Initializes instances of CharacterCounter.
@@ -3391,7 +3749,7 @@ var Expressive = (() => {
3391
3749
  };
3392
3750
 
3393
3751
  // src/ts/components/select.ts
3394
- var _defaults10 = {
3752
+ var _defaults12 = {
3395
3753
  classes: "",
3396
3754
  menuOptions: {}
3397
3755
  };
@@ -3440,7 +3798,7 @@ var Expressive = (() => {
3440
3798
  this._setupEventHandlers();
3441
3799
  }
3442
3800
  static get defaults() {
3443
- return _defaults10;
3801
+ return _defaults12;
3444
3802
  }
3445
3803
  /**
3446
3804
  * Initializes instances of FormSelect.
@@ -3571,7 +3929,7 @@ var Expressive = (() => {
3571
3929
  _buildInput() {
3572
3930
  this.input = document.createElement("input");
3573
3931
  this.input.id = "select-input-" + Utils.guid();
3574
- this.input.classList.add("menu-trigger");
3932
+ this.input.classList.add("menu-trigger", "no-autoinit");
3575
3933
  this.input.type = "text";
3576
3934
  this.input.readOnly = true;
3577
3935
  this.input.setAttribute("data-target", this.menuEl.id);
@@ -3726,11 +4084,21 @@ var Expressive = (() => {
3726
4084
  }
3727
4085
  li.appendChild(span);
3728
4086
  const iconUrl = realOption.getAttribute("data-icon");
3729
- const classes = realOption.getAttribute("class")?.split(" ").filter(Boolean);
4087
+ let imageUrl;
3730
4088
  if (iconUrl) {
4089
+ try {
4090
+ const url = new URL(iconUrl, this.el.ownerDocument.baseURI);
4091
+ if (["http:", "https:", "blob:"].includes(url.protocol) || /^data:image\/(?:avif|bmp|gif|jpeg|png|svg\+xml|webp|x-icon)(?:;|,)/i.test(url.href)) {
4092
+ imageUrl = url;
4093
+ }
4094
+ } catch {
4095
+ }
4096
+ }
4097
+ const classes = realOption.getAttribute("class")?.split(" ").filter(Boolean);
4098
+ if (imageUrl) {
3731
4099
  const img = document.createElement("img");
3732
4100
  if (classes?.length) img.classList.add(...classes);
3733
- img.src = iconUrl;
4101
+ img.src = imageUrl.href;
3734
4102
  img.ariaHidden = "true";
3735
4103
  li.prepend(img);
3736
4104
  }
@@ -3812,7 +4180,7 @@ var Expressive = (() => {
3812
4180
  };
3813
4181
 
3814
4182
  // src/ts/plugins/dockedDisplayPlugin.ts
3815
- var _defaults11 = {
4183
+ var _defaults13 = {
3816
4184
  margin: 5,
3817
4185
  transition: 10,
3818
4186
  duration: 250,
@@ -3861,7 +4229,7 @@ var Expressive = (() => {
3861
4229
  };
3862
4230
  this.el = el;
3863
4231
  this.options = {
3864
- ..._defaults11,
4232
+ ..._defaults13,
3865
4233
  ...options
3866
4234
  };
3867
4235
  this.container = document.createElement("div");
@@ -3882,7 +4250,7 @@ var Expressive = (() => {
3882
4250
  };
3883
4251
 
3884
4252
  // src/ts/components/datepicker.ts
3885
- var _defaults12 = {
4253
+ var _defaults14 = {
3886
4254
  // the default output format for the input field value
3887
4255
  format: "mmm dd, yyyy",
3888
4256
  // Used to create date object from current input string
@@ -4147,7 +4515,7 @@ var Expressive = (() => {
4147
4515
  }
4148
4516
  }
4149
4517
  static get defaults() {
4150
- return _defaults12;
4518
+ return _defaults14;
4151
4519
  }
4152
4520
  /**
4153
4521
  * Initializes instances of Datepicker.
@@ -4561,13 +4929,18 @@ var Expressive = (() => {
4561
4929
  arr.push(className);
4562
4930
  }
4563
4931
  }
4564
- 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>`;
4932
+ const day = _Datepicker._escape(opts.day);
4933
+ const month = _Datepicker._escape(opts.month);
4934
+ const year = _Datepicker._escape(opts.year);
4935
+ 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>`;
4565
4936
  }
4937
+ /** Assemble calendar cells; reject unsupported elements and attributes. */
4566
4938
  renderRow(days, isRTL, isRowSelected) {
4567
- return '<tr class="datepicker-row' + (isRowSelected ? " is-selected" : "") + '">' + (isRTL ? days.reverse() : days).join("") + "</tr>";
4939
+ return '<tr class="datepicker-row' + (isRowSelected ? " is-selected" : "") + '">' + _Datepicker._calendarMarkup((isRTL ? days.reverse() : days).join(""), "tr") + "</tr>";
4568
4940
  }
4941
+ /** Assemble calendar rows; reject unsupported elements and attributes. */
4569
4942
  renderTable(opts, data, randId) {
4570
- 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>";
4943
+ 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>";
4571
4944
  }
4572
4945
  renderHead(opts) {
4573
4946
  const arr = [];
@@ -4579,8 +4952,40 @@ var Expressive = (() => {
4579
4952
  }
4580
4953
  return "<thead><tr>" + (opts.isRTL ? arr.reverse() : arr).join("") + "</tr></thead>";
4581
4954
  }
4955
+ /** Wrap calendar rows after validating and encoding their contents. */
4582
4956
  renderBody(rows) {
4583
- return "<tbody>" + rows.join("") + "</tbody>";
4957
+ return "<tbody>" + _Datepicker._calendarMarkup(rows.join(""), "tbody") + "</tbody>";
4958
+ }
4959
+ static _calendarMarkup(html, parent) {
4960
+ const template = document.createElement("template");
4961
+ template.innerHTML = html;
4962
+ const attributes = {
4963
+ tr: ["class"],
4964
+ td: ["class", "data-day", "aria-selected"],
4965
+ button: ["class", "type", "data-year", "data-month", "data-day", "aria-label", "disabled", "tabindex"]
4966
+ };
4967
+ const render = (node, container) => {
4968
+ if (node.nodeType === Node.TEXT_NODE) {
4969
+ if (container === "tbody" || container === "tr") {
4970
+ if (node.textContent.trim()) throw new TypeError("Unsupported calendar row content");
4971
+ }
4972
+ return _Datepicker._escape(node.textContent);
4973
+ }
4974
+ if (!(node instanceof HTMLElement)) throw new TypeError("Unsupported calendar row content");
4975
+ const tag = node.localName === "tr" ? "tr" : node.localName === "td" ? "td" : node.localName === "button" ? "button" : null;
4976
+ if (!tag || !(container === "tbody" && tag === "tr" || container === "tr" && tag === "td" || container === "td" && tag === "button")) {
4977
+ throw new TypeError("Unsupported calendar row element");
4978
+ }
4979
+ if ([...node.attributes].some((attr) => !attributes[tag].includes(attr.name))) {
4980
+ throw new TypeError("Unsupported calendar row attribute");
4981
+ }
4982
+ if (tag === "button" && node.getAttribute("type") !== "button") {
4983
+ throw new TypeError('Calendar day buttons require type="button"');
4984
+ }
4985
+ const attrs = attributes[tag].filter((name) => node.hasAttribute(name)).map((name) => ` ${name}="${_Datepicker._escape(node.getAttribute(name))}"`).join("");
4986
+ return `<${tag}${attrs}>${[...node.childNodes].map((child) => render(child, tag)).join("")}</${tag}>`;
4987
+ };
4988
+ return [...template.content.childNodes].map((node) => render(node, parent)).join("");
4584
4989
  }
4585
4990
  /**
4586
4991
  * Whether each month arrow is usable at this position in the allowed range.
@@ -4620,7 +5025,7 @@ var Expressive = (() => {
4620
5025
  }
4621
5026
  renderTitle(instance, c, year, month, refYear, randId) {
4622
5027
  const opts = this.options, isMinYear = year === opts.minYear, isMaxYear = year === opts.maxYear;
4623
- let i, j, arr = [], html = '<div id="' + randId + '" class="datepicker-controls" role="heading" aria-live="assertive">';
5028
+ let i, j, arr = [], html = '<div id="' + _Datepicker._escape(randId) + '" class="datepicker-controls" role="heading" aria-live="assertive">';
4624
5029
  const { prev, next } = this._arrowState(year, month);
4625
5030
  for (i = 0; i < 12; i++) {
4626
5031
  arr.push(
@@ -4865,7 +5270,7 @@ var Expressive = (() => {
4865
5270
  var Datepicker = _Datepicker;
4866
5271
 
4867
5272
  // src/ts/components/lightbox.ts
4868
- var _defaults13 = {
5273
+ var _defaults15 = {
4869
5274
  inDuration: 275,
4870
5275
  outDuration: 200,
4871
5276
  onOpenStart: null,
@@ -4984,7 +5389,7 @@ var Expressive = (() => {
4984
5389
  this._setupEventHandlers();
4985
5390
  }
4986
5391
  static get defaults() {
4987
- return _defaults13;
5392
+ return _defaults15;
4988
5393
  }
4989
5394
  /**
4990
5395
  * Initializes instances of Lightbox.
@@ -5150,7 +5555,7 @@ var Expressive = (() => {
5150
5555
 
5151
5556
  // src/ts/components/scrollspy.ts
5152
5557
  var _defaultGetActiveElement = (id) => `a[href="#${id}"]`;
5153
- var _defaults14 = {
5558
+ var _defaults16 = {
5154
5559
  scrollOffset: 200,
5155
5560
  activeClass: "active",
5156
5561
  getActiveElement: _defaultGetActiveElement,
@@ -5173,7 +5578,7 @@ var Expressive = (() => {
5173
5578
  _ScrollSpy._observer?.observe(this.el);
5174
5579
  }
5175
5580
  static get defaults() {
5176
- return _defaults14;
5581
+ return _defaults16;
5177
5582
  }
5178
5583
  static init(els, options = {}) {
5179
5584
  return super.init(els, options, _ScrollSpy);
@@ -5295,7 +5700,7 @@ var Expressive = (() => {
5295
5700
  var ScrollSpy = _ScrollSpy;
5296
5701
 
5297
5702
  // src/ts/components/navigationDrawer.ts
5298
- var _defaults15 = {
5703
+ var _defaults17 = {
5299
5704
  edge: "left",
5300
5705
  draggable: true,
5301
5706
  dragTargetWidth: "10px",
@@ -5438,7 +5843,7 @@ var Expressive = (() => {
5438
5843
  _NavigationDrawer._sidenavs.push(this);
5439
5844
  }
5440
5845
  static get defaults() {
5441
- return _defaults15;
5846
+ return _defaults17;
5442
5847
  }
5443
5848
  /**
5444
5849
  * Initializes instances of NavigationDrawer.
@@ -5629,7 +6034,7 @@ var Expressive = (() => {
5629
6034
  var NavigationDrawer = _NavigationDrawer;
5630
6035
 
5631
6036
  // src/ts/components/navigationRail.ts
5632
- var _defaults16 = {
6037
+ var _defaults18 = {
5633
6038
  onOpenStart: null,
5634
6039
  onOpenEnd: null,
5635
6040
  onCloseStart: null,
@@ -5705,7 +6110,7 @@ var Expressive = (() => {
5705
6110
  document.addEventListener("keydown", this._onKeyDown);
5706
6111
  }
5707
6112
  static get defaults() {
5708
- return _defaults16;
6113
+ return _defaults18;
5709
6114
  }
5710
6115
  static init(els, options = {}) {
5711
6116
  return super.init(els, options, _NavigationRail);
@@ -5735,7 +6140,7 @@ var Expressive = (() => {
5735
6140
  };
5736
6141
 
5737
6142
  // src/ts/components/tabs.ts
5738
- var _defaults17 = {
6143
+ var _defaults19 = {
5739
6144
  duration: 300,
5740
6145
  onShow: null,
5741
6146
  swipeable: false,
@@ -5811,7 +6216,7 @@ var Expressive = (() => {
5811
6216
  this._setupEventHandlers();
5812
6217
  }
5813
6218
  static get defaults() {
5814
- return _defaults17;
6219
+ return _defaults19;
5815
6220
  }
5816
6221
  /**
5817
6222
  * Initializes instances of Tabs.
@@ -5998,7 +6403,7 @@ var Expressive = (() => {
5998
6403
  };
5999
6404
 
6000
6405
  // src/ts/components/timepicker.ts
6001
- var _defaults18 = {
6406
+ var _defaults20 = {
6002
6407
  dialRadius: 135,
6003
6408
  outerRadius: 105,
6004
6409
  innerRadius: 70,
@@ -6051,12 +6456,6 @@ var Expressive = (() => {
6051
6456
  if (this.displayPlugin) this.displayPlugin.show();
6052
6457
  }
6053
6458
  };
6054
- this._handleTimeInputEnterKey = (e) => {
6055
- if (e.key === Utils.keys.ENTER) {
6056
- e.preventDefault();
6057
- this._inputFromTextField();
6058
- }
6059
- };
6060
6459
  this._handleClockClickStart = (e) => {
6061
6460
  if (!e.isPrimary) return;
6062
6461
  if (e.pointerType === "mouse" && e.button !== 0) return;
@@ -6211,7 +6610,7 @@ var Expressive = (() => {
6211
6610
  }
6212
6611
  }
6213
6612
  static get defaults() {
6214
- return _defaults18;
6613
+ return _defaults20;
6215
6614
  }
6216
6615
  /**
6217
6616
  * Initializes instances of Timepicker.
@@ -6561,7 +6960,7 @@ var Expressive = (() => {
6561
6960
  var Timepicker = _Timepicker;
6562
6961
 
6563
6962
  // src/ts/components/snackbar.ts
6564
- var _defaults19 = {
6963
+ var _defaults21 = {
6565
6964
  text: "",
6566
6965
  action: "",
6567
6966
  onAction: null,
@@ -6608,7 +7007,7 @@ var Expressive = (() => {
6608
7007
  this._setTimer();
6609
7008
  }
6610
7009
  static get defaults() {
6611
- return _defaults19;
7010
+ return _defaults21;
6612
7011
  }
6613
7012
  static getInstance(el) {
6614
7013
  return el["Expressive_Snackbar"];
@@ -6843,7 +7242,7 @@ var Expressive = (() => {
6843
7242
  var Snackbar = _Snackbar;
6844
7243
 
6845
7244
  // src/ts/components/tooltip.ts
6846
- var _defaults20 = {
7245
+ var _defaults22 = {
6847
7246
  exitDelay: 200,
6848
7247
  enterDelay: 0,
6849
7248
  text: "",
@@ -6913,7 +7312,7 @@ var Expressive = (() => {
6913
7312
  this._setupEventHandlers();
6914
7313
  }
6915
7314
  static get defaults() {
6916
- return _defaults20;
7315
+ return _defaults22;
6917
7316
  }
6918
7317
  /**
6919
7318
  * Initializes instances of Tooltip.
@@ -6927,6 +7326,12 @@ var Expressive = (() => {
6927
7326
  return el["Expressive_Tooltip"];
6928
7327
  }
6929
7328
  destroy() {
7329
+ clearTimeout(this._enterDelayTimeout);
7330
+ clearTimeout(this._exitDelayTimeout);
7331
+ clearTimeout(this._animationTimeout);
7332
+ this.isOpen = false;
7333
+ this.isHovered = false;
7334
+ this.isFocused = false;
6930
7335
  if (this.el.getAttribute("aria-describedby") === this.tooltipEl.id) {
6931
7336
  this.el.removeAttribute("aria-describedby");
6932
7337
  }
@@ -7018,32 +7423,15 @@ var Expressive = (() => {
7018
7423
  tooltip.style.left = newCoordinates.x + "px";
7019
7424
  }
7020
7425
  _repositionWithinScreen(x, y, width, height) {
7021
- const scrollLeft = Utils.getDocumentScrollLeft();
7022
- const scrollTop = Utils.getDocumentScrollTop();
7023
- let newX = x - scrollLeft;
7024
- let newY = y - scrollTop;
7025
- const bounding = {
7026
- left: newX,
7027
- top: newY,
7426
+ return Utils._repositionWithinScreen(
7427
+ x,
7428
+ y,
7028
7429
  width,
7029
- height
7030
- };
7031
- const offset = this.options.margin + this.options.transitionMovement;
7032
- const edges = Utils.checkWithinContainer(document.body, bounding, offset);
7033
- if (edges.left) {
7034
- newX = offset;
7035
- } else if (edges.right) {
7036
- newX -= newX + width - window.innerWidth;
7037
- }
7038
- if (edges.top) {
7039
- newY = offset;
7040
- } else if (edges.bottom) {
7041
- newY -= newY + height - window.innerHeight;
7042
- }
7043
- return {
7044
- x: newX + scrollLeft,
7045
- y: newY + scrollTop
7046
- };
7430
+ height,
7431
+ this.options.margin,
7432
+ this.options.transitionMovement,
7433
+ "center"
7434
+ );
7047
7435
  }
7048
7436
  _animateIn() {
7049
7437
  this._positionTooltip();
@@ -7052,7 +7440,8 @@ var Expressive = (() => {
7052
7440
  this.tooltipEl.style.transition = `
7053
7441
  transform ${duration}ms ease-out,
7054
7442
  opacity ${duration}ms ease-out`;
7055
- setTimeout(() => {
7443
+ clearTimeout(this._animationTimeout);
7444
+ this._animationTimeout = setTimeout(() => {
7056
7445
  this.tooltipEl.style.transform = `translateX(${this.xMovement}px) translateY(${this.yMovement}px)`;
7057
7446
  this.tooltipEl.style.opacity = (this.options.opacity || 1).toString();
7058
7447
  }, 1);
@@ -7062,7 +7451,8 @@ var Expressive = (() => {
7062
7451
  this.tooltipEl.style.transition = `
7063
7452
  transform ${duration}ms ease-out,
7064
7453
  opacity ${duration}ms ease-out`;
7065
- setTimeout(() => {
7454
+ clearTimeout(this._animationTimeout);
7455
+ this._animationTimeout = setTimeout(() => {
7066
7456
  this.tooltipEl.style.transform = `translateX(0px) translateY(0px)`;
7067
7457
  this.tooltipEl.style.opacity = "0";
7068
7458
  }, 1);
@@ -7087,13 +7477,21 @@ var Expressive = (() => {
7087
7477
 
7088
7478
  // src/ts/components/registry.ts
7089
7479
  var AUTO_INIT_COMPONENTS = {
7480
+ AppBar: {
7481
+ component: AppBar,
7482
+ selector: "header.medium, header.large, header:has(> :is(nav:not(.tabs, .navigation-bar, .navigation-rail), .bar) > :is(.search-bar, .searchbar))"
7483
+ },
7090
7484
  Autocomplete: {
7091
7485
  component: Autocomplete,
7092
7486
  selector: ".autocomplete"
7093
7487
  },
7488
+ ButtonGroup: {
7489
+ component: ButtonGroup,
7490
+ selector: ".button-group"
7491
+ },
7094
7492
  // Shared with Cards.Init() - see CARDS_SELECTOR. This entry used to read
7095
7493
  // '.cards', which no card markup uses.
7096
- Cards: { component: Cards, selector: CARDS_SELECTOR },
7494
+ Cards: { component: Cards, selector: CARDS_SELECTOR, filter: isCardReveal },
7097
7495
  ExpandingCard: {
7098
7496
  component: ExpandingCard,
7099
7497
  selector: EXPANDING_CARD_SELECTOR
@@ -7129,14 +7527,20 @@ var Expressive = (() => {
7129
7527
  };
7130
7528
  function AutoInit(context = document.body, options) {
7131
7529
  for (const name of Object.keys(AUTO_INIT_COMPONENTS)) {
7132
- const { component, selector } = AUTO_INIT_COMPONENTS[name];
7530
+ const { component, selector, filter } = AUTO_INIT_COMPONENTS[name];
7133
7531
  const els = context.querySelectorAll(`:is(${selector}):not(.no-autoinit)`);
7134
- component.init(els, options?.[name] ?? {});
7532
+ if (filter) {
7533
+ els.forEach((el) => {
7534
+ if (filter(el)) component.init(el, options?.[name] ?? {});
7535
+ });
7536
+ } else {
7537
+ component.init(els, options?.[name] ?? {});
7538
+ }
7135
7539
  }
7136
7540
  }
7137
7541
 
7138
7542
  // src/ts/index.ts
7139
- var version = "0.8.0";
7543
+ var version = "0.9.1";
7140
7544
  Forms.Init();
7141
7545
  Chips.Init();
7142
7546
  Slider.Init();