From 30b800488ccd2194da152fc93c9bf29b8b5e7141 Mon Sep 17 00:00:00 2001 From: patrickjahr Date: Mon, 15 Apr 2024 09:35:13 +0200 Subject: [PATCH] wip(select): focus test --- .../select-options-sample.component.css | 41 ++++++++----------- .../select-options-sample.component.ts | 6 +-- .../select-sample.component.html | 10 +---- .../select-option/select-option.component.ts | 19 ++++++++- .../src/lib/select/select.component.html | 2 + .../sketch/src/lib/select/select.component.ts | 34 +++++++++++---- 6 files changed, 69 insertions(+), 43 deletions(-) diff --git a/apps/demo-app/src/app/pages/select-sample/select-options-sample/select-options-sample.component.css b/apps/demo-app/src/app/pages/select-sample/select-options-sample/select-options-sample.component.css index bf07ce9..b99341b 100644 --- a/apps/demo-app/src/app/pages/select-sample/select-options-sample/select-options-sample.component.css +++ b/apps/demo-app/src/app/pages/select-sample/select-options-sample/select-options-sample.component.css @@ -1,38 +1,33 @@ :host { - display: flex; - flex-direction: column; - gap: 0.25rem; - width: 100%; - margin-top: 0.5rem; + display: flex; + flex-direction: column; + width: 100%; + margin-top: 0.5rem; } sk-select-option { - background-color: #fff; - color: #000; - padding: 0.5rem 1rem; - cursor: pointer; - transition: color 0.3s ease-in-out, font-weight 0.3s ease-in-out, + background-color: #fff; + color: #000; + padding: 0.5rem 1rem; + cursor: pointer; + transition: color 0.3s ease-in-out, font-weight 0.3s ease-in-out, font-size 0.3s ease-in-out; } sk-select-option:first-child { - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; } sk-select-option:last-child { - border-bottom-left-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; } @media (hover) { - sk-select-option:hover { - color: deeppink; - font-weight: bold; - font-size: 1.5em; - } -} - -sk-select-option:not(:last-child) { - border-bottom: 1px solid #000; + sk-select-option:hover { + color: deeppink; + font-weight: bold; + font-size: 1.5em; + } } diff --git a/apps/demo-app/src/app/pages/select-sample/select-options-sample/select-options-sample.component.ts b/apps/demo-app/src/app/pages/select-sample/select-options-sample/select-options-sample.component.ts index 2e6a1fc..25d8bc5 100644 --- a/apps/demo-app/src/app/pages/select-sample/select-options-sample/select-options-sample.component.ts +++ b/apps/demo-app/src/app/pages/select-sample/select-options-sample/select-options-sample.component.ts @@ -34,9 +34,9 @@ export const slideDeleteAnimation = (): AnimationMetadata => { export const slideFadeAnimationFactory = (): AnimationMetadata[] => { return [ style({ opacity: 0, transform: 'translateY(-1.5rem)', scale: 0.8 }), - stagger('80ms', [ + stagger('32ms', [ animate( - '350ms cubic-bezier(0.05, 0.7, 0.1, 1)', + '150ms cubic-bezier(0.05, 0.7, 0.1, 1)', style({ opacity: 1, transform: 'translateY(0)', scale: 1 }) ), ]), @@ -84,7 +84,7 @@ export const zoomFactory = ( ), transition( '* => hidden', - query('sk-select-option', slideDeleteAnimation(), { + query('sk-select-option', fadeFactory(1, 0, '350ms'), { optional: true, }) ), diff --git a/apps/demo-app/src/app/pages/select-sample/select-sample.component.html b/apps/demo-app/src/app/pages/select-sample/select-sample.component.html index 68f59ec..3c5b938 100644 --- a/apps/demo-app/src/app/pages/select-sample/select-sample.component.html +++ b/apps/demo-app/src/app/pages/select-sample/select-sample.component.html @@ -3,7 +3,7 @@
Please select an option
@@ -20,12 +20,4 @@ - -
diff --git a/libs/sketch/src/lib/select/components/select-option/select-option.component.ts b/libs/sketch/src/lib/select/components/select-option/select-option.component.ts index 96b51dd..1ec3ecf 100644 --- a/libs/sketch/src/lib/select/components/select-option/select-option.component.ts +++ b/libs/sketch/src/lib/select/components/select-option/select-option.component.ts @@ -1,4 +1,10 @@ -import { Component, HostListener, inject, input } from '@angular/core'; +import { + Component, + HostBinding, + HostListener, + inject, + input, +} from '@angular/core'; import { CommonModule } from '@angular/common'; import { SelectComponent } from '../../select.component'; @@ -13,6 +19,17 @@ export class SelectOptionComponent { private readonly parent = inject(SelectComponent); value = input.required(); + @HostBinding('tabindex') + get tabindex(): number { + return 1; + } + + @HostListener('keydown.space', ['$event']) + @HostListener('keydown.enter', ['$event']) + enterItem(): void { + this.parent.selectionChanged(this.value(), true); + } + @HostListener('click', ['$event']) selectItem(): void { this.parent.selectionChanged(this.value()); diff --git a/libs/sketch/src/lib/select/select.component.html b/libs/sketch/src/lib/select/select.component.html index 9661212..96bf3ef 100644 --- a/libs/sketch/src/lib/select/select.component.html +++ b/libs/sketch/src/lib/select/select.component.html @@ -5,6 +5,8 @@ [skCdkOverlayPositions]="overlayPositions" [skCdkOverlayDisposeDelay]="animationDelay()" (click)="togglePanel(true)" + [tabIndex]="1" + (keydown.enter)="togglePanel(!this.panelIsVisible())" > @if (showPlaceholder()) { diff --git a/libs/sketch/src/lib/select/select.component.ts b/libs/sketch/src/lib/select/select.component.ts index 3aac9ba..e04f402 100644 --- a/libs/sketch/src/lib/select/select.component.ts +++ b/libs/sketch/src/lib/select/select.component.ts @@ -1,7 +1,9 @@ import { + AfterViewInit, ChangeDetectorRef, Component, computed, + contentChildren, effect, forwardRef, inject, @@ -11,7 +13,7 @@ import { untracked, ViewEncapsulation, } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { CommonModule, DOCUMENT } from '@angular/common'; import { CdkOverlayDirective, DEFAULT_DROPOUT_POSITIONS, @@ -19,11 +21,12 @@ import { import { CdkPortal } from '@angular/cdk/portal'; import { MultipleDirective } from './directives/multiple.directive'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { CdkTrapFocus } from '@angular/cdk/a11y'; @Component({ selector: 'sk-select', standalone: true, - imports: [CommonModule, CdkOverlayDirective, CdkPortal], + imports: [CommonModule, CdkOverlayDirective, CdkPortal, CdkTrapFocus], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -35,13 +38,13 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; styleUrl: './select.component.css', encapsulation: ViewEncapsulation.ShadowDom, }) -export class SelectComponent implements ControlValueAccessor { +export class SelectComponent implements ControlValueAccessor, AfterViewInit { private readonly multipleRef = inject(MultipleDirective, { optional: true }); private readonly changeDetectorRef = inject(ChangeDetectorRef); + private readonly document = inject(DOCUMENT); - animationDelay = input(0); - open = output(); - + readonly animationDelay = input(0); + readonly open = output(); readonly selectedValue = signal(undefined); readonly showPlaceholder = computed(() => { const selectedValue = this.selectedValue(); @@ -51,6 +54,9 @@ export class SelectComponent implements ControlValueAccessor { ); }); readonly panelIsVisible = signal(false); + readonly options = contentChildren('sk-select-option', { + descendants: true, + }); readonly overlayPositions = DEFAULT_DROPOUT_POSITIONS; protected updateSelectionMode = effect( @@ -78,9 +84,19 @@ export class SelectComponent implements ControlValueAccessor { togglePanel(visible: boolean): void { this.panelIsVisible.set(visible); this.open.emit(visible); + + if (visible) { + setTimeout(() => { + this.document.querySelector('sk-select-option')?.focus(); + }, 32); + } } - selectionChanged(value: T): void { + ngAfterViewInit(): void { + console.log('SelectComponent.ngAfterViewInit', this.options()); + } + + selectionChanged(value: T, forceClose = false): void { if (this.multipleRef?.multiple()) { this.selectedValue.update((selected) => { if (Array.isArray(selected)) { @@ -98,6 +114,10 @@ export class SelectComponent implements ControlValueAccessor { this.onChange?.(this.selectedValue()); this.onTouched?.(); + + if (forceClose) { + this.togglePanel(false); + } } writeValue(obj: T | T[] | undefined): void {