From ff926134cd31722f46039cab51d59e936bbee75a Mon Sep 17 00:00:00 2001 From: Mrtcndkn Date: Fri, 22 Apr 2016 16:17:10 +0300 Subject: [PATCH] Reimplement InputSwitch as a native component --- components/dom/domhandler.ts | 72 ++++++--- components/inputswitch/inputswitch.ts | 172 ++++++++++++++------- showcase/demo/inputswitch/inputswitch.html | 2 +- 3 files changed, 168 insertions(+), 78 deletions(-) diff --git a/components/dom/domhandler.ts b/components/dom/domhandler.ts index 3f5dde09978..0d0e2293c48 100644 --- a/components/dom/domhandler.ts +++ b/components/dom/domhandler.ts @@ -2,21 +2,21 @@ import {Injectable} from 'angular2/core'; @Injectable() export class DomHandler { - + public addClass(element: any, className: string):void { if (element.classList) element.classList.add(className); else element.className += ' ' + className; } - + public addMultipleClasses(element: any, className: string):void { if (element.classList) { let styles: string[] = className.split(' '); for(let i = 0; i < styles.length; i++) { element.classList.add(styles[i]); } - + } else { let styles: string[] = className.split(' '); @@ -25,35 +25,35 @@ export class DomHandler { } } } - + public removeClass(element: any, className: string):void { if (element.classList) element.classList.remove(className); else element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); } - + public hasClass(element: any, className: string):boolean { if (element.classList) return element.classList.contains(className); else return new RegExp('(^| )' + className + '( |$)', 'gi').test(element.className); } - + public siblings(element: any):any { return Array.prototype.filter.call(element.parentNode.children, function(child){ return child !== element; }); } - + public find(element: any, selector: string):any[] { return element.querySelectorAll(selector); } - + public findSingle(element: any, selector: string):any { return element.querySelector(selector); } - + public index(element: any): number { let children = element.parentNode.childNodes; let num = 0; @@ -63,22 +63,22 @@ export class DomHandler { } return -1; } - + public relativePosition(element: any, target: any):void { let elementOuterHeight = element.offsetParent ? element.offsetHeight : this.getHiddenElementOuterHeight(element); let targetHeight = target.offsetHeight; let targetOffset = target.getBoundingClientRect(); let top; - + if((targetOffset.top + targetHeight + elementOuterHeight) > window.innerHeight) top = -1* (elementOuterHeight); else top = targetHeight; - + element.style.top = top+ 'px'; element.style.left = 0 + 'px'; } - + public absolutePosition(element: any, target: any): void { let elementOuterHeight = element.offsetParent ? element.offsetHeight : this.getHiddenElementOuterHeight(element); let targetOuterHeight = target.offsetHeight; @@ -90,22 +90,22 @@ export class DomHandler { top = targetOffset.top + windowScrollTop - elementOuterHeight; else top = targetOuterHeight + targetOffset.top + windowScrollTop; - + element.style.top = top + 'px'; element.style.left = targetOffset.left + 'px'; } - + public getHiddenElementOuterHeight(element: any): number { element.style.visibility = 'hidden'; element.style.display = 'block'; let elementHeight = element.offsetHeight; element.style.display = 'none'; element.style.visibility = 'visible'; - + return elementHeight; } - - public scrollInView(container, item) { + + public scrollInView(container, item) { let borderTopValue: string = getComputedStyle(container).getPropertyValue('borderTopWidth'); let borderTop: number = borderTopValue ? parseFloat(borderTopValue) : 0; let paddingTopValue: string = getComputedStyle(container).getPropertyValue('paddingTop'); @@ -116,7 +116,7 @@ export class DomHandler { let scroll = container.scrollTop; let elementHeight = container.clientHeight; let itemHeight = this.getOuterHeight(item); - + if(offset < 0) { container.scrollTop = scroll + offset; } @@ -124,7 +124,7 @@ export class DomHandler { container.scrollTop = scroll + offset - elementHeight + itemHeight; } } - + public getOuterHeight(element): number { let height: number = element.offsetHeight; let style: any = getComputedStyle(element); @@ -132,7 +132,7 @@ export class DomHandler { height += parseInt(style.marginTop) + parseInt(style.marginBottom); return height; } - + public fadeIn(element, duration: number):void { element.style.opacity = 0; @@ -148,12 +148,12 @@ export class DomHandler { tick(); } - + public getWindowScrollTop(): number { let doc = document.documentElement; return (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); } - + public matches(element, selector: string): boolean { var p = Element.prototype; var f = p['matches']||p.webkitMatchesSelector||p['mozMatchesSelector']||p.msMatchesSelector||function(s) { @@ -161,4 +161,28 @@ export class DomHandler { }; return f.call(element, selector); } -} \ No newline at end of file + + public outerWidth(el) { + let width = el.offsetWidth; + let style = getComputedStyle(el); + + width += parseInt(style.paddingLeft) + parseInt(style.paddingRight); + return width; + } + + public innerWidth(el) { + let width = el.offsetWidth; + let style = getComputedStyle(el); + + width += parseInt(style.paddingLeft) + parseInt(style.paddingRight); + return width; + } + + public width(el) { + let width = el.offsetWidth; + let style = getComputedStyle(el); + + width -= parseInt(style.paddingLeft) + parseInt(style.paddingRight); + return width; + } +} diff --git a/components/inputswitch/inputswitch.ts b/components/inputswitch/inputswitch.ts index a8a0750056e..7ad4d883047 100644 --- a/components/inputswitch/inputswitch.ts +++ b/components/inputswitch/inputswitch.ts @@ -1,5 +1,6 @@ -import {Component,ElementRef,AfterViewInit,OnDestroy,OnChanges,Input,Output,SimpleChange,EventEmitter,forwardRef,Provider} from 'angular2/core'; -import {NG_VALUE_ACCESSOR,ControlValueAccessor} from 'angular2/common'; +import {Component,ElementRef,AfterViewInit,OnChanges,Input,forwardRef,Provider,EventEmitter,Output} from 'angular2/core'; +import {NG_VALUE_ACCESSOR,ControlValueAccessor,NgStyle} from 'angular2/common'; +import {DomHandler} from '../dom/domhandler'; import {CONST_EXPR} from 'angular2/src/facade/lang'; const INPUTSWITCH_VALUE_ACCESSOR: Provider = CONST_EXPR( @@ -12,90 +13,155 @@ const INPUTSWITCH_VALUE_ACCESSOR: Provider = CONST_EXPR( @Component({ selector: 'p-inputSwitch', template: ` -
+
- {{offLabel}} + {{offLabel}}
- {{onLabel}} + {{onLabel}}
-
+
- +
`, - providers: [INPUTSWITCH_VALUE_ACCESSOR] + providers: [INPUTSWITCH_VALUE_ACCESSOR,DomHandler], + directives: [NgStyle] }) -export class InputSwitch implements AfterViewInit, OnDestroy, OnChanges { +export class InputSwitch implements ControlValueAccessor, AfterViewInit { @Input() onLabel: string = 'On'; @Input() offLabel: string = 'Off'; - + + @Input() disabled: boolean; + @Input() style: string; - + @Input() styleClass: string; @Output() onChange: EventEmitter = new EventEmitter(); - + value: boolean; - + + checked: boolean = false; + + focus: boolean = false; + onModelChange: Function = () => {}; - + onModelTouched: Function = () => {}; - initialized: boolean; + private container: any; - inputSwitchElement: any; + private handle: any; - constructor(private el: ElementRef) { - this.initialized = false; - } + private onContainer: any; + + private offContainer: any; + + private onLabelChild: any; + + private offLabelChild: any; + + private offset: any; + + constructor(private el: ElementRef, private domHandler: DomHandler) {} ngAfterViewInit() { - this.inputSwitchElement = jQuery(this.el.nativeElement.children[0]).find('> .ui-helper-hidden-accessible > input'); - this.inputSwitchElement.puiswitch({ - checked: this.value, - enhanced: true, - change: (event: Event, ui: any) => { - this.value = ui.checked; - this.onModelChange(this.value); - if (this.onChange) { - this.onChange.emit({originalEvent: event, checked: this.value}); - } - } - }); - this.initialized = true; + this.handleDimensions(); } - - writeValue(value: any) : void { - this.value = value; + + handleDimensions() { + this.container = this.el.nativeElement.children[0]; + this.handle = this.domHandler.findSingle(this.el.nativeElement, 'div.ui-inputswitch-handle'); + this.onContainer = this.domHandler.findSingle(this.container,'div.ui-inputswitch-on'); + this.offContainer = this.domHandler.findSingle(this.container,'div.ui-inputswitch-off'); + this.onLabelChild = this.domHandler.findSingle(this.onContainer,'span.ui-inputswitch-onlabel'); + this.offLabelChild = this.domHandler.findSingle(this.offContainer,'span.ui-inputswitch-offlabel'); + + let onContainerWidth = this.domHandler.width(this.onContainer), + offContainerWidth = this.domHandler.width(this.offContainer), + spanPadding = this.domHandler.innerWidth(this.offLabelChild) - this.domHandler.width(this.offLabelChild), + handleMargins = this.domHandler.outerWidth(this.handle) - this.domHandler.innerWidth(this.handle); - if(this.initialized) { - this.inputSwitchElement.puiswitch('option', 'checked', this.value); + var containerWidth = (onContainerWidth > offContainerWidth) ? onContainerWidth : offContainerWidth, + handleWidth = containerWidth; + + this.handle.style.width = handleWidth + 'px'; + handleWidth = this.domHandler.width(this.handle); + containerWidth = containerWidth + handleWidth + 6; + + var labelWidth = containerWidth - handleWidth - spanPadding - handleMargins; + + this.container.style.width = containerWidth + 'px'; + this.onLabelChild.style.width = labelWidth + 'px'; + this.offLabelChild.style.width = labelWidth + 'px'; + //position + this.offContainer.style.width = (this.domHandler.width(this.container) - 5) + 'px'; + this.offset = this.domHandler.width(this.container) - this.domHandler.outerWidth(this.handle); + + //default value + if(this.checked) { + this.handle.style.left = this.offset + 'px'; + this.onContainer.style.width = this.offset + 'px'; + this.offLabelChild.style.marginRight = -this.offset + 'px'; + } + else { + this.onContainer.style.width = 0 + 'px'; + this.onLabelChild.style.marginLeft = -this.offset + 'px'; } } - - registerOnChange(fn: Function): void { - this.onModelChange = fn; + + toggle(event) { + if(!this.disabled) { + this.focus = true; + this.checked = !this.checked; + this.checked ? this.checkUI() : this.uncheckUI(); + this.onModelChange(this.checked); + this.onModelTouched(); + this.onChange.emit({ + originalEvent: event, + checked: this.checked + }) + } } - registerOnTouched(fn: Function): void { - this.onModelTouched = fn; + checkUI() { + this.onContainer.style.width = this.offset + 'px'; + this.onLabelChild.style.marginLeft = 0 + 'px'; + this.offLabelChild.style.marginRight = -this.offset + 'px'; + this.handle.style.left = this.offset + 'px'; } - ngOnChanges(changes: { [key: string]: SimpleChange }) { - if (this.initialized) { - for (var key in changes) { - this.inputSwitchElement.puiswitch('option', key, changes[key].currentValue); - } - } + uncheckUI() { + this.onContainer.style.width = 0 + 'px'; + this.onLabelChild.style.marginLeft = -this.offset + 'px'; + this.offLabelChild.style.marginRight = 0 + 'px'; + this.handle.style.left = 0 + 'px'; + } + + onFocus(event) { + this.focus = true; + } + + onBlur(event) { + this.focus = false; + this.onModelTouched(); + } + + writeValue(value: any) : void { + this.value = value; } - ngOnDestroy() { - this.inputSwitchElement.puiswitch('destroy'); - this.initialized = false; - this.inputSwitchElement = null; + registerOnChange(fn: Function): void { + this.onModelChange = fn; + } + + registerOnTouched(fn: Function): void { + this.onModelTouched = fn; } -} \ No newline at end of file +} diff --git a/showcase/demo/inputswitch/inputswitch.html b/showcase/demo/inputswitch/inputswitch.html index 8dc5143212d..68831f1436a 100644 --- a/showcase/demo/inputswitch/inputswitch.html +++ b/showcase/demo/inputswitch/inputswitch.html @@ -199,4 +199,4 @@

Dependencies

-
\ No newline at end of file +