bm-datepicker
TypeScript icon, indicating that this package has built-in type declarations

1.0.10 • Public • Published

Bookmaker Datepicker for Angular

A sweet, stylish and fast datepicker for Angular. Developed for developers who want full control over the stylesheet and patterns. Can be used within a formgroup or as a standalone input with a standalone callback output.

Notice

This application requires Angular version 15.1.0 or newer to work correctly.

Bookmaker Datepicker

Installation

npm i bm-datepicker

Import module in app.module.ts:

import { BmDatepickerModule } from 'bm-datepicker';
  imports: [
    BmDatepickerModule,
    ...
  ],

If you have an old version

v.1.0.2 - 1.0.4: (Deprected)

v.1.0.1

import { BmDatepickerModule } from "bm-datepicker";

Usage:

<bm-datepicker></bm-datepicker>

Formbuilder, evaluations, callbacks and error handling

Is it possible to connect Bookmaker to my Formbuilder?

Yes it is. Use formGroupInput to connect to your formgroup and formControlNameInput to set a control name.

<form [formGroup]="generatedFormGroup" (submit)="submitForm()">
  <bm-datepicker [formGroupInput]="generatedFormGroup" formControlNameInput="dateFrom"> </bm-datepicker>
</form>

If you need to pre-define a date, you can do it in the component.

this.generatedFormGroup = this.formBuilder.group({
  dateFrom: ["24-02-2023"],
});

Note! If you don't need Formbuilder at all, then remove both formGroupInput and formControlNameInput, otherwise you will get an error. You will then need the calendarOutput to fetch the callback event:

<bm-datepicker (calendarOutput)="calendarToOutput($event)"></bm-datepicker>

Can I use Formbuilder Validators?

Of course. Put you validation requirements in you component or service. Bookmaker will handle it via the formControlNameInput you gave it.

How do I handle validated errors and error messages?

You can add the error messages below the Bookmaker and reference to the input field via the FormControllName. In component:

this.generatedFormGroup = this.formBuilder.group({
  dateFrom: ["", Validators.required],
});

In HTML:

<p *ngIf="generatedFormGroup.get('dateFrom')?.errors?.['required']">This field is required</p>

There is a build in invalid handler, that can be used as an evaluator connected to the entire form (if the input field is in a formgroup). This handler will be activated when the date don't follow the selected pattern.

<button type="submit" [disabled]="generatedFormGroup.invalid">Submit form</button>

Use the errorMessage option to show an error message of your choice, underneath the input field, when the date don't follow the selected pattern.

<bm-datepicker errorMessage="The field is invalid"></bm-datepicker>

What if I want a different format of the input field, can I change it?

Yes, you can! Use the pattern option to change format. The pattern is using lowercase letters for year, month and day.

<bm-datepicker pattern="mm/dd/yy"></bm-datepicker>

The default format is yyyy-mm-dd and do not need the pattern to be written out.

The available patterns are... Have it your way! yyyy.mm-dd, dd/yyyy mm, mm/dd-yy - everything goes! The restrictions are

  • The dividers can only be space , period ., forward slash / or dash -.
  • Days and months must two letters (mm) and (dd).
  • Year can be either two letters (yy) or four letters (yyyy).

Can I use the input field to change the date?

Yes, you can! The Bookmaker will accept changes as long as it follows the pattern. You can either pick a date from the calendar or fill it in manually.

The formated date will be presented in the input field and can be used via selected formControlNameInput or used from the callback calendarOutput.

Can I lock input field against manually changes?

Yes. You can lock the field with the [readonly] option (and it must be inside square brackets []). This option must have value of true to work. Default value is always false and do not need this option.

<bm-datepicker [readonly]="true"></bm-datepicker>

I don't use Formbuilder, can I get a callback response from Bookmaker?

You can use calendarOutput to hook up to a response function of your own. The response value is the same as selected pattern. Default pattern is yyyy-mm-dd. Make a function in the same component as the Bookmaker to fetch the event value from the calendarOutput.

<bm-datepicker (calendarOutput)="calendarToOutput($event)"></bm-datepicker>

Can I get a ISO-standard date callback?

There is an option to fetch an ISO-standard formatted date like Sat Feb 11 2023 01:00:00 GMT+0100. Use the isoCalendarOutput in those cases. Read more about handling data below "Date handling, double calendars and advanced callbacks"

<bm-datepicker (isoCalendarOutput)="isoOutput($event)"></bm-datepicker>

Names, labels and texts

Hey, I want to change the label! Can I do that?

