From 9a9aa5e0180cbd14a3bb5fbadb708ffbf38d003e Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 14 Jun 2016 14:14:35 -0400 Subject: [PATCH 1/5] refactor(searchbar): improve searchbar animation on ios, remove unused code references #6023 --- src/components/searchbar/searchbar.ios.scss | 28 ++- src/components/searchbar/searchbar.ts | 160 ++++++++++-------- .../searchbar/test/floating/index.ts | 1 + .../searchbar/test/floating/main.html | 3 + src/config/directives.ts | 4 +- 5 files changed, 107 insertions(+), 89 deletions(-) diff --git a/src/components/searchbar/searchbar.ios.scss b/src/components/searchbar/searchbar.ios.scss index 74a6c719ba..0b0670ea4b 100644 --- a/src/components/searchbar/searchbar.ios.scss +++ b/src/components/searchbar/searchbar.ios.scss @@ -15,12 +15,11 @@ $searchbar-ios-input-search-icon-svg: "" !default; @@ -86,7 +85,6 @@ ion-searchbar { border-radius: 5px; font-size: 1.4rem; font-weight: 400; - line-height: $searchbar-ios-input-line-height; color: $searchbar-ios-input-text-color; background-color: $searchbar-ios-input-background-color; @@ -118,17 +116,18 @@ ion-searchbar { // ----------------------------------------- .searchbar-ios-cancel { - flex: 0 0 0%; + flex-shrink: 0; - margin-right: 0; + margin-right: -100%; margin-left: 0; padding: 0; + padding-left: 8px; height: 30px; - - visibility: hidden; - transform: translateX(calc(100% + #{$searchbar-ios-padding-left-right})); + transform: translate3d(0, 0, 0); transition: $searchbar-ios-cancel-transition; + cursor: pointer; + pointer-events: none; } @@ -145,20 +144,17 @@ ion-searchbar { } } -// Searchbar Focused + +// Searchbar Has Focus // ----------------------------------------- .searchbar-has-focus { .searchbar-ios-cancel { - flex: 0 0 auto; - - padding-left: 8px; - - visibility: visible; - transform: translateX(0); + pointer-events: auto; } } + // Searchbar in Toolbar // ----------------------------------------- diff --git a/src/components/searchbar/searchbar.ts b/src/components/searchbar/searchbar.ts index ba16d14768..fda09938bb 100644 --- a/src/components/searchbar/searchbar.ts +++ b/src/components/searchbar/searchbar.ts @@ -5,17 +5,6 @@ import {Config} from '../../config/config'; import {isPresent} from '../../util/util'; -/** -* @private -*/ -@Directive({ - selector: '.searchbar-input', -}) -export class SearchbarInput { - constructor(public elementRef: ElementRef) {} -} - - /** * @name Searchbar * @module ionic @@ -39,7 +28,9 @@ export class SearchbarInput { selector: 'ion-searchbar', host: { '[class.searchbar-has-value]': '_value', - '[class.searchbar-hide-cancel]': 'hideCancelButton' + '[class.searchbar-active]': '_isActive', + '[class.searchbar-hide-cancel]': 'hideCancelButton', + '[class.searchbar-left-aligned]': 'shouldAlignLeft()' }, template: '
' + @@ -47,21 +38,18 @@ export class SearchbarInput { '' + '' + '
' + - '' + + '' + '' + '
' + - '', - directives: [SearchbarInput], + '', encapsulation: ViewEncapsulation.None }) export class Searchbar { private _value: string|number = ''; private _tmr: any; private _shouldBlur: boolean = true; - - private inputEle: any; - private iconEle: any; - private mode: string; + private _isActive: boolean = false; + private _searchbarInput: ElementRef; /** * @input {string} Set the the cancel button text. Default: `"Cancel"`. @@ -131,12 +119,7 @@ export class Searchbar { /** * @private */ - @HostBinding('class.searchbar-has-focus') isFocused: boolean; - - /** - * @private - */ - @HostBinding('class.searchbar-left-aligned') shouldLeftAlign: boolean; + @HostBinding('class.searchbar-has-focus') _sbHasFocus: boolean; constructor( private _elementRef: ElementRef, @@ -152,28 +135,32 @@ export class Searchbar { /** * @private */ - @ViewChild(SearchbarInput) - private set _searchbarInput(searchbarInput: SearchbarInput) { - this.inputEle = searchbarInput.elementRef.nativeElement; + @ViewChild('searchbarInput') + private 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'); - this.inputEle.setAttribute('autocomplete', autoComplete); + 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'); - this.inputEle.setAttribute('autocorrect', autoCorrect); + 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); - this.inputEle.setAttribute('spellcheck', spellCheck); + inputEle.setAttribute('spellcheck', spellCheck); // by default set type="search" unless specified by the input - this.inputEle.setAttribute('type', this.type); + inputEle.setAttribute('type', this.type); } @ViewChild('searchbarIcon') _searchbarIcon: ElementRef; + @ViewChild('cancelButton', {read: ElementRef}) _cancelButton: ElementRef; + /** * @input {string} Set the input value. */ @@ -191,38 +178,44 @@ export class Searchbar { * On Initialization check for attributes */ ngOnInit() { - this.mode = this._config.get('mode'); - let hideCancelButton = this.hideCancelButton; if (typeof hideCancelButton === 'string') { this.hideCancelButton = (hideCancelButton === '' || hideCancelButton === 'true'); } - - this.shouldLeftAlign = this._value && this._value.toString().trim() !== ''; } /** * @private - * After View Initialization check the value + * After View Initialization position the elements */ ngAfterViewInit() { - this.iconEle = this._searchbarIcon.nativeElement; - this.setElementLeft(); + this.positionElements(); } /** * @private - * Determines whether or not to add style to the element - * to center it properly (ios only) + * After Content is checked position the elements */ - setElementLeft() { - if (this.mode !== 'ios') return; + ngAfterContentChecked() { + this.positionElements(); + } - if (this.shouldLeftAlign) { - this.inputEle.removeAttribute('style'); - this.iconEle.removeAttribute('style'); - } else { - this.addElementLeft(); + /** + * @private + * Positions the input search icon, placeholder, and the cancel button + * based on the input value and if it is focused. (ios only) + */ + positionElements() { + if (this._config.get('mode') !== 'ios') return; + + // Position the input placeholder & search icon + if (this._searchbarInput && this._searchbarIcon) { + this.positionInputPlaceholder(this._searchbarInput.nativeElement, this._searchbarIcon.nativeElement); + } + + // Position the cancel button + if (this._cancelButton && this._cancelButton.nativeElement) { + this.positionCancelButton(this._cancelButton.nativeElement); } } @@ -231,23 +224,50 @@ export class Searchbar { * Calculates the amount of padding/margin left for the elements * in order to center them based on the placeholder width */ - addElementLeft() { - // Create a dummy span to get the placeholder width - let tempSpan = document.createElement('span'); - tempSpan.innerHTML = this.placeholder; - document.body.appendChild(tempSpan); + positionInputPlaceholder(inputEle: HTMLElement, iconEle: HTMLElement) { + if (this.shouldAlignLeft()) { + inputEle.removeAttribute('style'); + iconEle.removeAttribute('style'); + } else { + // Create a dummy span to get the placeholder width + let tempSpan = document.createElement('span'); + tempSpan.innerHTML = this.placeholder; + document.body.appendChild(tempSpan); - // Get the width of the span then remove it - let textWidth = tempSpan.offsetWidth; - tempSpan.remove(); + // Get the width of the span then remove it + let textWidth = tempSpan.offsetWidth; + tempSpan.remove(); - // Set the input padding left - let inputLeft = 'calc(50% - ' + (textWidth / 2) + 'px)'; - this.inputEle.style.paddingLeft = inputLeft; + // Set the input padding left + let inputLeft = 'calc(50% - ' + (textWidth / 2) + 'px)'; + inputEle.style.paddingLeft = inputLeft; - // Set the icon margin left - let iconLeft = 'calc(50% - ' + ((textWidth / 2) + 30) + 'px)'; - this.iconEle.style.marginLeft = iconLeft; + // Set the icon margin left + let iconLeft = 'calc(50% - ' + ((textWidth / 2) + 30) + 'px)'; + iconEle.style.marginLeft = iconLeft; + } + } + + /** + * @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'; + } else { + cancelButtonEle.style.marginRight = -cancelButtonEle.offsetWidth + '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 ); } /** @@ -267,14 +287,14 @@ export class Searchbar { /** * @private - * Sets the Searchbar to focused and aligned left on input focus. + * Sets the Searchbar to focused and active on input focus. */ inputFocused(ev: UIEvent) { this.ionFocus.emit(ev); - this.isFocused = true; - this.shouldLeftAlign = true; - this.setElementLeft(); + this._sbHasFocus = true; + this._isActive = true; + this.positionElements(); } /** @@ -286,15 +306,14 @@ export class Searchbar { // _shouldBlur determines if it should blur // if we are clearing the input we still want to stay focused in the input if (this._shouldBlur === false) { - this.inputEle.focus(); + this._searchbarInput.nativeElement.focus(); this._shouldBlur = true; return; } this.ionBlur.emit(ev); - this.isFocused = false; - this.shouldLeftAlign = this._value && this._value.toString().trim() !== ''; - this.setElementLeft(); + this._sbHasFocus = false; + this.positionElements(); } /** @@ -324,6 +343,7 @@ export class Searchbar { this.clearInput(ev); this._shouldBlur = true; + this._isActive = false; } /** diff --git a/src/components/searchbar/test/floating/index.ts b/src/components/searchbar/test/floating/index.ts index c65a75c8d7..c5823d343e 100644 --- a/src/components/searchbar/test/floating/index.ts +++ b/src/components/searchbar/test/floating/index.ts @@ -41,6 +41,7 @@ class E2EApp { ngAfterViewInit() { this.customPlaceholder = 33; + this.defaultCancel = "after view"; this.changeDetectorRef.detectChanges(); } diff --git a/src/components/searchbar/test/floating/main.html b/src/components/searchbar/test/floating/main.html index 5737183093..e987a28de2 100644 --- a/src/components/searchbar/test/floating/main.html +++ b/src/components/searchbar/test/floating/main.html @@ -23,6 +23,9 @@
Search - Custom Cancel Button Danger
+
Search - Value passed
+ +

diff --git a/src/config/directives.ts b/src/config/directives.ts index da423c9168..b00a191f20 100644 --- a/src/config/directives.ts +++ b/src/config/directives.ts @@ -36,7 +36,7 @@ import {Segment, SegmentButton} from '../components/segment/segment'; import {RadioButton} from '../components/radio/radio-button'; import {RadioGroup} from '../components/radio/radio-group'; import {Range} from '../components/range/range'; -import {Searchbar, SearchbarInput} from '../components/searchbar/searchbar'; +import {Searchbar} from '../components/searchbar/searchbar'; import {Nav} from '../components/nav/nav'; import {NavPush, NavPop} from '../components/nav/nav-push'; import {NavRouter} from '../components/nav/nav-router'; @@ -89,7 +89,6 @@ import {ShowWhen, HideWhen} from '../components/show-hide-when/show-hide-when'; * - Icon * - Spinner * - Searchbar - * - SearchbarInput * - Segment * - SegmentButton * - Checkbox @@ -164,7 +163,6 @@ export const IONIC_DIRECTIVES: any[] = [ // Forms Searchbar, - SearchbarInput, Segment, SegmentButton, Checkbox, From 614ace462d42daf0965fd07d73c30df3d0e11d25 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 14 Jun 2016 14:47:13 -0400 Subject: [PATCH 2/5] fix(searchbar): set a negative tabindex for the cancel button this prevents tabbing to the cancel button and shifting the entire screen. --- src/components/searchbar/searchbar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/searchbar/searchbar.ts b/src/components/searchbar/searchbar.ts index fda09938bb..912d41ecaf 100644 --- a/src/components/searchbar/searchbar.ts +++ b/src/components/searchbar/searchbar.ts @@ -41,7 +41,7 @@ import {isPresent} from '../../util/util'; '' + '' + '' + - '', + '', encapsulation: ViewEncapsulation.None }) export class Searchbar { From 9927cf958dedd42c49ef7c7bccda5b09f7a35884 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 14 Jun 2016 15:36:22 -0400 Subject: [PATCH 3/5] refactor(searchbar): hide the cancel button by default, add property to show BREAKING CHANGES: Searchbar - The cancel button (ios and md) is now hidden by default, passing `showCancelButton` will display it. --- src/components/searchbar/searchbar.ios.scss | 4 ++++ src/components/searchbar/searchbar.md.scss | 2 +- src/components/searchbar/searchbar.ts | 16 ++++++++-------- src/components/searchbar/test/floating/main.html | 12 ++++++------ 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/components/searchbar/searchbar.ios.scss b/src/components/searchbar/searchbar.ios.scss index 0b0670ea4b..df9cce052d 100644 --- a/src/components/searchbar/searchbar.ios.scss +++ b/src/components/searchbar/searchbar.ios.scss @@ -128,8 +128,12 @@ ion-searchbar { transition: $searchbar-ios-cancel-transition; cursor: pointer; pointer-events: none; + display: none; } +.searchbar-show-cancel .searchbar-ios-cancel { + display: block; +} // Searchbar Left Aligned (iOS only) // ----------------------------------------- diff --git a/src/components/searchbar/searchbar.md.scss b/src/components/searchbar/searchbar.md.scss index b66d1f48e5..4f5692643a 100644 --- a/src/components/searchbar/searchbar.md.scss +++ b/src/components/searchbar/searchbar.md.scss @@ -130,7 +130,7 @@ ion-searchbar { // Searchbar Focused // ----------------------------------------- -.searchbar-has-focus:not(.searchbar-hide-cancel) { +.searchbar-has-focus.searchbar-show-cancel { .searchbar-search-icon { display: none; } diff --git a/src/components/searchbar/searchbar.ts b/src/components/searchbar/searchbar.ts index 912d41ecaf..bef595cacd 100644 --- a/src/components/searchbar/searchbar.ts +++ b/src/components/searchbar/searchbar.ts @@ -15,7 +15,7 @@ import {isPresent} from '../../util/util'; * ```html * * @@ -29,19 +29,19 @@ import {isPresent} from '../../util/util'; host: { '[class.searchbar-has-value]': '_value', '[class.searchbar-active]': '_isActive', - '[class.searchbar-hide-cancel]': 'hideCancelButton', + '[class.searchbar-show-cancel]': 'showCancelButton', '[class.searchbar-left-aligned]': 'shouldAlignLeft()' }, template: '
' + - '' + '
' + '' + '' + '
' + - '', + '', encapsulation: ViewEncapsulation.None }) export class Searchbar { @@ -59,7 +59,7 @@ export class Searchbar { /** * @input {boolean} Whether to hide the cancel button or not. Default: `"false"`. */ - @Input() hideCancelButton: any = false; + @Input() showCancelButton: any = false; /** * @input {number} How long, in milliseconds, to wait to trigger the `input` event after each keystroke. Default `250`. @@ -178,9 +178,9 @@ export class Searchbar { * On Initialization check for attributes */ ngOnInit() { - let hideCancelButton = this.hideCancelButton; - if (typeof hideCancelButton === 'string') { - this.hideCancelButton = (hideCancelButton === '' || hideCancelButton === 'true'); + let showCancelButton = this.showCancelButton; + if (typeof showCancelButton === 'string') { + this.showCancelButton = (showCancelButton === '' || showCancelButton === 'true'); } } diff --git a/src/components/searchbar/test/floating/main.html b/src/components/searchbar/test/floating/main.html index e987a28de2..bd598fc112 100644 --- a/src/components/searchbar/test/floating/main.html +++ b/src/components/searchbar/test/floating/main.html @@ -1,30 +1,30 @@
Search - Default
- +

defaultSearch: {{ defaultSearch }}

Search - Custom Placeholder
- +

customPlaceholder: {{ customPlaceholder }}

-
Search - Hide Cancel Button
- +
Search - No Cancel Button
+

defaultCancel: {{ defaultCancel }}

Search - Custom Cancel Button Danger
- +
Search - Value passed
- +

From b5f93f95c0b5b0076078adf5f379b7aaa22353d1 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 14 Jun 2016 16:01:33 -0400 Subject: [PATCH 4/5] fix(searchbar): add opacity so the searchbar doesn't show when it's moved over --- src/components/searchbar/searchbar.ios.scss | 2 ++ src/components/searchbar/test/toolbar/main.html | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/searchbar/searchbar.ios.scss b/src/components/searchbar/searchbar.ios.scss index df9cce052d..f5aeb55a3d 100644 --- a/src/components/searchbar/searchbar.ios.scss +++ b/src/components/searchbar/searchbar.ios.scss @@ -129,6 +129,7 @@ ion-searchbar { cursor: pointer; pointer-events: none; display: none; + opacity: 0; } .searchbar-show-cancel .searchbar-ios-cancel { @@ -155,6 +156,7 @@ ion-searchbar { .searchbar-has-focus { .searchbar-ios-cancel { pointer-events: auto; + opacity: 1; } } diff --git a/src/components/searchbar/test/toolbar/main.html b/src/components/searchbar/test/toolbar/main.html index d036151f66..7a40104212 100644 --- a/src/components/searchbar/test/toolbar/main.html +++ b/src/components/searchbar/test/toolbar/main.html @@ -10,7 +10,7 @@

Search - Primary Toolbar
- +
Search - Dark Toolbar
@@ -20,7 +20,7 @@
Search - Light Toolbar
- +
From 5733b0def64a39283b7d719aabf0a9806191021b Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 14 Jun 2016 16:16:32 -0400 Subject: [PATCH 5/5] style: fix linter errors --- src/components/searchbar/searchbar.ios.scss | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/searchbar/searchbar.ios.scss b/src/components/searchbar/searchbar.ios.scss index f5aeb55a3d..c8f1f4ba7a 100644 --- a/src/components/searchbar/searchbar.ios.scss +++ b/src/components/searchbar/searchbar.ios.scss @@ -116,6 +116,8 @@ ion-searchbar { // ----------------------------------------- .searchbar-ios-cancel { + display: none; + flex-shrink: 0; margin-right: -100%; @@ -124,12 +126,15 @@ ion-searchbar { padding-left: 8px; height: 30px; + + cursor: pointer; + + opacity: 0; + transform: translate3d(0, 0, 0); transition: $searchbar-ios-cancel-transition; - cursor: pointer; + pointer-events: none; - display: none; - opacity: 0; } .searchbar-show-cancel .searchbar-ios-cancel { @@ -155,8 +160,9 @@ ion-searchbar { .searchbar-has-focus { .searchbar-ios-cancel { - pointer-events: auto; opacity: 1; + + pointer-events: auto; } }