diff --git a/demos/scrollbar-fix.css b/demos/scrollbar-fix.css new file mode 100644 index 0000000000..dd56aaad3f --- /dev/null +++ b/demos/scrollbar-fix.css @@ -0,0 +1,6 @@ +body.has-scrollbar scroll-content { + margin-right: -15px; +} +body.has-scrollbar ion-menu scroll-content { + margin-right: 0; +} \ No newline at end of file diff --git a/demos/scrollbar-fix.js b/demos/scrollbar-fix.js new file mode 100644 index 0000000000..2d6944b680 --- /dev/null +++ b/demos/scrollbar-fix.js @@ -0,0 +1,45 @@ +(function(){ + + function hasScrollbar() { + + if (typeof window.top.innerWidth === 'number') { + return window.top.innerWidth > window.top.document.documentElement.clientWidth; + } + + // rootElem for quirksmode + var rootElem = window.top.document.documentElement || window.top.document.body; + + // Check overflow style property on body for fauxscrollbars + var overflowStyle; + + if (typeof rootElem.currentStyle !== 'undefined') { + overflowStyle = rootElem.currentStyle.overflow; + } + + overflowStyle = overflowStyle || window.top.getComputedStyle(rootElem, '').overflow; + + // Also need to check the Y axis overflow + var overflowYStyle; + + if (typeof rootElem.currentStyle !== 'undefined') { + overflowYStyle = rootElem.currentStyle.overflowY; + } + + overflowYStyle = overflowYStyle || window.top.getComputedStyle(rootElem, '').overflowY; + + var contentOverflows = rootElem.scrollHeight > rootElem.clientHeight; + var overflowShown = /^(visible|auto)$/.test(overflowStyle) || /^(visible|auto)$/.test(overflowYStyle); + var alwaysShowScroll = overflowStyle === 'scroll' || overflowYStyle === 'scroll'; + + return (contentOverflows && overflowShown) || (alwaysShowScroll) + } + + + if (hasScrollbar() === true) { + setTimeout(function() { + var body = document.getElementsByTagName('body')[0]; + body.className = body.className + ' has-scrollbar'; + }, 500); + } + +})(); \ No newline at end of file diff --git a/ionic/animations/animation.ts b/ionic/animations/animation.ts index 7d053f956d..71bda70c1c 100644 --- a/ionic/animations/animation.ts +++ b/ionic/animations/animation.ts @@ -772,8 +772,6 @@ function inlineStyle(ele, effect) { if (transforms.length) { transforms.push('translateZ(0px)'); ele.style[CSS.transform] = transforms.join(' '); - } else { - ele.style[CSS.transform] = 'translateZ(0px)'; } } } diff --git a/ionic/components/searchbar/searchbar.ios.scss b/ionic/components/searchbar/searchbar.ios.scss index 563ecdb0f9..affe1fb6a5 100644 --- a/ionic/components/searchbar/searchbar.ios.scss +++ b/ionic/components/searchbar/searchbar.ios.scss @@ -70,7 +70,6 @@ ion-searchbar { border-radius: 5px; color: $searchbar-ios-input-text-color; background-color: $searchbar-ios-input-background-color; - background-position: 8px center; @include placeholder($searchbar-ios-input-placeholder-color); diff --git a/ionic/components/searchbar/searchbar.ts b/ionic/components/searchbar/searchbar.ts index 47bf07d433..a696d0b689 100644 --- a/ionic/components/searchbar/searchbar.ts +++ b/ionic/components/searchbar/searchbar.ts @@ -46,13 +46,16 @@ export class SearchbarInput { * @property {boolean} [hideCancelButton=false] - Hides the cancel button * @property {string} [placeholder=Search] - Sets input placeholder to the value passed in * - * @property {Any} [input] - Expression to evaluate when the Searchbar input has changed + * @property {Any} [input] - Expression to evaluate when the Searchbar input has changed including cleared + * @property {Any} [keydown] - Expression to evaluate when a key is pushed down in the Searchbar input + * @property {Any} [keypress] - Expression to evaluate when a character is inserted in the Searchbar input + * @property {Any} [keyup] - Expression to evaluate when a key is released in the Searchbar input * @property {Any} [blur] - Expression to evaluate when the Searchbar input has blurred * @property {Any} [focus] - Expression to evaluate when the Searchbar input has focused - * @property {Any} [cancel] - Expression to evaluate when the cancel button is clicked. - * @property {Any} [clear] - Expression to evaluate when the clear input button is clicked. + * @property {Any} [cancel] - Expression to evaluate when the cancel button is clicked + * @property {Any} [clear] - Expression to evaluate when the clear input button is clicked * - * @see {@link /docs/v2/components#search Search Component Docs} + * @see {@link /docs/v2/components#searchbar Searchbar Component Docs} */ @Component({ selector: 'ion-searchbar', @@ -71,35 +74,50 @@ export class SearchbarInput { export class Searchbar extends Ion { @ViewChild(SearchbarInput) searchbarInput; + /** + * @private + */ @Input() cancelButtonText: string; + /** + * @private + */ @Input() hideCancelButton: any; + /** + * @private + */ @Input() placeholder: string; + /** + * @private + */ @Input() ngModel: any; + /** + * @private + */ @Output() input: EventEmitter = new EventEmitter(); + /** + * @private + */ @Output() blur: EventEmitter = new EventEmitter(); + /** + * @private + */ @Output() focus: EventEmitter = new EventEmitter(); + /** + * @private + */ @Output() cancel: EventEmitter = new EventEmitter(); + /** + * @private + */ @Output() clear: EventEmitter = new EventEmitter(); value: string = ''; blurInput: boolean = true; @HostBinding('class.searchbar-focused') isFocused; - @HostBinding('class.searchbar-left-aligned') shouldLeftAlign; - @HostListener('keyup', ['$event']) - /** - * @private - * Update the Searchbar input value when the input changes - */ - private inputChanged(ev) { - this.value = ev.target.value; - this.onChange(this.value); - this.input.emit(this); - } - constructor( elementRef: ElementRef, config: Config, @@ -130,6 +148,11 @@ export class Searchbar extends Ion { this.onChange(this.value); this.shouldLeftAlign = this.value && this.value.trim() != ''; + + // Using querySelector instead of searchbarInput because at this point it doesn't exist + this.inputElement = this.elementRef.nativeElement.querySelector('.searchbar-input'); + this.searchIconElement = this.elementRef.nativeElement.querySelector('.searchbar-search-icon'); + this.setElementLeft(); } /** @@ -146,6 +169,54 @@ export class Searchbar extends Ion { } } + /** + * @private + * Determines whether or not to add style to the element + * to center it properly + */ + setElementLeft() { + if (this.shouldLeftAlign) { + this.inputElement.removeAttribute("style"); + this.searchIconElement.removeAttribute("style"); + } else { + this.addElementLeft(); + } + } + + /** + * @private + * 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); + + // 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.inputElement.style.paddingLeft = inputLeft; + + // Set the icon margin left + let iconLeft = "calc(50% - " + ((textWidth / 2) + this.searchIconElement.offsetWidth + 15) + "px)"; + this.searchIconElement.style.marginLeft = iconLeft; + } + + /** + * @private + * Update the Searchbar input value when the input changes + */ + inputChanged(ev) { + this.value = ev.target.value; + this.onChange(this.value); + this.input.emit(this); + } + /** * @private * Sets the Searchbar to focused and aligned left on input focus. @@ -155,6 +226,7 @@ export class Searchbar extends Ion { this.isFocused = true; this.shouldLeftAlign = true; + this.setElementLeft(); } /** @@ -171,9 +243,10 @@ export class Searchbar extends Ion { return; } this.blur.emit(this); - + this.isFocused = false; this.shouldLeftAlign = this.value && this.value.trim() != ''; + this.setElementLeft(); } /** @@ -185,6 +258,8 @@ export class Searchbar extends Ion { this.value = ''; this.onChange(this.value); + this.input.emit(this); + this.blurInput = false; } diff --git a/ionic/components/searchbar/test/floating/index.ts b/ionic/components/searchbar/test/floating/index.ts index c2b119d666..1cb395d5a6 100644 --- a/ionic/components/searchbar/test/floating/index.ts +++ b/ionic/components/searchbar/test/floating/index.ts @@ -17,22 +17,22 @@ class E2EApp { } onClearSearchbar(searchbar) { - console.log("Clicked clear input on", searchbar); + console.log("Clicked clear input on", searchbar.value); } onCancelSearchbar(searchbar) { - console.log("Clicked cancel button with", searchbar); + console.log("Clicked cancel button with", searchbar.value); } triggerInput(searchbar) { - console.log("Triggered input", searchbar); + console.log("Triggered input", searchbar.value); } inputBlurred(searchbar) { - console.log("Blurred input", searchbar); + console.log("Blurred input", searchbar.value); } inputFocused(searchbar) { - console.log("Focused input", searchbar); + console.log("Focused input", searchbar.value); } } diff --git a/ionic/components/segment/segment.ts b/ionic/components/segment/segment.ts index 16764c152d..0af9a8cc99 100644 --- a/ionic/components/segment/segment.ts +++ b/ionic/components/segment/segment.ts @@ -55,7 +55,7 @@ import {isDefined} from '../../util/util'; }) export class SegmentButton { @Input() value: string; - @Output() select: EventEmitter = new EventEmitter(); + @Output() select: EventEmitter = new EventEmitter(); constructor(private _renderer: Renderer, private _elementRef: ElementRef) {} @@ -66,7 +66,7 @@ export class SegmentButton { @HostListener('click', ['$event']) private onClick(ev) { console.debug('SegmentButton, select', this.value); - this.select.emit(ev, this.value); + this.select.emit(this); } ngOnInit() { @@ -131,8 +131,8 @@ export class SegmentButton { selector: 'ion-segment' }) export class Segment { - @Output() change: EventEmitter = new EventEmitter(); @ContentChildren(SegmentButton) _buttons; + @Output() change: EventEmitter = new EventEmitter(); value: any; constructor( @@ -166,10 +166,10 @@ export class Segment { ngAfterViewInit() { let buttons = this._buttons.toArray(); for (let button of buttons) { - button.select.subscribe(() => { - this.writeValue(button.value); - this.onChange(button.value); - this.change.emit(this.value); + button.select.subscribe((selectedButton) => { + this.writeValue(selectedButton.value); + this.onChange(selectedButton.value); + this.change.emit(selectedButton); }); if (isDefined(this.value)) { diff --git a/ionic/components/segment/test/basic/index.ts b/ionic/components/segment/test/basic/index.ts index 166217b2b1..3b77594c40 100644 --- a/ionic/components/segment/test/basic/index.ts +++ b/ionic/components/segment/test/basic/index.ts @@ -20,12 +20,12 @@ class MyApp { this.appType = 'free'; } - onSegmentChanged(value) { - console.log("Segment changed to", value); + onSegmentChanged(segmentButton) { + console.log("Segment changed to", segmentButton.value); } - onSegmentClicked(value) { - console.log("Segment clicked", value); + onSegmentSelected(segmentButton) { + console.log("Segment selected", segmentButton.value); } doSubmit(event) { diff --git a/ionic/components/segment/test/basic/main.html b/ionic/components/segment/test/basic/main.html index 5e8442bfbc..2a43dff194 100644 --- a/ionic/components/segment/test/basic/main.html +++ b/ionic/components/segment/test/basic/main.html @@ -1,9 +1,9 @@ - + Friends - + Enemies diff --git a/ionic/components/text-input/test/form-inputs/main.html b/ionic/components/text-input/test/form-inputs/main.html index 9dc613e003..e045e73407 100644 --- a/ionic/components/text-input/test/form-inputs/main.html +++ b/ionic/components/text-input/test/form-inputs/main.html @@ -7,22 +7,22 @@
- + Email - + Username - + Password - + Comments @@ -43,15 +43,15 @@ - + Username - + Password -
+
@@ -63,4 +63,26 @@ + + + Email + + + + + Username + + + + + Password + + + + + Comments + + + + diff --git a/ionic/components/text-input/text-input.ios.scss b/ionic/components/text-input/text-input.ios.scss index 65a74e438c..f16eddfd79 100644 --- a/ionic/components/text-input/text-input.ios.scss +++ b/ionic/components/text-input/text-input.ios.scss @@ -4,7 +4,12 @@ // iOS Text Input // -------------------------------------------------- -$text-input-ios-background-color: $list-ios-background-color !default; +$text-input-ios-background-color: $list-ios-background-color !default; + +$text-input-ios-input-clear-icon-width: 30px !default; +$text-input-ios-input-clear-icon-color: rgba(0, 0, 0, 0.5) !default; +$text-input-ios-input-clear-icon-svg: "" !default; +$text-input-ios-input-clear-icon-size: 18px !default; // Default Input @@ -34,3 +39,23 @@ $text-input-ios-background-color: $list-ios-background-color !default; margin-top: 8px; margin-bottom: 8px; } + +// Clear Input Icon +// -------------------------------------------------- + +ion-input[clearInput] { + position: relative; + + .text-input { + padding-right: $text-input-ios-input-clear-icon-width; + } +} + +.text-input-clear-icon { + width: $text-input-ios-input-clear-icon-width; + + @include svg-background-image($text-input-ios-input-clear-icon-svg); + background-size: $text-input-ios-input-clear-icon-size; + right: ($item-ios-padding-right / 2); + bottom: 0; +} diff --git a/ionic/components/text-input/text-input.md.scss b/ionic/components/text-input/text-input.md.scss index f090bfc143..99f96198a7 100644 --- a/ionic/components/text-input/text-input.md.scss +++ b/ionic/components/text-input/text-input.md.scss @@ -9,6 +9,12 @@ $text-input-md-highlight-color: map-get($colors-md, primary) !defaul $text-input-md-hightlight-color-valid: map-get($colors-md, secondary) !default; $text-input-md-hightlight-color-invalid: map-get($colors-md, danger) !default; +$text-input-md-input-clear-icon-width: 30px !default; +$text-input-md-input-clear-icon-color: #5B5B5B !default; +$text-input-md-input-clear-icon-svg: "" !default; +$text-input-md-input-clear-icon-size: 22px !default; + + // Default Input // -------------------------------------------------- @@ -65,3 +71,24 @@ ion-input.ng-invalid.ng-touched:after { margin-top: 8px; margin-bottom: 8px; } + + +// Clear Input Icon +// -------------------------------------------------- + +ion-input[clearInput] { + position: relative; + + .text-input { + padding-right: $text-input-md-input-clear-icon-width; + } +} + +.text-input-clear-icon { + width: $text-input-md-input-clear-icon-width; + + @include svg-background-image($text-input-md-input-clear-icon-svg); + background-size: $text-input-md-input-clear-icon-size; + right: ($item-md-padding-right / 2); + bottom: 2px; +} diff --git a/ionic/components/text-input/text-input.scss b/ionic/components/text-input/text-input.scss index 4ddcc82d3e..23cc93ae54 100644 --- a/ionic/components/text-input/text-input.scss +++ b/ionic/components/text-input/text-input.scss @@ -61,3 +61,15 @@ input, textarea { @include placeholder(); } + +// Clear Input Icon +// -------------------------------------------------- + +.text-input-clear-icon { + margin: 0; + padding: 0; + + background-repeat: no-repeat; + background-position: center; + position: absolute; +} diff --git a/ionic/components/text-input/text-input.ts b/ionic/components/text-input/text-input.ts index 78cc86fc9f..35d82fc37e 100644 --- a/ionic/components/text-input/text-input.ts +++ b/ionic/components/text-input/text-input.ts @@ -1,24 +1,30 @@ -import {Component, Directive, Attribute, forwardRef, Host, Optional, ElementRef, Renderer} from 'angular2/core'; +import {Component, Directive, Attribute, forwardRef, Host, Optional, ElementRef, Renderer, Input, ContentChild} from 'angular2/core'; import {NgIf} from 'angular2/common'; import {NavController} from '../nav/nav-controller'; import {Config} from '../../config/config'; import {Form} from '../../util/form'; -import {Label} from './label'; +import {Label} from '../label/label'; import {IonicApp} from '../app/app'; import {Content} from '../content/content'; import * as dom from '../../util/dom'; import {Platform} from '../../platform/platform'; +import {Button} from '../button/button'; /** * @name Input * @module ionic * @description - * `ionInput` is a generic wrapper for both inputs and textareas. You can give `ion-input` to tell it how to handle a chile `ion-label` component - * @property [fixed-labels] - a persistant label that sits next the the input - * @property [floating-labels] - a label that will float about the input if the input is empty of looses focus - * @property [stacked-labels] - A stacked label will always appear on top of the input + * + * `ion-input` is a generic wrapper for both inputs and textareas. You can give `ion-input` attributes to tell it how to handle a child `ion-label` component. + * + * @property [fixed-label] - a persistant label that sits next the the input + * @property [floating-label] - a label that will float about the input if the input is empty of looses focus + * @property [stacked-label] - A stacked label will always appear on top of the input + * @property [inset] - The input will be inset + * @property [clearInput] - A clear icon will appear in the input which clears it + * * @usage * ```html * @@ -26,7 +32,7 @@ import {Platform} from '../../platform/platform'; * * * - * + * * * * @@ -61,10 +67,19 @@ import {Platform} from '../../platform/platform'; '
' + '' + '' + + '' + '
', - directives: [NgIf, forwardRef(() => InputScrollAssist)] + directives: [NgIf, forwardRef(() => InputScrollAssist), forwardRef(() => TextInputElement), Button] }) export class TextInput { + @ContentChild(forwardRef(() => TextInputElement)) textInputElement; + + /** + * @private + */ + @Input() clearInput: any; + + value: any = ''; constructor( form: Form, @@ -130,12 +145,23 @@ export class TextInput { this.label = label; } + /** + * @private + * On Initialization check for attributes + */ + ngOnInit() { + let clearInput = this.clearInput; + if (typeof clearInput === 'string') { + this.clearInput = (clearInput === '' || clearInput === 'true'); + } + } + /** * @private */ ngAfterViewInit() { if (this.input && this.label) { - // if there is an input and an label + // if there is an input and a label // then give the label an ID // and tell the input the ID of who it's labelled by this.input.labelledBy(this.label.id); @@ -160,6 +186,14 @@ export class TextInput { }; } + /** + * @private + */ + clearTextInput() { + console.log("Should clear input"); + console.log(this.textInputElement.value); + } + /** * @private */ @@ -398,6 +432,7 @@ export class TextInput { */ hasValue(inputValue) { this.renderer.setElementClass(this.elementRef, 'input-has-value', inputValue && inputValue !== ''); + this.value = inputValue; } /** @@ -470,7 +505,7 @@ export class TextInputElement { @Attribute('type') type: string, elementRef: ElementRef, renderer: Renderer, - @Optional() wrapper: TextInput, + @Optional() wrapper: TextInput ) { this.type = type; this.elementRef = elementRef; diff --git a/ionic/config/config.ts b/ionic/config/config.ts index 43eef4c5c8..121efbc7d3 100644 --- a/ionic/config/config.ts +++ b/ionic/config/config.ts @@ -90,7 +90,7 @@ import {isObject, isDefined, isFunction, isArray, extend} from '../util/util'; * | pageTransitionDelay | 16 | 120 | * | popupEnter | popup-pop-in | popup-md-pop-in | * | popupLeave | popup-pop-out | popup-md-pop-out | - * | tabbarPlacement | bottom | true | + * | tabbarPlacement | bottom | top | * | tabbarHighlight | | top | * | tabSubPage | | true | * diff --git a/scripts/docs/templates/common.template.html b/scripts/docs/templates/common.template.html index 1e97d0e997..c90fd49560 100644 --- a/scripts/docs/templates/common.template.html +++ b/scripts/docs/templates/common.template.html @@ -28,7 +28,7 @@ angular_controller: APIDemoCtrl <@ endif @> <@- endmacro -@> <@ macro paramTable(params, isDirective) -@> - +
@@ -79,15 +79,6 @@ angular_controller: APIDemoCtrl <@ endif @> <@ block body @> - <@ block content @> @@ -117,6 +108,10 @@ Delegate: <$ doc.delegate $> + +Improve this doc + + <@ if doc.codepen @> {% include codepen.html id="<$ doc.codepen $>" %} <@ endif @> @@ -212,7 +207,10 @@ Delegate: <$ doc.delegate $> <@ endif @> <@ if method.returns @> -* Returns: <$ typeInfo(method.returns) $> +
+ +Returns: <$ typeInfo(method.returns) $> +
<@ endif @> <@ endfor -@> diff --git a/tooling/generate.js b/tooling/generate.js index 7ac82c0e83..91272423e2 100644 --- a/tooling/generate.js +++ b/tooling/generate.js @@ -68,7 +68,7 @@ Generate.generate = function generate(options) { Generate.defaultTemplates = function defaultTemplates(options) { var template = options.template ? options.template : 'page'; - options.rootDirectory = options.rootDirectory || path.join('www', 'app'); + options.rootDirectory = options.rootDirectory || path.join('app'); var savePath = path.join(options.appDirectory, options.rootDirectory, options.fileName); var templates = Generate.loadGeneratorTemplates(path.join(__dirname, 'generators', options.template));
<@ if isDirective @>Attr<@ else @>Param<@ endif @>