fix: make keyboard dismissable on mobile devices

This commit is contained in:
Rainer Killinger
2022-09-23 13:48:07 +00:00
parent 4a3f79ca20
commit b2cc1fd91f
10 changed files with 86 additions and 7 deletions

View File

@@ -22,6 +22,7 @@ import {environment} from '../environments/environment';
import {StatusBar, Style} from '@capacitor/status-bar';
import {Capacitor} from '@capacitor/core';
import {ScheduleSyncService} from './modules/background/schedule/schedule-sync.service';
import {Keyboard, KeyboardResize} from '@capacitor/keyboard';
/**
* TODO
@@ -45,6 +46,11 @@ export class AppComponent implements AfterContentInit {
title: string;
}>;
/**
* Angular component selectors that should not infulence keyboard state
*/
ommitedEventSources = ['ion-input', 'ion-searchbar'];
/**
*
* @param platform TODO
@@ -104,6 +110,11 @@ export class AppComponent implements AfterContentInit {
'others',
]);
});
window.addEventListener('touchmove', this.touchMoveEvent, true);
if (Capacitor.isNativePlatform()) {
Keyboard.setResizeMode({mode: KeyboardResize.None});
}
}
private async authNotificationsInit() {
@@ -129,4 +140,30 @@ export class AppComponent implements AfterContentInit {
});
await toast.present();
}
/**
* Checks if keyboard should be dissmissed
*/
touchMoveEvent = (event: Event): void => {
if (
this.ommitedEventSources.includes(
(event?.target as unknown as Record<string, string>)?.[
's-hn'
]?.toLowerCase(),
)
) {
return;
}
this.unfocusActiveElement();
};
/**
* Loses focus on the currently active element (meant for input fields).
* Results in virtual keyboard being dissmissed on native and web plattforms.
*/
unfocusActiveElement() {
const activeElement = document.activeElement;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(activeElement as any)?.blur();
}
}

View File

@@ -50,7 +50,10 @@
<ion-content fullscreen="true" #ionContent>
<stapps-navigation-section></stapps-navigation-section>
<stapps-search-section></stapps-search-section>
<stapps-search-section
#search
(focusin)="onSearchBarFocus($event)"
></stapps-search-section>
<stapps-news-section></stapps-news-section>
<stapps-mensa-section></stapps-mensa-section>
<stapps-favorites-section></stapps-favorites-section>

View File

@@ -52,6 +52,8 @@ export class DashboardComponent implements OnInit, OnDestroy {
@ViewChild('schedule', {read: ElementRef}) scheduleRef: ElementRef;
@ViewChild('search', {read: ElementRef}) searchRef: ElementRef;
@ViewChild('ionContent') ionContentRef: IonContent;
collapseAnimation: DashboardCollapse;
@@ -88,6 +90,11 @@ export class DashboardComponent implements OnInit, OnDestroy {
},
};
/**
* Offset from search bar to top
*/
searchToTopOffset = 0;
constructor(
private readonly dataRoutingService: DataRoutingService,
private scheduleProvider: ScheduleProvider,
@@ -121,6 +128,11 @@ export class DashboardComponent implements OnInit, OnDestroy {
);
}
ionViewDidEnter() {
this.searchToTopOffset =
this.searchRef.nativeElement?.getBoundingClientRect().top - 100;
}
async loadNextEvent() {
const dataSeries = await this.scheduleProvider.getDateSeries(
this.uuids,
@@ -142,4 +154,15 @@ export class DashboardComponent implements OnInit, OnDestroy {
this._uuidSubscription.unsubscribe();
this.collapseAnimation.destroy();
}
async onSearchBarFocus(_event: Event) {
this.ionContentRef.getScrollElement().then(element => {
if (
element.scrollHeight - element.clientHeight >=
this.searchToTopOffset
) {
this.ionContentRef.scrollToPoint(0, this.searchToTopOffset, 100);
}
});
}
}

View File

@@ -20,9 +20,11 @@
<div class="searchbar">
<ion-input
type="search"
enterkeyhint="search"
placeholder="{{ 'search.search_bar.placeholder' | translate }}"
(submit)="onSubmitSearch()"
(keyup.enter)="onSubmitSearch()"
(search)="onSubmitSearch()"
[(ngModel)]="searchTerm"
></ion-input>
<ion-icon

View File

@@ -22,10 +22,8 @@
border-radius: var(--border-radius-default);
--padding-start: var(--spacing-md);
--padding-end: var(--spacing-md);
--padding-top: var(--spacing-xl);
--padding-bottom: var(--spacing-xl);
font-size: var(--font-size-xs);
--placeholder-font-weight: var(--font-weight-bold);
--padding-top: var(--spacing-md);
--padding-bottom: var(--spacing-md);
box-shadow: var(--shadow-default);
}
ion-icon {

View File

@@ -14,6 +14,8 @@
*/
import {Component} from '@angular/core';
import {Router} from '@angular/router';
import {Capacitor} from '@capacitor/core';
import {Keyboard} from '@capacitor/keyboard';
/**
* Shows a search input field
@@ -32,6 +34,17 @@ export class SearchSectionComponent {
* User submits search
*/
onSubmitSearch() {
this.router.navigate(['/search', this.searchTerm]);
this.router
.navigate(['/search', this.searchTerm])
.then(() => this.hideKeyboard());
}
/**
* Hides keyboard in native app environments
*/
hideKeyboard() {
if (Capacitor.isNativePlatform()) {
Keyboard.hide();
}
}
}

View File

@@ -25,6 +25,7 @@
<ion-searchbar
(ngModelChange)="searchStringChanged($event)"
(keyup.enter)="hideKeyboard()"
(search)="hideKeyboard()"
[(ngModel)]="queryText"
showClearButton="always"
placeholder="{{ 'search.search_bar.placeholder' | translate }}"

View File

@@ -9,6 +9,7 @@
<ion-searchbar
(ngModelChange)="searchStringChanged($event)"
(keyup.enter)="hideKeyboard()"
(search)="hideKeyboard()"
[(ngModel)]="queryText"
mode="md"
placeholder="{{ 'hebisSearch.search_bar.placeholder' | translate }}"

View File

@@ -26,6 +26,7 @@
<ion-searchbar
(keyup)="searchKeyUp($event)"
(keyup.enter)="hideKeyboard()"
(search)="hideKeyboard()"
[(ngModel)]="queryText"
(ionClear)="searchStringChanged()"
mode="md"