Use the label option to change the text.

<bm-datepicker label="Date from"></bm-datepicker>

What about the placeholder, can I change that too?

Use the placeholder option to change the text. The default value is "Pick a date".

<bm-datepicker placeholder="Pick a date"></bm-datepicker>

How I change the name of the Weekdays?

Bookmaker comes with a default array of the names in english. Use it to change it to a language of your choice. Connect it to weekdays option.

In the HTML:

<bm-datepicker [weekdays]="weekdays"></bm-datepicker>

In the component:

weekdays = ['Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat', 'Sun'];

Can I change the week to start on a Sunday?

Yes! Use the [isSunday] option (and it must be inside square brackets []).

<bm-datepicker [isSunday]="true"></bm-datepicker>

How I change the name of the Months name?

Bookmaker comes with a default array of the names in english. Use it to change it to a language of your choice. Connect it to months option.

In the HTML:

<bm-datepicker [months]="months"></bm-datepicker>

In the component:

  months = [
    'January', 'February', 'March', 'April',
    'May', 'June', 'July', 'August',
    'September', 'October', 'November', 'December'
  ];

Styles, icons and animations

Can I change the animations?

You can't change the toggle effect, but you change when it happens. You can use the [manualClose] option to have the calendar open until you click on the open/close icon.

<bm-datepicker [manualClose]="true"></bm-datepicker>

Note! The toggle effect will occure after manually changes in the input field. This is needed to update the calender correctly and can't be turned off.

Can I change placement of the datepicker and input field?

Sorry, not yet. Officially. (Maybe if you hack the stylesheet - if so - let me know about it) :-)

I want a different calendar button icon. How do I change that (even if the default icon is quite fancy)?

Bookmaker have a default calendar icon. If you want to use your own, you can add it inside the Bookmaker. The default size i maximum 20px, but if you want bigger button, you should also change the size in the .bm-toggle-button style

<bm-datepicker>
  <ng-icon name="akarCalendar" size="16px" color="white"></ng-icon>
</bm-datepicker>

Can I change the stylesheet?

You can change everything in Bookmaker. That's the beauty. It is a list of styles, but hey, you are a developer, aren't you? You can use the stylesheet to change parts or everything, it's up to you. Treat it as an usual stylesheet in SCSS. This is an overview of all elements, but you can concentrate it and put togther elements as you wish.

Use styleSheet option to do the changes, and formControlNameInput to make an individual stylesheet:

In the HTML:

<bm-datepicker formControlNameInput="dateFrom" [styleSheet]="styles"> <bm-datepicker></bm-datepicker></bm-datepicker>

In the component:

