From c4cf9df387da3524d80660fb69037de77553feea Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 18 Nov 2016 15:20:53 -0600 Subject: [PATCH] fix(input): fix tabbing between tappable inputs --- src/components/checkbox/checkbox.ts | 11 +++++++++-- src/components/radio/radio-button.ts | 11 +++++++++-- src/components/toggle/toggle.ts | 26 +++++++++++++++++++++++--- src/util/form.ts | 23 ++++++++++++++++++++++- src/util/key.ts | 15 +++++++++++---- 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/src/components/checkbox/checkbox.ts b/src/components/checkbox/checkbox.ts index a90d529a08a..1e96022a40a 100644 --- a/src/components/checkbox/checkbox.ts +++ b/src/components/checkbox/checkbox.ts @@ -2,7 +2,7 @@ import { AfterContentInit, Component, ElementRef, EventEmitter, forwardRef, Host import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Config } from '../../config/config'; -import { Form } from '../../util/form'; +import { Form, IonicTapInput } from '../../util/form'; import { Ion } from '../ion'; import { isTrueProperty } from '../../util/util'; import { Item } from '../item/item'; @@ -72,7 +72,7 @@ export const CHECKBOX_VALUE_ACCESSOR: any = { providers: [CHECKBOX_VALUE_ACCESSOR], encapsulation: ViewEncapsulation.None, }) -export class Checkbox extends Ion implements AfterContentInit, ControlValueAccessor, OnDestroy { +export class Checkbox extends Ion implements IonicTapInput, AfterContentInit, ControlValueAccessor, OnDestroy { /** @private */ _checked: boolean = false; /** @private */ @@ -210,6 +210,13 @@ export class Checkbox extends Ion implements AfterContentInit, ControlValueAcces this.onTouched(); } + /** + * @private + */ + initFocus() { + this._elementRef.nativeElement.querySelector('button').focus(); + } + /** * @private */ diff --git a/src/components/radio/radio-button.ts b/src/components/radio/radio-button.ts index efd8edf12f0..a70cbea2b58 100644 --- a/src/components/radio/radio-button.ts +++ b/src/components/radio/radio-button.ts @@ -1,7 +1,7 @@ import { Component, ElementRef, EventEmitter, HostListener, Input, OnInit, OnDestroy, Optional, Output, Renderer, ViewEncapsulation } from '@angular/core'; import { Config } from '../../config/config'; -import { Form } from '../../util/form'; +import { Form, IonicTapInput } from '../../util/form'; import { Ion } from '../ion'; import { isBlank, isCheckedProperty, isPresent, isTrueProperty } from '../../util/util'; import { Item } from '../item/item'; @@ -63,7 +63,7 @@ import { RadioGroup } from './radio-group'; }, encapsulation: ViewEncapsulation.None, }) -export class RadioButton extends Ion implements OnDestroy, OnInit { +export class RadioButton extends Ion implements IonicTapInput, OnDestroy, OnInit { /** * @internal @@ -180,6 +180,13 @@ export class RadioButton extends Ion implements OnDestroy, OnInit { this._item && this._item.setElementClass('item-radio-disabled', this._disabled); } + /** + * @private + */ + initFocus() { + this._elementRef.nativeElement.querySelector('button').focus(); + } + /** * @internal */ diff --git a/src/components/toggle/toggle.ts b/src/components/toggle/toggle.ts index a3f98d5da95..e829f8b9295 100644 --- a/src/components/toggle/toggle.ts +++ b/src/components/toggle/toggle.ts @@ -1,12 +1,13 @@ -import { AfterContentInit, Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, Optional, Output, Renderer, ViewEncapsulation } from '@angular/core'; +import { AfterContentInit, Component, ElementRef, EventEmitter, forwardRef, HostListener, Input, OnDestroy, Optional, Output, Renderer, ViewEncapsulation } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Config } from '../../config/config'; -import { Form } from '../../util/form'; +import { Form, IonicTapInput } from '../../util/form'; import { isTrueProperty } from '../../util/util'; import { Ion } from '../ion'; import { Item } from '../item/item'; import { pointerCoord } from '../../util/dom'; +import { Key } from '../../util/key'; import { Haptic } from '../../util/haptic'; import { UIEventManager } from '../../util/ui-event-manager'; @@ -74,7 +75,7 @@ export const TOGGLE_VALUE_ACCESSOR: any = { providers: [TOGGLE_VALUE_ACCESSOR], encapsulation: ViewEncapsulation.None, }) -export class Toggle extends Ion implements AfterContentInit, ControlValueAccessor, OnDestroy { +export class Toggle extends Ion implements IonicTapInput, AfterContentInit, ControlValueAccessor, OnDestroy { /** @private */ _checked: boolean = false; /** @private */ @@ -285,6 +286,25 @@ export class Toggle extends Ion implements AfterContentInit, ControlValueAccesso }); } + /** + * @private + */ + @HostListener('keyup', ['$event']) _keyup(ev: KeyboardEvent) { + if (ev.keyCode === Key.SPACE || ev.keyCode === Key.ENTER) { + console.debug(`toggle, keyup: ${ev.keyCode}`); + ev.preventDefault(); + ev.stopPropagation(); + this.onChange(!this._checked); + } + } + + /** + * @private + */ + initFocus() { + this._elementRef.nativeElement.querySelector('button').focus(); + } + /** * @private */ diff --git a/src/util/form.ts b/src/util/form.ts index 3327707cc73..bde732a4c10 100644 --- a/src/util/form.ts +++ b/src/util/form.ts @@ -25,7 +25,7 @@ export class Form { } focusOut() { - let activeElement = document.activeElement; + const activeElement = document.activeElement; activeElement && activeElement.blur && activeElement.blur(); } @@ -61,3 +61,24 @@ export class Form { } } + + +export abstract class IonicTapInput implements IonicFormInput { + + abstract initFocus(); + + abstract get checked(): boolean; + + abstract set checked(val: boolean); + + abstract get disabled(): boolean; + + abstract set disabled(val: boolean); + +} + +export abstract class IonicFormInput { + + abstract initFocus(); + +} diff --git a/src/util/key.ts b/src/util/key.ts index 1af40604b03..c013b4d4450 100644 --- a/src/util/key.ts +++ b/src/util/key.ts @@ -1,5 +1,12 @@ + export enum Key { - ENTER = 13, - ESCAPE = 27, - TAB = 9 -}; + LEFT = 37, + UP = 38, + RIGHT = 39, + DOWN = 40, + + ENTER = 13, + ESCAPE = 27, + SPACE = 32, + TAB = 9 +}