ngx-st-duration
v18.0.1
Published
- [Overview](#overview) - [Installation](#installation) - [Basic Usage](#basic-usage) - [Inputs](#inputs) - [Outputs](#outputs) - [Usage Examples](#usage-examples) - [Best Practices](#best-practices)
Readme
Duration Picker Component - Complete Documentation
Table of Contents
Overview
The st-duration-picker component is a time duration selector with separate inputs for days, hours, and minutes. Features include:
- Separate select inputs for days, hours, and minutes
- Converts to/from total minutes
- Customizable value ranges
- Angular Forms integration (model or FormControl binding)
- Custom labels support
- Disabled state support
Installation
npm install ngx-st-durationImport the module:
import { StDurationModule } from 'ngx-st-duration';
@NgModule({
imports: [StDurationModule]
})
export class AppModule { }Basic Usage
<st-duration-picker
label="Duration"
[initMinutesValue]="480"
(durationEmitter)="onDurationChange($event)">
</st-duration-picker>onDurationChange(minutes: number): void {
console.log('Duration in minutes:', minutes);
}Inputs
label
- Type:
string - Default:
'' - Description: Label text displayed above the duration picker.
- Example:
label="Select Duration" label="Meeting Length"
daysData
- Type:
number[] - Default:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13](0-13 days) - Description: Array of available day values in the days dropdown.
- Example:
[daysData]="[0, 1, 2, 3, 4, 5, 6, 7]" [daysData]="[...Array(30).keys()]" <!-- 0-29 days -->
hoursData
- Type:
number[] - Default:
[0, 1, 2, 3, ..., 23](0-23 hours) - Description: Array of available hour values in the hours dropdown.
- Example:
[hoursData]="[0, 1, 2, 3, 4, 5, 6, 7, 8]" <!-- 0-8 hours --> [hoursData]="[...Array(12).keys()]" <!-- 0-11 hours -->
minutesData
- Type:
number[] - Default:
[0, 15, 30, 45] - Description: Array of available minute values in the minutes dropdown.
- Example:
[minutesData]="[0, 15, 30, 45]" [minutesData]="[0, 10, 20, 30, 40, 50]" [minutesData]="[0, 5, 10, 15, 20, 25, 30]"
initMinutesValue
- Type:
number - Default:
0 - Description: Initial duration value in total minutes. The component will calculate and display the appropriate days, hours, and minutes.
- Example:
[initMinutesValue]="480" <!-- 8 hours --> [initMinutesValue]="1440" <!-- 1 day --> [initMinutesValue]="90" <!-- 1 hour 30 minutes -->
labelsSettings
- Type:
{ days: string; hours: string; minutes: string } - Default:
{ days: 'Days', hours: 'Hours', minutes: 'Minutes' } - Description: Custom labels for the three dropdown selects.
- Example:
[labelsSettings]="{ days: 'D', hours: 'H', minutes: 'M' }" [labelsSettings]="{ days: 'Días', hours: 'Horas', minutes: 'Minutos' }"
bindType
- Type:
'model' | 'control' - Default:
undefined - Description: Type of Angular Forms binding to use.
'model': Use withangularModelfor ngModel binding'control': Use withangularFormControlfor FormControl binding
- Example:
bindType="model" bindType="control"
angularFormControl
- Type:
FormControl - Default:
undefined - Description: FormControl to bind when using
bindType="control". The control value will be updated with total minutes. - Example:
durationControl = new FormControl(480);bindType="control" [angularFormControl]="durationControl"
angularModel
- Type:
model signal - Default:
undefined - Description: Model signal to bind when using
bindType="model". The model value will be updated with total minutes. - Example:
durationModel = signal(480);bindType="model" [(angularModel)]="durationModel"
disabledPickers
- Type:
boolean - Default:
false - Description: Disables all three dropdowns (days, hours, minutes).
- Example:
[disabledPickers]="isReadOnly" [disabledPickers]="true"
Outputs
durationEmitter
- Type:
number - Description: Emitted whenever the duration changes. Contains the total duration in minutes.
- Example:
(durationEmitter)="onDurationChange($event)" onDurationChange(totalMinutes: number): void { console.log('Duration:', totalMinutes, 'minutes'); const hours = totalMinutes / 60; const days = totalMinutes / 1440; }
Usage Examples
Example 1: Basic Duration Picker
// Component
@Component({
selector: 'app-basic-duration',
template: `
<st-duration-picker
label="Select Duration"
[initMinutesValue]="duration"
(durationEmitter)="updateDuration($event)">
</st-duration-picker>
<p>Total: {{ duration }} minutes ({{ formatDuration() }})</p>
`
})
export class BasicDurationComponent {
duration = 120; // 2 hours
updateDuration(minutes: number): void {
this.duration = minutes;
}
formatDuration(): string {
const hours = Math.floor(this.duration / 60);
const mins = this.duration % 60;
return `${hours}h ${mins}m`;
}
}Example 2: With FormControl Binding
// Component
@Component({
selector: 'app-form-duration',
template: `
<form [formGroup]="form">
<st-duration-picker
label="Meeting Duration"
bindType="control"
[angularFormControl]="form.get('duration')"
[initMinutesValue]="form.get('duration')?.value">
</st-duration-picker>
<button [disabled]="form.invalid" (click)="save()">
Save
</button>
</form>
<p>Selected: {{ form.get('duration')?.value }} minutes</p>
`
})
export class FormDurationComponent {
form = this.fb.group({
title: ['', Validators.required],
duration: [60, [Validators.required, Validators.min(15)]]
});
constructor(private fb: FormBuilder) {}
save(): void {
console.log('Form data:', this.form.value);
}
}Example 3: Custom Time Intervals
// Component
@Component({
selector: 'app-custom-intervals',
template: `
<st-duration-picker
label="Appointment Duration"
[daysData]="[0]"
[hoursData]="workHours"
[minutesData]="[0, 30]"
[initMinutesValue]="60"
(durationEmitter)="onDurationChange($event)">
</st-duration-picker>
`
})
export class CustomIntervalsComponent {
workHours = [0, 1, 2, 3, 4, 5, 6, 7, 8]; // 0-8 hours
onDurationChange(minutes: number): void {
console.log('Appointment duration:', minutes);
}
}Example 4: With Custom Labels
// Component
@Component({
selector: 'app-custom-labels',
template: `
<st-duration-picker
label="Duración del evento"
[labelsSettings]="spanishLabels"
[initMinutesValue]="90"
(durationEmitter)="onDurationChange($event)">
</st-duration-picker>
`
})
export class CustomLabelsComponent {
spanishLabels = {
days: 'Días',
hours: 'Horas',
minutes: 'Minutos'
};
onDurationChange(minutes: number): void {
console.log('Duración:', minutes, 'minutos');
}
}Example 5: Task Estimation
// Component
@Component({
selector: 'app-task-estimation',
template: `
<div class="task-form">
<h3>Estimate Task Duration</h3>
<st-duration-picker
label="Estimated Time"
[daysData]="estimateDays"
[hoursData]="estimateHours"
[minutesData]="[0, 15, 30, 45]"
[initMinutesValue]="taskDuration"
(durationEmitter)="updateEstimate($event)">
</st-duration-picker>
<div class="estimate-summary">
<p><strong>Estimate:</strong> {{ formatEstimate() }}</p>
<p><strong>Cost:</strong> {{ calculateCost() | currency }}</p>
</div>
<button (click)="saveEstimate()">Save Estimate</button>
</div>
`
})
export class TaskEstimationComponent {
estimateDays = [0, 1, 2, 3, 4, 5];
estimateHours = [...Array(24).keys()];
taskDuration = 240; // 4 hours
hourlyRate = 50;
updateEstimate(minutes: number): void {
this.taskDuration = minutes;
}
formatEstimate(): string {
const days = Math.floor(this.taskDuration / 1440);
let remaining = this.taskDuration % 1440;
const hours = Math.floor(remaining / 60);
const minutes = remaining % 60;
const parts = [];
if (days > 0) parts.push(`${days}d`);
if (hours > 0) parts.push(`${hours}h`);
if (minutes > 0) parts.push(`${minutes}m`);
return parts.join(' ') || '0m';
}
calculateCost(): number {
const hours = this.taskDuration / 60;
return hours * this.hourlyRate;
}
saveEstimate(): void {
console.log('Saving estimate:', this.taskDuration, 'minutes');
}
}Example 6: Meeting Scheduler
// Component
@Component({
selector: 'app-meeting-scheduler',
template: `
<form [formGroup]="meetingForm">
<mat-form-field>
<mat-label>Meeting Title</mat-label>
<input matInput formControlName="title">
</mat-form-field>
<st-duration-picker
label="Meeting Duration"
bindType="control"
[angularFormControl]="meetingForm.get('duration')"
[daysData]="[0]"
[hoursData]="[0, 1, 2, 3, 4]"
[minutesData]="meetingMinutes"
[initMinutesValue]="30">
</st-duration-picker>
<mat-form-field>
<mat-label>Location</mat-label>
<input matInput formControlName="location">
</mat-form-field>
<div class="meeting-info">
<p>Duration: {{ formatDuration(meetingForm.get('duration')?.value) }}</p>
<p>End Time: {{ calculateEndTime() }}</p>
</div>
<button mat-raised-button color="primary" (click)="scheduleMeeting()">
Schedule Meeting
</button>
</form>
`
})
export class MeetingSchedulerComponent {
meetingMinutes = [0, 15, 30, 45];
startTime = new Date();
meetingForm = this.fb.group({
title: ['', Validators.required],
duration: [30, Validators.required],
location: ['']
});
constructor(private fb: FormBuilder) {}
formatDuration(minutes: number): string {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
}
calculateEndTime(): string {
const duration = this.meetingForm.get('duration')?.value || 0;
const endTime = new Date(this.startTime.getTime() + duration * 60000);
return endTime.toLocaleTimeString();
}
scheduleMeeting(): void {
if (this.meetingForm.valid) {
console.log('Scheduling meeting:', this.meetingForm.value);
}
}
}Example 7: Disabled State
// Component
@Component({
selector: 'app-readonly-duration',
template: `
<st-duration-picker
label="Event Duration"
[initMinutesValue]="eventDuration"
[disabledPickers]="isReadOnly">
</st-duration-picker>
<button (click)="toggleReadOnly()">
{{ isReadOnly ? 'Edit' : 'Lock' }}
</button>
`
})
export class ReadonlyDurationComponent {
eventDuration = 120;
isReadOnly = true;
toggleReadOnly(): void {
this.isReadOnly = !this.isReadOnly;
}
}Example 8: Work Shift Duration
// Component
@Component({
selector: 'app-shift-duration',
template: `
<div class="shift-planner">
<h3>Plan Work Shift</h3>
<st-duration-picker
label="Shift Duration"
[daysData]="[0]"
[hoursData]="shiftHours"
[minutesData]="[0, 30]"
[labelsSettings]="{ days: '', hours: 'Hours', minutes: 'Minutes' }"
[initMinutesValue]="shiftDuration"
(durationEmitter)="calculateBreaks($event)">
</st-duration-picker>
<div class="shift-details">
<p><strong>Shift Length:</strong> {{ formatShift() }}</p>
<p><strong>Required Breaks:</strong> {{ breakMinutes }} minutes</p>
<p><strong>Actual Work Time:</strong> {{ actualWorkTime }} minutes</p>
</div>
</div>
`
})
export class ShiftDurationComponent {
shiftHours = [4, 5, 6, 7, 8, 9, 10, 11, 12];
shiftDuration = 480; // 8 hours
breakMinutes = 60;
get actualWorkTime(): number {
return this.shiftDuration - this.breakMinutes;
}
calculateBreaks(minutes: number): void {
this.shiftDuration = minutes;
// Calculate required breaks based on shift length
const hours = minutes / 60;
if (hours <= 4) {
this.breakMinutes = 0;
} else if (hours <= 6) {
this.breakMinutes = 15;
} else if (hours <= 8) {
this.breakMinutes = 30;
} else {
this.breakMinutes = 60;
}
}
formatShift(): string {
const hours = Math.floor(this.shiftDuration / 60);
const minutes = this.shiftDuration % 60;
return `${hours}h ${minutes}m`;
}
}Best Practices
Set appropriate ranges for your use case:
<!-- Short durations (appointments) --> [daysData]="[0]" [hoursData]="[0, 1, 2, 3, 4]" [minutesData]="[0, 15, 30, 45]" <!-- Long durations (projects) --> [daysData]="[...Array(30).keys()]" [hoursData]="[...Array(24).keys()]" [minutesData]="[0]"Choose appropriate minute intervals:
[minutesData]="[0, 15, 30, 45]" <!-- Standard --> [minutesData]="[0, 30]" <!-- Half-hour --> [minutesData]="[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]" <!-- 5-min -->Use FormControl binding for forms with validation:
bindType="control" [angularFormControl]="myFormControl"Provide clear labels that match context:
label="Meeting Duration" label="Estimated Time" label="Shift Length"Initialize with sensible defaults:
[initMinutesValue]="30" <!-- 30-min meeting --> [initMinutesValue]="480" <!-- 8-hour shift -->Handle the output appropriately:
onDurationChange(minutes: number): void { this.validateDuration(minutes); this.calculateCost(minutes); this.updateEndTime(minutes); }
Duration Calculation
The component converts between total minutes and days/hours/minutes:
// Minutes to days/hours/minutes
const totalMinutes = 1530; // Input
const days = Math.floor(totalMinutes / 1440); // 1 day
const hours = Math.floor((totalMinutes % 1440) / 60); // 1 hour
const minutes = totalMinutes % 60; // 30 minutes
// Days/hours/minutes to total minutes
const days = 1;
const hours = 2;
const minutes = 30;
const total = days * 1440 + hours * 60 + minutes; // 1590 minutesCommon Use Cases
- Meeting Scheduling: Select meeting duration
- Task Estimation: Estimate time required for tasks
- Shift Planning: Define work shift lengths
- Event Duration: Set event or activity durations
- SLA Definitions: Define service level agreement times
- Rental Periods: Select rental durations
This documentation covers all inputs, outputs, and usage patterns for the Duration Picker component.
