UNPKG

@nebular/theme

Version:
373 lines 13.5 kB
import { Attribute, ChangeDetectorRef, Directive, ElementRef, forwardRef, Inject, Input, isDevMode, Renderer2, } from '@angular/core'; import { filter, map, takeUntil } from 'rxjs/operators'; import { fromEvent, merge, Subject } from 'rxjs'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { NbTimePickerComponent } from './timepicker.component'; import { NbAdjustment, NbPosition, NbPositionBuilderService, } from '../cdk/overlay/overlay-position'; import { NbOverlayService } from '../cdk/overlay/overlay-service'; import { NbTrigger, NbTriggerStrategyBuilderService } from '../cdk/overlay/overlay-trigger'; import { NbDateService } from '../calendar-kit/services/date.service'; import { NbCalendarTimeModelService } from '../calendar-kit/services/calendar-time-model.service'; import { NB_DOCUMENT } from '../../theme.options'; /** * The `NbTimePickerDirective` is form control that gives you ability to select a time. The timepicker * is shown when input receives a `focus` event. * ```html * <input [nbTimepicker]="timepicker"> * <nb-timepicker #timepicker></nb-timepicker> * ``` * * @stacked-example(Showcase, timepicker/timepicker-showcase.component) * * ### Installation * * Import `NbTimepickerModule.forRoot()` to your root module. * ```ts * @NgModule({ * imports: [ * // ... * NbTimepickerModule.forRoot(), * ], * }) * export class AppModule { } * ``` * And `NbTimepickerModule` to your feature module. * ```ts * @NgModule({ * imports: [ * // ... * NbTimepickerModule, * ], * }) * export class PageModule { } * * ``` * <div id="native-parse-issue" class="note note-warning"> * <div class="note-title">Note</div> * <div class="note-body"> * Timepicker uses native Date object by default, which doesn't support parsing by custom format. * According to the ECMAScript specification, the only supported format is a format described by ISO 8061 standard. * This standard requires date part to be included in the date string, * meaning you have to type a date+time in the input. * We highly recommend you to use NbDateFnsDateModule or NbMomentDateModule to be able to support time only strings in * the timepicker inputs. These modules use date-fns and moment date libraries, which provide capabilities * to parse time only strings. * See "Formatting Issue" at * <a href="https://akveo.github.io/nebular/docs/components/datepicker/overview#formatting-issue">Date picker docs</a> * for installation instructions. * </div> * </div> * <hr> * * ### Usage * * To show seconds column along with hours and minutes use `withSeconds` input * * ```html * <input [nbTimepicker]="timepicker"> * <nb-timepicker #timepicker withSeconds></nb-timepicker> * ``` * @stacked-example(Time picker with seconds, timepicker/timepicker-with-seconds.component) * * To force timepicker work in 12 hours format, use `twelveHoursFormat` input. * By default, timepicker choose 12 or 24 formats based on application locale standards * * ```html * <input [nbTimepicker]="timepicker" twelveHoursFormat> * <nb-timepicker #timepicker></nb-timepicker> * ``` * * @stacked-example(Twelve hours format showcase, timepicker/timepicker-twelve-hours-format.component) * * A single column picker with options value as time and minute, so users won’t be able to pick * hours and minutes individually. * You can control options minutes offset via `step` input, e.g.: 11:00, 11:20, 11:40...' * * @stacked-example(Single column, timepicker/timepicker-single-column.component) * * Timepicker support forms and reactive forms API so you can provide value using `formControl` and `ngModel` directives * @stacked-example(Form control, timepicker/timepicker-form-control.component) * * <input [nbTimepicker]="timepicker" twelveHoursFormat> * <nb-timepicker #timepicke [formControl]="formControl"></nb-timepicker> * * @stacked-example(NgModel, timepicker/timepicker-ng-model.component) * * <input [nbTimepicker]="timepicker" twelveHoursFormat> * <nb-timepicker #timepicke [ngModel]="date"></nb-timepicker> * * @styles * * timepicker-cell-text-color: * timepicker-cell-hover-background-color: * timepicker-cell-hover-text-color: * timepicker-cell-focus-background-color: * timepicker-cell-focus-text-color: * timepicker-cell-active-background-color: * timepicker-cell-active-text-color: * timepicker-cell-text-font-size: * timepicker-cell-text-font-family: * timepicker-cell-text-line-height: * timepicker-cell-text-font-weight: * timepicker-cell-height: * timepicker-header-cell-text-color: * timepicker-header-cell-text-font-size: * timepicker-header-cell-text-font-family: * timepicker-header-cell-height: * timepicker-header-cell-text-line-height: * timepicker-header-cell-text-font-weight: * timepicker-border-color: * timepicker-border-style: * timepicker-border-width: * timepicker-scrollbar-color: * timepicker-scrollbar-background-color: * timepicker-scrollbar-width: * timepicker-single-column-width: * timepicker-multiple-column-width: * timepicker-title-height: * timepicker-title-padding: * timepicker-container-width: * timepicker-container-height: * */ export class NbTimePickerDirective { constructor(document, positionBuilder, hostRef, triggerStrategyBuilder, overlay, cd, calendarTimeModelService, dateService, renderer, placeholder) { this.document = document; this.positionBuilder = positionBuilder; this.hostRef = hostRef; this.triggerStrategyBuilder = triggerStrategyBuilder; this.overlay = overlay; this.cd = cd; this.calendarTimeModelService = calendarTimeModelService; this.dateService = dateService; this.renderer = renderer; this.placeholder = placeholder; /** * Time picker overlay offset. * */ this.overlayOffset = 8; this.destroy$ = new Subject(); this.onChange = () => { }; this.onTouched = () => { }; } /** * Provides timepicker component. * */ get timepicker() { return this._timePickerComponent; } set timepicker(timePicker) { this._timePickerComponent = timePicker; } /** * Returns html input element. * @docs-private * */ get input() { return this.hostRef.nativeElement; } /** * Determines is timepicker overlay opened. * @docs-private * */ get isOpen() { return this.overlayRef && this.overlayRef.hasAttached(); } /** * Determines is timepicker overlay closed. * @docs-private * */ get isClosed() { return !this.isOpen; } /** * Returns host input value. * @docs-private * */ get inputValue() { return this.input.value; } set inputValue(value) { this.input.value = value; } ngAfterViewInit() { this.subscribeOnInputChange(); if (!this.placeholder) { this.renderer.setProperty(this.input, 'placeholder', this.timepicker.timeFormat); } this.triggerStrategy = this.createTriggerStrategy(); this.subscribeOnTriggers(); this.subscribeToBlur(); } show() { if (this.isClosed) { this.attachToOverlay(); } } hide() { if (this.isOpen) { this.overlayRef.detach(); this.cd.markForCheck(); } } /** * Attaches picker to the timepicker portal. * @docs-private * */ attachToOverlay() { if (!this.overlayRef) { this.setupTimepicker(); this.initOverlay(); } this.overlayRef.attach(this.timepicker.portal); } setupTimepicker() { if (this.dateService.getId() === 'native' && isDevMode()) { console.warn('Date.parse noes not support parsing time with custom format.' + ' See details here https://akveo.github.io/nebular/docs/components/datepicker/overview#native-parse-issue'); } this.timepicker.setHost(this.hostRef); if (this.inputValue) { const val = this.dateService.getId() === 'native' ? this.parseNativeDateString(this.inputValue) : this.inputValue; this.timepicker.date = this.dateService.parse(val, this.timepicker.timeFormat); } else { this.timepicker.date = this.calendarTimeModelService.getResetTime(); } } initOverlay() { this.positionStrategy = this.createPositionStrategy(); this.subscribeOnApplyClick(); this.createOverlay(); } subscribeOnApplyClick() { this.timepicker.onSelectTime.pipe(takeUntil(this.destroy$)).subscribe((value) => { const time = this.dateService.format(value.time, this.timepicker.timeFormat).toUpperCase(); this.inputValue = time; this.timepicker.date = value.time; this.onChange(value.time); if (value.save) { this.lastInputValue = time; this.hide(); } }); } createOverlay() { const scrollStrategy = this.createScrollStrategy(); this.overlayRef = this.overlay.create({ positionStrategy: this.positionStrategy, scrollStrategy }); } subscribeOnTriggers() { this.triggerStrategy.show$ .pipe(filter(() => this.isClosed)) .subscribe(() => this.show()); this.triggerStrategy.hide$ .pipe(filter(() => this.isOpen)) .subscribe(() => { this.inputValue = this.lastInputValue || ''; this.hide(); }); } createTriggerStrategy() { return this.triggerStrategyBuilder .trigger(NbTrigger.FOCUS) .host(this.hostRef.nativeElement) .container(() => this.getContainer()) .build(); } createPositionStrategy() { return this.positionBuilder .connectedTo(this.hostRef) .position(NbPosition.BOTTOM) .offset(this.overlayOffset) .adjustment(NbAdjustment.VERTICAL); } getContainer() { return this.overlayRef && this.isOpen && { location: { nativeElement: this.overlayRef.overlayElement, }, }; } createScrollStrategy() { return this.overlay.scrollStrategies.block(); } subscribeOnInputChange() { fromEvent(this.input, 'input') .pipe(map(() => this.inputValue), takeUntil(this.destroy$)) .subscribe((value) => this.handleInputChange(value)); } subscribeToBlur() { merge(this.timepicker.blur, fromEvent(this.input, 'blur').pipe(filter(() => !this.isOpen && this.document.activeElement !== this.input))).pipe(takeUntil(this.destroy$)) .subscribe(() => this.onTouched()); } /** * Parses input value and write if it isn't null. * @docs-private * */ handleInputChange(value) { if (this.dateService.getId() === 'native') { /** * Native date service dont parse only time string value, * and we adding year mouth and day to convert string to valid date format **/ value = this.parseNativeDateString(value); } const isValidDate = this.dateService.isValidDateString(value, this.timepicker.timeFormat); if (isValidDate) { this.lastInputValue = value; const date = this.dateService.parse(value, this.timepicker.timeFormat); this.onChange(date); this.timepicker.date = date; } } updateValue(value) { if (value) { this.timepicker.date = value; this.inputValue = this.dateService.format(value, this.timepicker.timeFormat).toUpperCase(); } } writeValue(value) { this.updateValue(value); } registerOnChange(fn) { this.onChange = fn; } registerOnTouched(fn) { this.onTouched = fn; } parseNativeDateString(value) { const date = this.dateService.today(); const year = this.dateService.getYear(date); const month = this.calendarTimeModelService.paddToTwoSymbols(this.dateService.getMonth(date)); const day = this.calendarTimeModelService.paddToTwoSymbols(this.dateService.getDate(date)); return `${year}-${month}-${day} ${value}`; } } NbTimePickerDirective.decorators = [ { type: Directive, args: [{ selector: 'input[nbTimepicker]', providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NbTimePickerDirective), multi: true, }], },] } ]; NbTimePickerDirective.ctorParameters = () => [ { type: undefined, decorators: [{ type: Inject, args: [NB_DOCUMENT,] }] }, { type: NbPositionBuilderService }, { type: ElementRef }, { type: NbTriggerStrategyBuilderService }, { type: NbOverlayService }, { type: ChangeDetectorRef }, { type: NbCalendarTimeModelService }, { type: NbDateService }, { type: Renderer2 }, { type: String, decorators: [{ type: Attribute, args: ['placeholder',] }] } ]; NbTimePickerDirective.propDecorators = { timepicker: [{ type: Input, args: ['nbTimepicker',] }], overlayOffset: [{ type: Input }] }; //# sourceMappingURL=timepicker.directive.js.map