Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(searchbar): searchbar animations can be disabled #8162

Merged
merged 5 commits into from
Oct 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 28 additions & 21 deletions src/components/searchbar/searchbar.ios.scss
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ $searchbar-ios-toolbar-input-background: rgba(0, 0, 0, .08) !default;

background-repeat: no-repeat;
background-size: $searchbar-ios-input-search-icon-size;

transition: $searchbar-ios-input-transition;
}


Expand All @@ -87,8 +85,6 @@ $searchbar-ios-toolbar-input-background: rgba(0, 0, 0, .08) !default;

color: $searchbar-ios-input-text-color;
background-color: $searchbar-ios-input-background-color;

transition: $searchbar-ios-input-transition;
}


Expand Down Expand Up @@ -119,28 +115,15 @@ $searchbar-ios-toolbar-input-background: rgba(0, 0, 0, .08) !default;

flex-shrink: 0;

margin-right: -100%;
margin-left: 0;
padding: 0;
padding-left: 8px;

height: 30px;

cursor: pointer;

opacity: 0;

transform: translate3d(0, 0, 0);
transition: $searchbar-ios-cancel-transition;

pointer-events: none;
}

.searchbar-ios.searchbar-show-cancel .searchbar-ios-cancel {
display: block;
}


// Searchbar Left Aligned (iOS only)
// -----------------------------------------

Expand All @@ -156,10 +139,8 @@ $searchbar-ios-toolbar-input-background: rgba(0, 0, 0, .08) !default;
// Searchbar Has Focus
// -----------------------------------------

.searchbar-ios.searchbar-has-focus .searchbar-ios-cancel {
opacity: 1;

pointer-events: auto;
.searchbar-ios.searchbar-show-cancel.searchbar-has-focus .searchbar-ios-cancel {
display: block;
}


Expand Down Expand Up @@ -225,3 +206,29 @@ $searchbar-ios-toolbar-input-background: rgba(0, 0, 0, .08) !default;
}

}

// Searchbar animation
// -----------------------------------------

.searchbar-ios.searchbar-animated .searchbar-search-icon,
.searchbar-ios.searchbar-animated .searchbar-input {
transition: $searchbar-ios-input-transition;
}

.searchbar-animated.searchbar-has-focus .searchbar-ios-cancel {
opacity: 1;

pointer-events: auto;
}

.searchbar-animated .searchbar-ios-cancel {
display: block;

margin-right: -100%;

opacity: 0;
transform: translate3d(0, 0, 0);
transition: $searchbar-ios-cancel-transition;

pointer-events: none;
}
1 change: 1 addition & 0 deletions src/components/searchbar/searchbar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ ion-searchbar {
.searchbar-has-value.searchbar-has-focus .searchbar-clear-icon {
display: block;
}

162 changes: 92 additions & 70 deletions src/components/searchbar/searchbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { NgControl } from '@angular/forms';

import { Config } from '../../config/config';
import { Ion } from '../ion';
import { isPresent } from '../../util/util';
import { isPresent, isTrueProperty } from '../../util/util';
import { Debouncer } from '../../util/debouncer';


Expand All @@ -30,27 +30,37 @@ import { Debouncer } from '../../util/debouncer';
selector: 'ion-searchbar',
template:
'<div class="searchbar-input-container">' +
'<button ion-button (click)="cancelSearchbar($event)" (mousedown)="cancelSearchbar($event)" clear color="dark" class="searchbar-md-cancel">' +
'<button ion-button (click)="cancelSearchbar($event)" (mousedown)="cancelSearchbar($event)" clear color="dark" class="searchbar-md-cancel" type="button">' +
'<ion-icon name="arrow-back"></ion-icon>' +
'</button>' +
'<div #searchbarIcon class="searchbar-search-icon"></div>' +
'<input #searchbarInput [(ngModel)]="_value" [attr.placeholder]="placeholder" (input)="inputChanged($event)" (blur)="inputBlurred($event)" (focus)="inputFocused($event)" class="searchbar-input">' +
'<button ion-button clear class="searchbar-clear-icon" (click)="clearInput($event)" (mousedown)="clearInput($event)"></button>' +
'<input #searchbarInput class="searchbar-input" (input)="inputChanged($event)" (blur)="inputBlurred($event)" (focus)="inputFocused($event)" ' +
'[attr.placeholder]="placeholder" ' +
'[attr.type]="type" ' +
'[attr.autocomplete]="_autocomplete" ' +
'[attr.autocorrect]="_autocorrect" ' +
'[attr.spellcheck]="_spellcheck">' +
'<button ion-button clear class="searchbar-clear-icon" (click)="clearInput($event)" (mousedown)="clearInput($event)" type="button"></button>' +
'</div>' +
'<button ion-button #cancelButton [tabindex]="_isActive ? 1 : -1" clear (click)="cancelSearchbar($event)" (mousedown)="cancelSearchbar($event)" class="searchbar-ios-cancel">{{cancelButtonText}}</button>',
'<button ion-button #cancelButton [tabindex]="_isActive ? 1 : -1" clear (click)="cancelSearchbar($event)" (mousedown)="cancelSearchbar($event)" class="searchbar-ios-cancel" type="button">{{cancelButtonText}}</button>',
host: {
'[class.searchbar-animated]': 'animated',
'[class.searchbar-has-value]': '_value',
'[class.searchbar-active]': '_isActive',
'[class.searchbar-show-cancel]': 'showCancelButton',
'[class.searchbar-left-aligned]': 'shouldAlignLeft()'
'[class.searchbar-left-aligned]': '_shouldAlignLeft'
},
encapsulation: ViewEncapsulation.None
})
export class Searchbar extends Ion {
_value: string|number = '';
_shouldBlur: boolean = true;
_shouldAlignLeft: boolean = true;
_isCancelVisible: boolean = false;
_spellcheck: boolean = false;
_autocomplete: string = 'off';
_autocorrect: string = 'off';
_isActive: boolean = false;
_searchbarInput: ElementRef;
_debouncer: Debouncer = new Debouncer(250);

/**
Expand Down Expand Up @@ -98,23 +108,37 @@ export class Searchbar extends Ion {
/**
* @input {string} Set the input's autocomplete property. Values: `"on"`, `"off"`. Default `"off"`.
*/
@Input() autocomplete: string;
@Input()
set autocomplete(val: string) {
this._autocomplete = (val === '' || val === 'on') ? 'on' : this._config.get('autocomplete', 'off');
}

/**
* @input {string} Set the input's autocorrect property. Values: `"on"`, `"off"`. Default `"off"`.
*/
@Input() autocorrect: string;
@Input()
set autocorrect(val: string) {
this._autocorrect = (val === '' || val === 'on') ? 'on' : this._config.get('autocorrect', 'off');
}

/**
* @input {string|boolean} Set the input's spellcheck property. Values: `true`, `false`. Default `false`.
*/
@Input() spellcheck: string|boolean;
@Input()
set spellcheck(val: string | boolean) {
this._spellcheck = (val === '' || val === 'true' || val === true) ? true : this._config.getBoolean('spellcheck', false);
}

/**
* @input {string} Set the type of the input. Values: `"text"`, `"password"`, `"email"`, `"number"`, `"search"`, `"tel"`, `"url"`. Default `"search"`.
*/
@Input() type: string = 'search';

/**
* @input {string|boolean} Configures if the searchbar is animated or no. By default, animation is disabled.
*/
@Input() animated: string | boolean = false;

/**
* @output {event} When the Searchbar input has changed including cleared.
*/
Expand Down Expand Up @@ -161,30 +185,7 @@ export class Searchbar extends Ion {
}
}