styles = `
p.bm-paragraph{
    font-family:"Poppins", Verdana, sans-serif;
    color: #000;
    margin:0;
    padding:0;
} 
input.bm-date-input{
    font-family:"Poppins", Verdana, sans-serif;
    color: #000;
    margin:0;
    display:flex;
    flex:1;
    height:40px;
    border-radius: 9999px;
    padding: 3px 0 3px 20px; 
    border: 1px solid rgb(195, 195, 195)
} 
label.bm-label{
    font-family:"Poppins", Verdana, sans-serif;
    color: #000;
    margin:0;
    padding:0;
    font-size: .9rem;
    font-weight: 500;
    margin-left:15px;
}
.bm-date-input-wrapper{
    position:relative; 
    width: 100%; 
    display:flex;
    flex-direction:row; 
    align-items: center;
}
.bm-date-input-wrapper input[readonly] { 
    cursor: default !important;
    background:  rgb(245,245,245);
}
.bm-toggle-button{
    position:absolute; 
    right:5px; 
    width:30px; 
    height:30px;
    display:flex;
    align-items:center; 
    justify-content:center; 
    background-color:rgb(0, 153, 235);
    border: 1px solid rgb(0, 153, 235);
    border-radius: 9999px;
    transition:.5s
}
.bm-toggle-button:hover{
    background-color: rgb(0, 131, 202);
    cursor:pointer;
    color:fff;
    border: 1px solid rgb(0, 131, 202)
}
.bm-table{
    width:100%; 
    background-color:#f5f5f5;
    overflow: hidden;
    padding:16px; 
    display:flex;
    flex-direction: column; 
    border-radius: 8px; 
    box-shadow: 0 0 0.125rem 0 rgba(0,0,0,0.08), 0 0.125rem 0.75rem 0 rgba(0,0,0,0.24);
    box-sizing:border-box;
}
.bm-tr{
    display:flex;
    flex:1; 
    justify-content:space-between; 
    align-items:center;
    flex-direction: row;
}
.bm-th{
    display:flex; 
    flex:1; 
    justify-content:center; 
    align-items:center;
    flex-direction:row;
}
.bm-td{
    display:flex; 
    flex:1;
    justify-content:center; 
    align-items: center; 
}
.bm-td-empty{
    display:flex; 
    flex:1;
    justify-content:center; 
    align-items: center; 
}
.bm-td-empty .bm-td-inner-empty{
    height:30px;
    width:30px;
    margin:2px;
}
.bm-td-inner{
    height:30px;
    width:30px;
    margin:2px;
    display:flex; 
    justify-content:center; 
    align-items: center; 
    border-radius:9999px;
    transition: .4s;
    border: 1px solid #f5f5f5;
}
.bm-td-inner:hover{
    background-color: rgb(0, 131, 202);
    cursor:pointer;
    border: 1px solid rgb(0, 131, 202)
}
.bm-td-selected-day{
    background-color: rgb(0, 153, 235);
    cursor:pointer;
    border: 1px solid rgb(0, 153, 235)
}
.bm-td-inner:hover p{color:#fff !important}
.bm-td-selected-day p{color:#fff !important}
.bm-th p{
    font-size: .9rem;
    font-weight: 500;
}
.bm-td-inner p{
    font-size: .8rem;
    font-weight: 300;
}
.bm-daylabels-wrapper{margin-bottom: 20px}
.bm-year-month-title {
    display:flex; 
    flex: 5;
    justify-content: center; 
    align-items: center;
    flex-direction: column;
    margin-bottom: 10px
}
.bm-year-month-title p.bm-month-title{
    font-size: 1.5rem;
    font-weight: 600;
    margin-top: -8px
}
.bm-td-current-day{border: 1px solid rgb(0, 202, 101)}
.bm-td-lock-day{ 
    pointer-events:none;
    background: repeating-linear-gradient(-55deg,rgb(200, 200, 200), rgb(200, 200, 200) 2px,rgba(0,0,0,0) 2px, rgba(0,0,0,0) 4px);
    border: 1px solid rgb(200, 200, 200)
}
.bm-arrow {
    border: solid #000;
    border-width: 0 3px 3px 0;
    display: inline-block;
    padding: 3px;
}
.bm-td-inner:hover .bm-arrow{
    border-color:white;
    border-width: 0 3px 3px 0;
}
.bm-arrow-right {transform: rotate(-45deg);}
.bm-arrow-left {transform: rotate(135deg);}
.bm-weekend {color: #ff0000 !important}
.bm-error-message {
    padding: 8px 16px 20px 16px;
}
.bm-error-message p{
    color: #ff0000;
    font-size: .8rem;
}
  `;

There is a reference to the font "Poppins" in the stylesheet, but I only get Verdana?

To handle external fonts in Bookmaker, you need to have them globally in your Angular project. Use the link below in your root index.html to get Poppins fontstyle:

<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700;800&display=swap" rel="stylesheet" />

Date handling, double calendars and advanced callbacks

What is the default behavior?

