npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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-duration

Import 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 with angularModel for ngModel binding
    • 'control': Use with angularFormControl for 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

  1. 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]"
  2. 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 -->
  3. Use FormControl binding for forms with validation:

    bindType="control"
    [angularFormControl]="myFormControl"
  4. Provide clear labels that match context:

    label="Meeting Duration"
    label="Estimated Time"
    label="Shift Length"
  5. Initialize with sensible defaults:

    [initMinutesValue]="30"   <!-- 30-min meeting -->
    [initMinutesValue]="480"  <!-- 8-hour shift -->
  6. 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 minutes

Common Use Cases

  1. Meeting Scheduling: Select meeting duration
  2. Task Estimation: Estimate time required for tasks
  3. Shift Planning: Define work shift lengths
  4. Event Duration: Set event or activity durations
  5. SLA Definitions: Define service level agreement times
  6. Rental Periods: Select rental durations

This documentation covers all inputs, outputs, and usage patterns for the Duration Picker component.