@tedi-design-system/angular 9.0.0-rc.6 → 9.0.0-rc.7
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.
|
@@ -487,7 +487,11 @@ function breakpointInput(input) {
|
|
|
487
487
|
return { xs: input };
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
-
|
|
490
|
+
/** Native controls keep their own semantics and keyboard behavior. */
|
|
491
|
+
const NATIVE_INTERACTIVE_SELECTOR = 'button, a[href], input:not([type="hidden"]), select, textarea';
|
|
492
|
+
const FOCUSABLE_SELECTOR$1 = `${NATIVE_INTERACTIVE_SELECTOR}, [tabindex]`;
|
|
493
|
+
/** Native controls that fire `click` on Space but not on Enter. */
|
|
494
|
+
const SPACE_ACTIVATED_SELECTOR = 'input[type="checkbox"], input[type="radio"]';
|
|
491
495
|
class DropdownTriggerDirective {
|
|
492
496
|
/**
|
|
493
497
|
* The `aria-haspopup` value for the trigger, telling assistive technology
|
|
@@ -510,6 +514,8 @@ class DropdownTriggerDirective {
|
|
|
510
514
|
* would both be tab stops, and the ARIA state would land on the wrong element.
|
|
511
515
|
*/
|
|
512
516
|
triggerElement = signal(null, ...(ngDevMode ? [{ debugName: "triggerElement" }] : []));
|
|
517
|
+
/** Only a Space press that starts on the trigger may activate it on keyup. */
|
|
518
|
+
spacePressed = false;
|
|
513
519
|
hasPopup = computed(() => {
|
|
514
520
|
const explicit = this.ariaHaspopup();
|
|
515
521
|
if (explicit)
|
|
@@ -545,14 +551,21 @@ class DropdownTriggerDirective {
|
|
|
545
551
|
this.renderer.setAttribute(el, "aria-haspopup", hasPopup);
|
|
546
552
|
}
|
|
547
553
|
this.renderer.setAttribute(el, "aria-expanded", String(this.dropdown.isOpen()));
|
|
548
|
-
if (!this.isNativelyFocusable(el)) {
|
|
549
|
-
this.renderer.setAttribute(el, "role", "button");
|
|
550
|
-
this.renderer.setAttribute(el, "tabindex", "0");
|
|
551
|
-
}
|
|
552
554
|
});
|
|
553
555
|
}
|
|
554
556
|
ngAfterViewInit() {
|
|
555
|
-
this.
|
|
557
|
+
const el = this.resolveTriggerElement();
|
|
558
|
+
// Non-native triggers need button semantics, but a role or tabindex the
|
|
559
|
+
// consumer set (e.g. `role="checkbox"`) is theirs to keep.
|
|
560
|
+
if (!this.isNativelyInteractive(el)) {
|
|
561
|
+
if (!el.hasAttribute("role")) {
|
|
562
|
+
this.renderer.setAttribute(el, "role", "button");
|
|
563
|
+
}
|
|
564
|
+
if (!el.hasAttribute("tabindex")) {
|
|
565
|
+
this.renderer.setAttribute(el, "tabindex", "0");
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
this.triggerElement.set(el);
|
|
556
569
|
}
|
|
557
570
|
focus() {
|
|
558
571
|
this.triggerElement()?.focus();
|
|
@@ -568,6 +581,20 @@ class DropdownTriggerDirective {
|
|
|
568
581
|
onKeydown(event) {
|
|
569
582
|
const key = event.key;
|
|
570
583
|
switch (key) {
|
|
584
|
+
case "Enter":
|
|
585
|
+
if (!this.handlesKey(event, key))
|
|
586
|
+
break;
|
|
587
|
+
// Also keeps Enter on a checkbox from submitting its form.
|
|
588
|
+
event.preventDefault();
|
|
589
|
+
this.dropdown.toggleDropdown();
|
|
590
|
+
break;
|
|
591
|
+
case " ":
|
|
592
|
+
if (!this.handlesKey(event, key))
|
|
593
|
+
break;
|
|
594
|
+
// Match button timing and prevent page scrolling.
|
|
595
|
+
event.preventDefault();
|
|
596
|
+
this.spacePressed = true;
|
|
597
|
+
break;
|
|
571
598
|
case "ArrowDown":
|
|
572
599
|
if (!this.isWidgetContent())
|
|
573
600
|
break;
|
|
@@ -587,17 +614,43 @@ class DropdownTriggerDirective {
|
|
|
587
614
|
break;
|
|
588
615
|
}
|
|
589
616
|
}
|
|
617
|
+
onKeyup(event) {
|
|
618
|
+
if (event.key !== " " || !this.spacePressed)
|
|
619
|
+
return;
|
|
620
|
+
this.spacePressed = false;
|
|
621
|
+
if (event.target !== this.focusableElement)
|
|
622
|
+
return;
|
|
623
|
+
this.dropdown.toggleDropdown();
|
|
624
|
+
}
|
|
625
|
+
onFocusout(event) {
|
|
626
|
+
if (event.target === this.focusableElement)
|
|
627
|
+
this.spacePressed = false;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Whether the directive must activate the trigger on `key` itself, because the
|
|
631
|
+
* element does not turn that key into a native click: both keys for a generic
|
|
632
|
+
* trigger, Enter for a checkbox or radio. Other native controls handle both.
|
|
633
|
+
*/
|
|
634
|
+
handlesKey(event, key) {
|
|
635
|
+
const el = this.focusableElement;
|
|
636
|
+
// Keys from other controls inside the host are theirs.
|
|
637
|
+
if (event.target !== el)
|
|
638
|
+
return false;
|
|
639
|
+
if (!this.isNativelyInteractive(el))
|
|
640
|
+
return true;
|
|
641
|
+
return key === "Enter" && el.matches(SPACE_ACTIVATED_SELECTOR);
|
|
642
|
+
}
|
|
590
643
|
isWidgetContent() {
|
|
591
644
|
return this.dropdown.dropdownContent().isWidget();
|
|
592
645
|
}
|
|
593
646
|
resolveTriggerElement() {
|
|
594
647
|
const el = this.host.nativeElement;
|
|
595
|
-
if (this.
|
|
648
|
+
if (this.isNativelyInteractive(el))
|
|
596
649
|
return el;
|
|
597
650
|
return el.querySelector(FOCUSABLE_SELECTOR$1) ?? el;
|
|
598
651
|
}
|
|
599
|
-
|
|
600
|
-
return
|
|
652
|
+
isNativelyInteractive(el) {
|
|
653
|
+
return el.matches(NATIVE_INTERACTIVE_SELECTOR);
|
|
601
654
|
}
|
|
602
655
|
openAndFocusFirst() {
|
|
603
656
|
if (this.dropdown.isOpen()) {
|
|
@@ -616,7 +669,7 @@ class DropdownTriggerDirective {
|
|
|
616
669
|
}
|
|
617
670
|
}
|
|
618
671
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: DropdownTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
619
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.24", type: DropdownTriggerDirective, isStandalone: true, selector: "[tedi-dropdown-trigger]", inputs: { ariaHaspopup: { classPropertyName: "ariaHaspopup", publicName: "ariaHaspopup", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick()", "keydown": "onKeydown($event)" } }, hostDirectives: [{ directive: i1.CdkOverlayOrigin }], ngImport: i0 });
|
|
672
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.24", type: DropdownTriggerDirective, isStandalone: true, selector: "[tedi-dropdown-trigger]", inputs: { ariaHaspopup: { classPropertyName: "ariaHaspopup", publicName: "ariaHaspopup", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick()", "keydown": "onKeydown($event)", "keyup": "onKeyup($event)", "focusout": "onFocusout($event)" } }, hostDirectives: [{ directive: i1.CdkOverlayOrigin }], ngImport: i0 });
|
|
620
673
|
}
|
|
621
674
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: DropdownTriggerDirective, decorators: [{
|
|
622
675
|
type: Directive,
|
|
@@ -631,6 +684,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
|
|
|
631
684
|
}], onKeydown: [{
|
|
632
685
|
type: HostListener,
|
|
633
686
|
args: ["keydown", ["$event"]]
|
|
687
|
+
}], onKeyup: [{
|
|
688
|
+
type: HostListener,
|
|
689
|
+
args: ["keyup", ["$event"]]
|
|
690
|
+
}], onFocusout: [{
|
|
691
|
+
type: HostListener,
|
|
692
|
+
args: ["focusout", ["$event"]]
|
|
634
693
|
}] } });
|
|
635
694
|
|
|
636
695
|
const DROPDOWN_API = new InjectionToken("DropdownApi");
|