/**
* @private
*/
@ViewChild('searchbarInput')
set searchbarInput(searchbarInput: ElementRef) {
this._searchbarInput = searchbarInput;

let inputEle = searchbarInput.nativeElement;

// By defalt set autocomplete="off" unless specified by the input
let autoComplete = (this.autocomplete === '' || this.autocomplete === 'on') ? 'on' : this._config.get('autocomplete', 'off');
inputEle.setAttribute('autocomplete', autoComplete);

// by default set autocorrect="off" unless specified by the input
let autoCorrect = (this.autocorrect === '' || this.autocorrect === 'on') ? 'on' : this._config.get('autocorrect', 'off');
inputEle.setAttribute('autocorrect', autoCorrect);

// by default set spellcheck="false" unless specified by the input
let spellCheck = (this.spellcheck === '' || this.spellcheck === 'true' || this.spellcheck === true) ? true : this._config.getBoolean('spellcheck', false);
inputEle.setAttribute('spellcheck', spellCheck);

// by default set type="search" unless specified by the input
inputEle.setAttribute('type', this.type);
}
@ViewChild('searchbarInput') _searchbarInput: ElementRef;

@ViewChild('searchbarIcon') _searchbarIcon: ElementRef;

Expand All @@ -200,6 +201,12 @@ export class Searchbar extends Ion {

set value(val) {
this._value = val;
if (this._searchbarInput) {
let ele = this._searchbarInput.nativeElement;
if (ele) {
ele.value = val;
}
}
}

/**
Expand All @@ -217,7 +224,7 @@ export class Searchbar extends Ion {
* @private
* After View Checked position the elements
*/
ngAfterViewChecked() {
ngAfterContentInit() {
this.positionElements();
}

Expand All @@ -227,26 +234,31 @@ export class Searchbar extends Ion {
* based on the input value and if it is focused. (ios only)
*/
positionElements() {
if (this._config.get('mode') !== 'ios') return;
let isAnimated = isTrueProperty(this.animated);
let prevAlignLeft = this._shouldAlignLeft;
let shouldAlignLeft = (!isAnimated || (this._value && this._value.toString().trim() !== '') || this._sbHasFocus === true);
this._shouldAlignLeft = shouldAlignLeft;

// Position the input placeholder & search icon
if (this._searchbarInput && this._searchbarIcon) {
this.positionInputPlaceholder(this._searchbarInput.nativeElement, this._searchbarIcon.nativeElement);
if (this._config.get('mode') !== 'ios') {
return;
}

// Position the cancel button
if (this._cancelButton && this._cancelButton.nativeElement) {
this.positionCancelButton(this._cancelButton.nativeElement);
if (prevAlignLeft !== shouldAlignLeft) {
this.positionPlaceholder();
}
if (isAnimated) {
this.positionCancelButton();
}
}

/**
* @private
* Calculates the amount of padding/margin left for the elements
* in order to center them based on the placeholder width
*/
positionInputPlaceholder(inputEle: HTMLElement, iconEle: HTMLElement) {
if (this.shouldAlignLeft()) {
positionPlaceholder() {
if (!this._searchbarInput || !this._searchbarIcon) {
return;
}
let inputEle = this._searchbarInput.nativeElement;
let iconEle = this._searchbarIcon.nativeElement;

if (this._shouldAlignLeft) {
inputEle.removeAttribute('style');
iconEle.removeAttribute('style');
} else {
Expand All @@ -273,32 +285,34 @@ export class Searchbar extends Ion {
* @private
* Show the iOS Cancel button on focus, hide it offscreen otherwise
*/
positionCancelButton(cancelButtonEle: HTMLElement) {
if (cancelButtonEle.offsetWidth > 0) {
if (this._sbHasFocus) {
cancelButtonEle.style.marginRight = '0';
positionCancelButton() {
if (!this._cancelButton || !this._cancelButton.nativeElement) {
return;
}
let showShowCancel = this._sbHasFocus;
if (showShowCancel !== this._isCancelVisible) {
let cancelStyleEle = this._cancelButton.nativeElement;
let cancelStyle = cancelStyleEle.style;
this._isCancelVisible = showShowCancel;
if (showShowCancel) {
cancelStyle.marginRight = '0';
} else {
cancelButtonEle.style.marginRight = -cancelButtonEle.offsetWidth + 'px';
let offset = cancelStyleEle.offsetWidth;
if (offset > 0) {
cancelStyle.marginRight = -offset + 'px';
}
}
}
}

/**
* @private
* Align the input placeholder left on focus or if a value exists
*/
shouldAlignLeft() {
return ( (this._value && this._value.toString().trim() !== '') || this._sbHasFocus === true );
}

/**
* @private
* Update the Searchbar input value when the input changes
*/
inputChanged(ev: any) {
let value = ev.target.value;
this._value = ev.target.value;
this._debouncer.debounce(() => {
this._value = value;
this.onChange(this._value);
this.ionInput.emit(ev);
});
Expand Down Expand Up @@ -342,12 +356,16 @@ export class Searchbar extends Ion {
clearInput(ev: UIEvent) {
this.ionClear.emit(ev);

if (isPresent(this._value) && this._value !== '') {
this._value = '';
this.onChange(this._value);
this.ionInput.emit(ev);
}

// setTimeout() fixes https://github.com/driftyco/ionic/issues/7527
// wait for 4 frames
setTimeout(() => {
let value = this._value;
if (isPresent(value) && value !== '') {
this.value = ''; // DOM WRITE
this.onChange(this._value);
this.ionInput.emit(ev);
}
}, 16 * 4);
this._shouldBlur = false;
}

Expand All @@ -370,7 +388,7 @@ export class Searchbar extends Ion {
* Write a new value to the element.
*/
writeValue(val: any) {
this._value = val;
this.value = val;
this.positionElements();
}

Expand Down Expand Up @@ -399,4 +417,8 @@ export class Searchbar extends Ion {
registerOnTouched(fn: () => {}): void {
this.onTouched = fn;
}

setFocus() {
this._renderer.invokeElementMethod(this._searchbarInput.nativeElement, 'focus');
}
}
5 changes: 4 additions & 1 deletion src/components/searchbar/test/basic/main.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<ion-content>
<h5 padding-left> Search - Default </h5>
<ion-searchbar [(ngModel)]="defaultSearch" showCancelButton debounce="500" (ionInput)="triggerInput($event)" (ionBlur)="inputBlurred($event)" (ionFocus)="inputFocused($event)" (ionCancel)="onCancelSearchbar($event)" (ionClear)="onClearSearchbar($event)"></ion-searchbar>
<ion-searchbar [(ngModel)]="defaultSearch" type="tel" showCancelButton debounce="500" (ionInput)="triggerInput($event)" (ionBlur)="inputBlurred($event)" (ionFocus)="inputFocused($event)" (ionCancel)="onCancelSearchbar($event)" (ionClear)="onClearSearchbar($event)"></ion-searchbar>

<h5 padding-left> Search - Animated </h5>
<ion-searchbar animated="true" showCancelButton debounce="500" (ionInput)="triggerInput($event)" (ionBlur)="inputBlurred($event)" (ionFocus)="inputFocused($event)" (ionCancel)="onCancelSearchbar($event)" (ionClear)="onClearSearchbar($event)"></ion-searchbar>

<p padding-left>
defaultSearch: <b>{{ defaultSearch }}</b>
Expand Down
Loading