valor-software / ngx-bootstrap

Fast and reliable Bootstrap widgets in Angular (supports Ivy engine)
https://valor-software.com/ngx-bootstrap
MIT License
5.53k stars 1.69k forks source link

feat(timepicker)(datepicker): add keys support to DateTimePicker #1455

Closed alami2010 closed 4 years ago

alami2010 commented 7 years ago

Hello, I share with you my datetimepicker that wrap the datetimepicker ng2-boutsrap with support of keys i hpe will helpful for someone

Code

``` @Component({ selector: 'date-time-picker', template: `

{{item.message}}
`, providers: [VALUE_ACCESSOR, DatepickerConfig ,TimepickerConfig ,{ provide: ViewValidationElement, useExisting: forwardRef(() => DateTimePickerComponent) }] }) /** * This widgets display a date time picker with date, month, year and hour select. This widget has keyboard support. * */ export class DateTimePickerComponent extends ViewValidationElement implements ControlValueAccessor, AfterViewChecked, AfterViewInit { /* * Unique identifier for control, also used as the control instance's name. */ inputId: string; /* * Boolean to show meridian or not */ ismeridian: boolean = true; /* * Bound objet value input with timer */ timePicker: Date = new Date(); /* * Is Date time picker with time to show to time */ isCalendarWithTime: boolean = false; /* * Default=3. Size of the div containing the controls (label + input + validation messages). */ @Input() col: number; /* * If true, input loose focus when modified value = maxSize */ @Input('auto-tab') isAutoTab: boolean; /* * Default=false, bindable value. If the property becomes true, focus is set to the field. See FocusDirective. */ @Input('focused') focused: boolean; /* * Default=true, bindable value. Define is the widget is visible. */ @Input() visible: boolean; /* * Default=true, bindable value */ @Input() enabled: boolean; /* * Date format: DD days, MM month, YYYY years, HH hours, mm minutes */ @Input() dateFormat: any; /* * max length in input */ maxlength: number; /* * Help message on mouse over */ @Input() helpMsg: string; /* * Bound objet value input */ @Input() value: Date; /* * Event Emitter ( on Change ) */ @Output() onChange = new EventEmitter(); /* * Is date valid with format ? */ isDateValid: boolean = true; /* * Bound objet value with date panel */ datePicker: any; /* * Bound objet value with input */ dateTimeModel: any; /* * Panel ELement HTML */ panel: any; /* * Which page is show on panel ( days , months , years ) */ datePickerPageShown: any; /* * is Panel DateTimePicker visible */ panelVisible: boolean; /* * Document Click Listener */ private documentClickListener: any; /* * Width of date time picker */ width: string; /* * Bound objet value */ selectionDate: Date; /* * Input Element HTML */ input: HTMLInputElement; /** * Aria related inputs. */ @Input('aria-label') ariaLabel: string; @Input('aria-labelledby') ariaLabelledBy: string; maskinput:string; maskinputvalue:string; eventHandlerInputKeyPress(event) { // this.log.debug(event, event.keyCode, event.keyIdentifier); } constructor(private domHandler: DomHandler, private el: ElementRef, private renderer: Renderer) { super(); this.panelVisible = false; this.id = this.el.nativeElement.id; this.inputId = this.id + CONTROL_TYPE_SUFFIX.INPUT; this.datePicker = new Date; } @HostListener('click', ['$event']) onClick(e: Event) { if (this.panelVisible) { e.preventDefault(); e.stopPropagation(); if ($(this.panel).has(e.srcElement)) { $(this.el.nativeElement).find('.date-time-btn').get(0).focus(); } } } ngAfterViewChecked() { if (this.visible) if (this.domHandler.findSingle(this.el.nativeElement, 'daypicker').childElementCount > 0) { this.datePickerPageShown = 'DP_DAYS'; } else if (this.domHandler.findSingle(this.el.nativeElement, 'monthpicker').childElementCount > 0) { this.datePickerPageShown = 'DP_MONTH'; } else if (this.domHandler.findSingle(this.el.nativeElement, 'yearpicker').childElementCount > 0) { this.datePickerPageShown = 'DP_YEARS'; } else { } } ngOnInit() { console.info(' ==>on init focused ', this.focused); if (!this.helpMsg) this.helpMsg = ''; if (this.value) { this.datePicker = this.value; this.timePicker = this.value; this.changeModel(this.value); } this.input = this.domHandler.findSingle(this.el.nativeElement, 'input'); this.documentClickListener = this.renderer.listenGlobal('body', 'click', (e) => { let sourceEl = e.srcElement; if (!$(this.domHandler.getClosest(sourceEl, '.calendar-popup')).is($(this.panel))) { this.hide(); } return true; }); } ngAfterViewInit() { this.panel = this.domHandler.findSingle(this.el.nativeElement, 'div.calendar-popup'); // If date format is not set, the following default format will be applied: if (this.dateFormat) { // check if date format contains time this.isCalendarWithTime = this.dateFormat.toUpperCase().includes('H'); } else { // the default date this.dateFormat = 'DD/MM/YYYY'; } this.maskinput=this.dateFormat.replace(/[a-zA-Z]/g,'9'); this.maskinputvalue = this.dateFormat.replace(/[a-zA-Z]/g,'_');; this.maxlength = this.dateFormat.length; /* if (this.datePicker) { this.selectionDate = moment(this.input.value, this.dateFormat).toDate(); this.input.value = this.converFromDate(this.datePicker); // Change here for date type }*/ if (this.col > 0) { this.classList.push(' col-md-' + this.col); } } converFromDate(value: any): string { if (value instanceof Date) { return moment(value).format(this.dateFormat); } else { return value; } } convertToDate(value: any): Date { if (value instanceof Date) { return value; } return moment(value, this.dateFormat).toDate(); } align() { this.domHandler.relativePosition(this.panel, this.el.nativeElement.children[0]); } showCalendar() { if (!this.panelVisible) { this.panelVisible = true; this.panel.style.zIndex = 1000; // ++PUI.zindex; this.align(); this.domHandler.fadeIn(this.panel, 300); } $(this.el.nativeElement).find('.date-time-btn').get(0).focus(); } processKeyPressForTimePicker(e: any, isFirst: boolean) { // enter and bigDown if (e.keyCode === KeyBoardKeysData.ENTER) { this.changeModel(this.datePicker); return false; } let indexInput: number = isFirst ? 0 : 1; // key down if (e.keyCode === KeyBoardKeysData.DOWN) { $(this.domHandler.findSingle(this.el.nativeElement, 'timepicker')).find('tbody a').get(2 + indexInput).click(); } // Key up if (e.keyCode === KeyBoardKeysData.UP) { $(this.domHandler.findSingle(this.el.nativeElement, 'timepicker')).find('tbody a').get(indexInput).click(); } return true; } processKeyPressForDays(e: any) { // enter if (e.keyCode === KeyBoardKeysData.ENTER) { this.changeModel(this.datePicker); $(this.domHandler.findSingle(this.el.nativeElement, 'daypicker')).find('button.active').click(); return false; } // up to months key shift if (e.keyCode === KeyBoardKeysData.UP && e.ctrlKey) { $(this.domHandler.findSingle(this.el.nativeElement, 'daypicker')).find('thead button').get(1).click(); } // key first day if (e.keyCode === KeyBoardKeysData.HOME) { this.datePicker = new Date(this.datePicker.getFullYear(), this.datePicker.getMonth(), 1); } // key last day if (e.keyCode === KeyBoardKeysData.END) { this.datePicker = new Date(this.datePicker.getFullYear(), this.datePicker.getMonth(), new Date(this.datePicker.getFullYear(), this.datePicker.getMonth() + 1, 0).getDate()); } // key big up if (e.keyCode === KeyBoardKeysData.PAGE_UP) { this.datePicker = moment(this.datePicker).add(+1, 'month').toDate(); } // key big down if (e.keyCode === KeyBoardKeysData.PAGE_DOWN) { this.datePicker = moment(this.datePicker).add(-1, 'month').toDate(); } // key down if (e.keyCode === KeyBoardKeysData.DOWN) { this.datePicker = moment(this.datePicker).add(+7, 'days').toDate(); } // Key up if (e.keyCode === KeyBoardKeysData.UP) { this.datePicker = moment(this.datePicker).add(-7, 'days').toDate(); } //Key right if (e.keyCode === KeyBoardKeysData.RIGHT) { this.datePicker = moment(this.datePicker).add(+1, 'days').toDate(); } //page left if (e.keyCode === KeyBoardKeysData.LEFT) { this.datePicker = moment(this.datePicker).add(-1, 'days').toDate(); } // key down if (!this.panelVisible && (e.keyCode === 40 || e.keyCode === 38)) { this.showCalendar(); } return false; } processKeyPressForMonths(e: any) { // enter if (e.keyCode === KeyBoardKeysData.ENTER || (e.keyCode === KeyBoardKeysData.DOWN && e.ctrlKey)) { $(this.domHandler.findSingle(this.el.nativeElement, 'monthpicker')).find('tbody button.active').click(); } // up to year key shift if (e.keyCode === KeyBoardKeysData.UP && e.ctrlKey) { $(this.domHandler.findSingle(this.el.nativeElement, 'monthpicker')).find('thead button').get(1).click(); } // key first month if (e.keyCode === KeyBoardKeysData.HOME) { this.datePicker = new Date(this.datePicker.getFullYear(), 0, this.datePicker.getDate()); } // key last month if (e.keyCode === KeyBoardKeysData.END) { this.datePicker = new Date(this.datePicker.getFullYear(), 11, this.datePicker.getDate()); } // key big up if (e.keyCode === KeyBoardKeysData.PAGE_UP) { this.datePicker = moment(this.datePicker).add(+1, 'year',).toDate(); } // key big down if (e.keyCode === KeyBoardKeysData.PAGE_DOWN) { this.datePicker = moment(this.datePicker).add(-1, 'year',).toDate(); } // key down if (e.keyCode === KeyBoardKeysData.DOWN) { this.datePicker = moment(this.datePicker).add(+3, 'month').toDate(); } // Key up if (e.keyCode === KeyBoardKeysData.UP) { this.datePicker = moment(this.datePicker).add(-3, 'month').toDate(); } //Key right if (e.keyCode === KeyBoardKeysData.RIGHT) { this.datePicker = moment(this.datePicker).add(+1, 'month').toDate(); } //page left if (e.keyCode === KeyBoardKeysData.LEFT) { this.datePicker = moment(this.datePicker).add(-1, 'month').toDate(); } } processKeyPressForYears(e: any) { // enter if (e.keyCode === KeyBoardKeysData.ENTER || (e.keyCode === KeyBoardKeysData.DOWN && e.ctrlKey)) { $(this.domHandler.findSingle(this.el.nativeElement, 'yearpicker')).find('tbody button.active').click(); } // key big up if (e.keyCode === KeyBoardKeysData.PAGE_UP) { this.datePicker = moment(this.datePicker).add(+20, 'year').toDate(); } // key big down if (e.keyCode === KeyBoardKeysData.PAGE_DOWN) { this.datePicker = moment(this.datePicker).add(-20, 'year').toDate(); } // key down if (e.keyCode === KeyBoardKeysData.DOWN) { this.datePicker = moment(this.datePicker).add(+5, 'year').toDate(); } // Key up if (e.keyCode === KeyBoardKeysData.UP) { this.datePicker = moment(this.datePicker).add(-5, 'year').toDate(); } //Key right if (e.keyCode === KeyBoardKeysData.RIGHT) { this.datePicker = moment(this.datePicker).add(+1, 'year').toDate(); } //page left if (e.keyCode === KeyBoardKeysData.LEFT) { this.datePicker = moment(this.datePicker).add(-1, 'year').toDate(); } return false; } hide() { this.panelVisible = false; } ngOnDestroy() { if (this.documentClickListener) { this.documentClickListener(); } } changeValue() { console.info(' ==>on init changeValue ', this.dateTimeModel); this.isDateValid = this.isEmptyOrContainsMaskValue(this.dateTimeModel) || moment(this.dateTimeModel, this.dateFormat, true).isValid() ; if (this.isDateValid && !this.isEmptyOrContainsMaskValue(this.dateTimeModel)) { this.selectionDate = moment(this.dateTimeModel, this.dateFormat).toDate(); this.datePicker = this.selectionDate ; this.timePicker = this.selectionDate; this.onChange.emit(this); this.onModelChange(this.selectionDate); this.onError.emit(this.control); } else { this.datePicker = new Date(); this.timePicker = new Date(); this.onChange.emit(this); this.onModelChange(null); this.onError.emit(this.control); } } isEmptyOrContainsMaskValue(value:string){ return !value || value === this.maskinputvalue; } changeModel(newDate) { if (newDate instanceof Date) { var newDateTime = new Date(newDate.getFullYear(), newDate.getMonth(), newDate.getDate(), this.timePicker.getHours(), this.timePicker.getMinutes(), this.timePicker.getSeconds()); this.isDateValid = true; this.onChange.emit(this); this.onModelChange(newDateTime); this.onError.emit(this.control); this.dateTimeModel = moment(newDateTime).format(this.dateFormat); this.hide() } } @HostListener('keydown', ['$event']) onKeyDown(e: KeyboardEvent) { // support chrom and firefox let element: any = e.target || e.srcElement; //echap if (e.keyCode === KeyBoardKeysData.ESCAPE) { this.hide(); } // key down up if (!this.panelVisible && (e.keyCode === KeyBoardKeysData.UP || e.keyCode === KeyBoardKeysData.DOWN)) { this.showCalendar(); return true; } // check if is the focus on timepicker input if (element.tagName.toLowerCase() == 'input' && element.getAttribute('type') == 'text' && (' ' + element.className + ' ').indexOf('input-date-time') < 0) { let isFirstInput = $(element).parent().is(':first-child'); return this.processKeyPressForTimePicker(e, isFirstInput); } else if (element.tagName.toLowerCase() == 'input' && element.getAttribute('type') == 'text' && (' ' + element.className + ' ').indexOf('input-date-time') > 0) { return true; } console.info('this.datePickerPageShown',this.datePickerPageShown); switch (this.datePickerPageShown) { case 'DP_DAYS': this.processKeyPressForDays(e); break; case 'DP_MONTH': this.processKeyPressForMonths(e); break; case 'DP_YEARS': this.processKeyPressForYears(e); break; } console.info('this.datePickerPageShown',this.datePickerPageShown); if (e.keyCode == KeyBoardKeysData.TAB) return true; return false; } } @NgModule({ exports: DateTimePickerComponent], declarations: [DateTimePickerComponent], imports: [CommonModule, FormsModule, DatepickerModule, TimepickerModule, AutoTabDirectiveModule, FocusDirectiveModule,MaskSelectorModule] }) export class DateTimePickerModule { } ``` ``` export enum KeyBoardKeysData { ENTER = 13 , UP = 38, DOWN = 40, LEFT = 37, RIGHT = 39, HOME = 36, END = 35, PAGE_UP = 33, PAGE_DOWN = 34, ESCAPE = 27, TAB = 9, BACKSPACE = 8 } ``` ``` import { Injectable } from '@angular/core'; @Injectable() export class DomHandler { constructor() { } 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(' '); for (let i = 0; i < styles.length; i++) { element.className += ' ' + styles[ i ]; } } } 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); } /** * Used to find an element index, in list composed as follow: * ul * li * a < being the element passed. * @param element * @returns {number} */ public index(element: any): number { let children: NodeList = element.parentNode.parentNode.childNodes; let num = 0; for (let i = 0; i < children.length; i++) { if (children[ i ] === element.parentNode) return num; if (children[ i ].nodeType === 1) num++; // 1 = HTML node, avoid comments and text. } return -1; } public relPosition(element: any, target: any): void { element.style.top = target.offsetTop + target.offsetHeight + 'px'; element.style.left = target.offsetLeft + 'px'; } public relativePosition(element: any, target: HTMLElement): 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 - targetOffset.height) + 'px'; element.style.left = (targetOffset.width + 16) + 'px'; } public absolutePosition(element: any, target: any): void { let elementOuterHeight = element.offsetParent ? element.offsetHeight : this.getHiddenElementOuterHeight(element); let targetOuterHeight = target.offsetHeight; let targetOffset = target.getBoundingClientRect(); let windowScrollTop = this.getWindowScrollTop(); let top; if (targetOffset.top + targetOuterHeight + elementOuterHeight > window.innerHeight) { 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 getHiddenElementOuterHeightAndWidth(element: any): any { element.style.visibility = 'hidden'; element.style.display = 'block'; let elementHeight = element.offsetHeight; let elementWidth = element.offsetWidth; element.style.display = 'none'; element.style.visibility = 'visible'; return {height : elementHeight , width :elementWidth }; } 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'); let paddingTop: number = paddingTopValue ? parseFloat(paddingTopValue) : 0; let containerRect = container.getBoundingClientRect(); let itemRect = item.getBoundingClientRect(); let offset = (itemRect.top + document.body.scrollTop) - (containerRect.top + document.body.scrollTop) - borderTop - paddingTop; let scroll = container.scrollTop; let elementHeight = container.clientHeight; let itemHeight = this.getOuterHeight(item); if (offset < 0) { container.scrollTop = scroll + offset; } else if ((offset + itemHeight) > elementHeight) { container.scrollTop = scroll + offset - elementHeight + itemHeight; } } public getOuterHeight(element): number { let height: number = element.offsetHeight; let style: any = getComputedStyle(element); height += parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10); return height; } public fadeIn(element, duration: number): void { element.style.opacity = 0; let last = +new Date(); let tick = function () { element.style.opacity = +element.style.opacity + (new Date().getTime() - last) / duration; last = +new Date(); if (+element.style.opacity < 1) { let a = (window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16); } }; tick(); } public relativePositionDTP(element: any, target: HTMLElement): void { let elementOuter = element.offsetParent ? element.offsetHeight : this.getHiddenElementOuterHeightAndWidth(element); let targetOffset = target.getBoundingClientRect(); let is_X_Ok = targetOffset.left + targetOffset.width + elementOuter.width < window.innerWidth; let is_Y_Ok = targetOffset.top + targetOffset.height + elementOuter.height -30 < window.innerHeight; if (is_X_Ok && is_Y_Ok ) { element.style.left = (targetOffset.width + 16) + 'px'; element.style.top = '0px'; } else if (!is_X_Ok && is_Y_Ok) { element.style.left = '15px'; element.style.top = '26px'; } else if (is_X_Ok && !is_Y_Ok) { element.style.left = '15px'; element.style.top = (-elementOuter.height)+'px'; } else if (!is_X_Ok && !is_Y_Ok) { element.style.left = '15px'; element.style.top = (-elementOuter.height)+'px'; } } public getWindowScrollTop(): number { let doc = document.documentElement; return (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); } public matches(element, selector: string): boolean { let p = Element.prototype; let f = p[ 'matches' ] || p.webkitMatchesSelector || p[ 'mozMatchesSelector' ] || p.msMatchesSelector || function (s) { return [].indexOf.call(document.querySelectorAll(s), this) !== -1; }; return f.call(element, selector); } /** * Get the further matching element up the DOM tree. * @param {Element} elem Starting element * @param {String} selector Selector to match against (class, ID, data attribute, or tag) * @return {Element} Returns null if not match found */ public getFurthest(elem: any, selector: string) { // Variables let elements: any[] = []; let firstChar = selector.charAt(0); let supports = 'classList' in document.documentElement; let attribute, value; // If selector is a data attribute, split attribute from value if (firstChar === '[') { selector = selector.substr(1, selector.length - 2); attribute = selector.split('='); if (attribute.length > 1) { value = true; attribute[ 1 ] = attribute[ 1 ].replace(/"/g, '').replace(/'/g, ''); } } // Get closest match for (; elem && elem !== document && elem.nodeType === 1; elem = elem.parentNode) { // If selector is a class if (firstChar === '.') { if (supports) { if (elem.classList.contains(selector.substr(1))) { elements.push(elem); } } else { if (new RegExp('(^|\\s)' + selector.substr(1) + '(\\s|$)') .test(elem.className)) { elements.push(elem); } } } // If selector is an ID if (firstChar === '#') { if (elem.id === selector.substr(1)) { elements.push(elem); } } // If selector is a data attribute if (firstChar === '[') { if (elem.hasAttribute(attribute[ 0 ])) { if (value) { if (elem.getAttribute(attribute[ 0 ]) === attribute[ 1 ]) { elements.push(elem); } } else { elements.push(elem); } } } // If selector is a tag if (elem.tagName.toLowerCase() === selector) { elements.push(elem); } } return elements[ elements.length - 1 ]; } /** * Get the closest matching element up the DOM tree. * @param {Element} elem Starting element * @param {String} selector Selector to match against (class, ID, data attribute, or tag) * @return {Element} Returns null if not match found */ public getClosest(elem: any, selector: string) { // Variables let firstChar = selector.charAt(0); let supports = 'classList' in document.documentElement; let attribute: string[]; let value: boolean; // If selector is a data attribute, split attribute from value if (firstChar === '[') { selector = selector.substr(1, selector.length - 2); attribute = selector.split('='); if (attribute.length > 1) { value = true; attribute[ 1 ] = attribute[ 1 ].replace(/"/g, '').replace(/'/g, ''); } } // Get closest match for (; elem && elem !== document && elem.nodeType === 1; elem = elem.parentNode) { // If selector is a class if (firstChar === '.') { if (supports) { if (elem.classList.contains(selector.substr(1))) { return elem; } } else { if (new RegExp('(^|\\s)' + selector.substr(1) + '(\\s|$)') .test(elem.className)) { return elem; } } } // If selector is an ID if (firstChar === '#') { if (elem.id === selector.substr(1)) { return elem; } } // If selector is a data attribute if (firstChar === '[') { if (elem.hasAttribute(attribute[ 0 ])) { if (value) { if (elem.getAttribute(attribute[ 0 ]) === attribute[ 1 ]) { return elem; } } else { return elem; } } } // If selector is a tag if (elem.tagName.toLowerCase() === selector) { return elem; } } return null; } } ```