The default behavior is to lock dates before current date. Current day is indicated with a green border. Selected date have blue background (if you don't change the stylesheet...)

<bm-datepicker></bm-datepicker>

How do I pre-define my own selected date?

You must use pattern and selectedDate options. Default pattern is yyyy-mm-dd. If you're using this, you don't need to add this option, only selectedDate. It is important that selectedDate uses the same pattern as pattern.

<bm-datepicker pattern="dd-mm-yyyy" selectedDate="24-02-2023"></bm-datepicker>

If you want a dynamic date from the component, you can wrap the option name inside square brackets []

    selectedDateValue="24-02-2023"

And add square brackets around the option [selectedDate].

<bm-datepicker pattern="dd-mm-yyyy" [selectedDate]="selectedDateValue"></bm-datepicker>

Note! The option selectedDate overrides the default settings. If only selectedDate is used without lockDateBefore or lockDateAfter, all dates are unlocked.

Can I lock dates before or after a current or a pre-defined date?

If you want to define your own lockings you can use the lockDateBefore or lockDateAfter options or use [lockDateBefore] or [lockDateAfter] for dynamic values. It is important that lockDateBefore and lockDateAfter uses the same pattern as pattern.

<bm-datepicker pattern="dd-mm-yyyy" lockDateBefore="24-02-2023"></bm-datepicker> <bm-datepicker pattern="dd-mm-yyyy" lockDateAfter="24-02-2023"></bm-datepicker>

You can either combine both lockDateBefore and lockDateAfter to limit the input range.

<bm-datepicker pattern="dd-mm-yyyy" lockDateBefore="20-02-2023" lockDateAfter="24-02-2023"></bm-datepicker>

Or you can add lockDateAfter with selectedDate if you need all dates before the lockDateAfter.

<bm-datepicker pattern="dd-mm-yyyy" selectedDate="24-02-2023" lockDateAfter="24-02-2023"></bm-datepicker>

Or add all three and get a limit input range and a pre-selected date.

<bm-datepicker pattern="dd-mm-yyyy" selectedDate="24-02-2023" lockDateBefore="20-02-2023" lockDateAfter="24-02-2023"></bm-datepicker>

I want two datepickers - like "From Date" and "To Date", is that possible?

Of course! This is not a tiny lite version! This is the real deal! I assume that you have read through the documentation, because this tutorial needs some basic knownledge.

In this tutorial you will learn how to use two datepickers - a "From Date" and a "To Date". The "From Date" will be responsable for locking lockDateBefore inside "To Date" and the "To Date" will be responsable for locking lockDateAfter inside "From Date".

Why?

Because it will prevent "From Date" to be selected later than "To Date" and vice versa.

Also - "From Date" will be locked before current date.

1. Set up the HTML

It is important to use square brackets [] though the values will be dynamic. There will also need the callback function calendarOutput that will be used in the component.

<bm-datepicker label="From Date" pattern="dd-mm-yyyy" [lockDateAfter]="lockDateAfterValue" [selectedDate]="selectedFromDateValue" (calendarOutput)="lockDateBefore($event)"></bm-datepicker> <bm-datepicker label="To Date" pattern="dd-mm-yyyy" [lockDateBefore]="lockDateBeforeValue" [selectedDate]="selectedToDateValue" (calendarOutput)="lockDateAfter($event)"></bm-datepicker>

2. Set up the component

In the component we need variables to hold the dates and functions to receive callbacks from the datepicker.

lockDateAfterValue: string = ""
lockDateBeforeValue: string = ""
selectedFromDateValue: string = ""
selectedToDateValue: string = ""

lockDateBefore(event:any){
    this.lockDateBeforeValue = event.selectedDate
}
lockDateAfter(event:any){
    this.lockDateAfterValue = event.selectedDate
}

3. Pre-define dates

You can also pre-define dates from, for example, a database. Use the variables in the component to set values:

lockDateAfterValue: string = "24-02-2023";
lockDateBeforeValue: string = "15-02-2023";
selectedFromDateValue: string = "24-02-2023";
selectedToDateValue: string = "20-02-2023";

4. Lock both calendars from current date

So this works fine. But what about limit the before date to current date in both calendars? The "To Date" calendar is just fine, as it is, but if we use the dynamic option of [lockDateBefore] inside the "From Date"...

<bm-datepicker label="From Date" pattern="dd-mm-yyyy" [lockDateAfter]="lockDateAfterValue" [lockDateBefore]="lockDateBeforeValue" [selectedDate]="selectedFromDateValue" (calendarOutput)="lockDateBefore($event)"></bm-datepicker>

...There will only be trouble. The problem is that the value will change everytime everytime we select a new date. We can always select forward, but never backwards. How to solve this - it is simple. Add a new variable in the component that won't be updated at all:

constantLockDateBeforeValue: string = "23-02-2023";

And add it to the [lockDateBefore] in "From Date":

<bm-datepicker label="From Date" pattern="dd-mm-yyyy" [lockDateBefore]="constantLockDateBeforeValue" [lockDateAfter]="lockDateAfterValue" [selectedDate]="selectedFromDateValue" (calendarOutput)="lockDateBefore($event)"></bm-datepicker> <bm-datepicker label="To Date" pattern="dd-mm-yyyy" [lockDateBefore]="lockDateBeforeValue" [selectedDate]="selectedToDateValue" (calendarOutput)="lockDateAfter($event)"></bm-datepicker>

Done!

5. Test, tweak and test again!

That's it! Now it's up to you! Good luck!

Author

Steffo Dimfelt steffo.dimfelt@gmail.com

Version list

  • 1.0.10: Adjust stylesheet
  • 1.0.8: Adjust locked days and selected date

Package Sidebar

Install

npm i bm-datepicker

Weekly Downloads

0

Version

1.0.10

License

MIT

Unpacked Size

264 kB

Total Files

41

Last publish

Collaborators

  • steffodimfelt