diff --git a/src/components/searchbar/searchbar.ios.scss b/src/components/searchbar/searchbar.ios.scss
index b1cea8f18fb..697e5431f87 100644
--- a/src/components/searchbar/searchbar.ios.scss
+++ b/src/components/searchbar/searchbar.ios.scss
@@ -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;
}
@@ -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;
}
@@ -119,7 +115,6 @@ $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;
@@ -127,20 +122,8 @@ $searchbar-ios-toolbar-input-background: rgba(0, 0, 0, .08) !default;
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)
// -----------------------------------------
@@ -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;
}
@@ -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;
+}
diff --git a/src/components/searchbar/searchbar.scss b/src/components/searchbar/searchbar.scss
index 9d68e30b5cc..0a67f402dde 100644
--- a/src/components/searchbar/searchbar.scss
+++ b/src/components/searchbar/searchbar.scss
@@ -48,3 +48,4 @@ ion-searchbar {
.searchbar-has-value.searchbar-has-focus .searchbar-clear-icon {
display: block;
}
+
diff --git a/src/components/searchbar/searchbar.ts b/src/components/searchbar/searchbar.ts
index 13358193c89..f769e77a5b7 100644
--- a/src/components/searchbar/searchbar.ts
+++ b/src/components/searchbar/searchbar.ts
@@ -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';
@@ -30,27 +30,37 @@ import { Debouncer } from '../../util/debouncer';
selector: 'ion-searchbar',
template:
'
' +
- '',
+ '',
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);
/**
@@ -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.
*/
@@ -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;
@@ -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;
+ }
+ }
}
/**
@@ -217,7 +224,7 @@ export class Searchbar extends Ion {
* @private
* After View Checked position the elements
*/
- ngAfterViewChecked() {
+ ngAfterContentInit() {
this.positionElements();
}
@@ -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 {
@@ -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);
});
@@ -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;
}
@@ -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();
}
@@ -399,4 +417,8 @@ export class Searchbar extends Ion {
registerOnTouched(fn: () => {}): void {
this.onTouched = fn;
}
+
+ setFocus() {
+ this._renderer.invokeElementMethod(this._searchbarInput.nativeElement, 'focus');
+ }
}
diff --git a/src/components/searchbar/test/basic/main.html b/src/components/searchbar/test/basic/main.html
index 02309d58241..54eee134b75 100644
--- a/src/components/searchbar/test/basic/main.html
+++ b/src/components/searchbar/test/basic/main.html
@@ -1,6 +1,9 @@
Search - Default
-
+
+
+ Search - Animated
+
defaultSearch: {{ defaultSearch }}
diff --git a/src/components/searchbar/test/nav/app-module.ts b/src/components/searchbar/test/nav/app-module.ts
index f75d1de3fa7..946da836762 100644
--- a/src/components/searchbar/test/nav/app-module.ts
+++ b/src/components/searchbar/test/nav/app-module.ts
@@ -1,5 +1,5 @@
import { Component, NgModule } from '@angular/core';
-import { IonicApp, IonicModule, NavController, NavParams } from '../../../..';
+import { IonicApp, IonicModule, ModalController, NavController, NavParams, ViewController } from '../../../..';
@Component({
@@ -13,13 +13,23 @@ export class MainPage {
}
}
+@Component({
+ templateUrl: 'modal.html'
+})
+export class ModalPage {
+ constructor(public viewCtrl: ViewController) {}
+ close() {
+ this.viewCtrl.dismiss();
+ }
+}
+
@Component({
templateUrl: 'search.html'
})
export class SearchPage {
items: string[];
- constructor(public navCtrl: NavController) {
+ constructor(public navCtrl: NavController, public modalCtrl: ModalController) {
this.initializeItems();
}
@@ -88,6 +98,11 @@ export class SearchPage {
return false;
});
}
+
+ openModal() {
+ let modal = this.modalCtrl.create(ModalPage);
+ modal.present();
+ }
}
@Component({
@@ -121,6 +136,7 @@ export class E2EApp {
E2EApp,
MainPage,
SearchPage,
+ ModalPage,
DetailPage,
TabsPage
],
@@ -132,6 +148,7 @@ export class E2EApp {
E2EApp,
MainPage,
SearchPage,
+ ModalPage,
DetailPage,
TabsPage
]
diff --git a/src/components/searchbar/test/nav/modal.html b/src/components/searchbar/test/nav/modal.html
new file mode 100644
index 00000000000..7db91bfa285
--- /dev/null
+++ b/src/components/searchbar/test/nav/modal.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Close the modal with the button and the searchbar SHOULD NOT become focused.
+
+
diff --git a/src/components/searchbar/test/nav/search.html b/src/components/searchbar/test/nav/search.html
index 8aa8be3a5bd..a3b301b4f05 100644
--- a/src/components/searchbar/test/nav/search.html
+++ b/src/components/searchbar/test/nav/search.html
@@ -1,21 +1,24 @@
- Searchbar
+
+
-
-
+ Searchbar
+
+
+
-
-
-
+