@nebular/theme
Version:
@nebular/theme
373 lines • 13.5 kB
JavaScript
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