karolisgrinkevicius commented 7 years ago

Anyone used this? Does it work properly with latest ng2-bootstrap? Btw @alami2010 thanks for your snippet! Is there any chance to see how it works in jsfiddle or some other js sandbox?

alami2010 commented 7 years ago

@karolisgrinkevicius you can test it samply by adding ng2-bootstrap and adding this Class DateTimePickerComponent , it works fine for me in my project, I am waiting for your feedback

BenevidesLecontes commented 7 years ago

@alami2010 I'm trying to use your code, but it has variables and stuffs that they are not declared. What's DomHandler, VALUE_ACCESSOR, ViewValidationElement, ViewValidationElement, CONTROL_TYPE_SUFFIX, KeyBoardKeysData and so on?

karolisgrinkevicius commented 7 years ago

This requires jQuery at least to work. I assume that many of us don't have it in the tree. Since there are no pure javascript angular2 solution we can't go forward.

alami2010 commented 7 years ago

@BenevidesLecontes , i added DomHander and KeyBoardKeysData in the top , @karolisgrinkevicius yes you have use Jquery if you find any unsed variable you can delete it

valorkin commented 7 years ago

this is what I am working on https://projects.invisionapp.com/share/CE8JRAGRY#/screens/187235464_Behance

BenevidesLecontes commented 7 years ago

@valorkin amazing work. Waiting for this feature.

valorkin commented 7 years ago

new datepicker available from v1.9.0 http://valor-software.com/ngx-bootstrap/#/datepicker#examples timepicker will be added soon