ngxsmk-datepicker
Version:
<!-- SEO Keywords: Angular DatePicker, Angular Date Range Picker, Lightweight Calendar Component, Angular Signals DatePicker, SSR Ready DatePicker, Zoneless Angular, A11y DatePicker, Mobile-Friendly DatePicker, Ionic DatePicker Meta Description: The
703 lines (575 loc) • 21.5 kB
Markdown
# Integration Guides
**Last updated:** March 21, 2026 · **Current stable:** v2.2.8
This document provides integration examples for using ngxsmk-datepicker with popular frameworks and libraries.
## Table of Contents
- [Theming](#theming)
- [Accessibility](#accessibility)
- [Input sanitization and CSP](#input-sanitization-and-csp)
- [Angular Material](#angular-material)
- [Ionic](#ionic)
- [Tailwind CSS](#tailwind-css)
- [React, Vue, & Vanilla JS (Web Components)](#react-vue--vanilla-js-web-components)
- [Modals and overlays](#modals-and-overlays)
## Theming
Two different mechanisms apply:
- **Component input `[theme]`**: Accepts only `'light'` or `'dark'`. Use it to switch the built-in light/dark color set (e.g. `[theme]="'dark'"` or `[theme]="isDark() ? 'dark' : 'light'"`).
- **ThemeBuilderService.applyTheme(themeObject, element?)**: Accepts a theme **object** (colors, spacing, borderRadius, shadows, etc.) and applies it as CSS variables to the given element (or globally if no element). Use it for custom brand colors and full design tokens. See [THEME-TOKENS.md](THEME-TOKENS.md).
- When `element` is a **wrapper** (not the `ngxsmk-datepicker` host), the theme is applied to the wrapper and all descendant `ngxsmk-datepicker` elements so library defaults are overridden.
- Use `theme.shadows.focus` to customize the input focus ring (e.g. `'0 0 0 3px color-mix(in srgb, var(--datepicker-primary-color) 15%, transparent)'`). Internal `--ngxsmk-color-*` variables are bridged from your theme colors automatically.
Do not pass a theme object to the `[theme]` input; use `ThemeBuilderService` for that.
## Accessibility
The datepicker is built with **accessibility in mind**: keyboard navigation (arrows, Enter, Escape, T/Y/N/W, etc.), ARIA roles and labels on interactive elements, and live regions for screen reader announcements. For keyboard shortcuts and ARIA options see [API.md – Keyboard Support](API.md#keyboard-support) and the ARIA-related inputs in the API reference.
## Input sanitization and CSP
- **Input sanitization**: The library sanitizes user-provided date/time strings (e.g. from the input field) before use: it strips HTML delimiters, script handlers, and dangerous protocols. Template bindings do not use `innerHTML` for user content, so Angular's `DomSanitizer` is not required for normal usage.
- **CSP**: If your app enforces a Content-Security-Policy, ensure it allows the same directives required by Angular (e.g. `script-src` for your app and Angular, `style-src` for component styles). The datepicker does not use `eval`, inline scripts, or nonce-based scripts; it uses standard Angular templates and styles. No extra CSP directives are required specifically for ngxsmk-datepicker.
## Angular Material
The main `ngxsmk-datepicker` bundle does **not** import `@angular/material`, so non-Material apps are not forced to install it. If you use `mat-form-field`, install Material and add the directive as below.
### Installation
```bash
npm install @angular/material @angular/cdk ngxsmk-datepicker
```
### Basic Integration (Standalone Components)
**Recommended:** Use the **`ngxsmkMatFormFieldControl`** directive on the datepicker so `mat-form-field` finds it. Add this directive file to your project (e.g. `ngxsmk-mat-form-field.directive.ts`) so only Material apps pull in `@angular/material`:
```typescript
// ngxsmk-mat-form-field.directive.ts
import { Directive, forwardRef } from '@angular/core';
import { MatFormFieldControl } from '@angular/material/form-field';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
@Directive({
selector: 'ngxsmk-datepicker[ngxsmkMatFormFieldControl]',
standalone: true,
providers: [
{ provide: MatFormFieldControl, useExisting: forwardRef(() => NgxsmkDatepickerComponent) },
],
})
export class NgxsmkDatepickerMatFormFieldControlDirective {}
```
Then in your component:
```typescript
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
import { NgxsmkDatepickerMatFormFieldControlDirective } from './ngxsmk-mat-form-field.directive'; // your local file
import { MatFormFieldModule } from '@angular/material/form-field';
@Component({
imports: [MatFormFieldModule, NgxsmkDatepickerComponent, NgxsmkDatepickerMatFormFieldControlDirective, ...],
template: `
<mat-form-field appearance="outline">
<mat-label>Select Date</mat-label>
<ngxsmk-datepicker ngxsmkMatFormFieldControl [value]="dateControl.value" (valueChange)="dateControl.setValue($event)" ... />
</mat-form-field>
`
})
```
If you see "mat-form-field must contain a MatFormFieldControl", add the directive to the datepicker host (see Issue #187).
**Alternative (legacy / when directive is not used):** In `main.ts` before bootstrap, call `NgxsmkDatepickerComponent.withMaterialSupport(MatFormFieldControl)` (and optionally set `globalThis.__NGXSMK_MAT_FORM_FIELD_CONTROL__ = MatFormFieldControl`). Then use the datepicker inside `mat-form-field` without the directive. Prefer the directive above.
```typescript
import { Component } from '@angular/core';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
@Component({
selector: 'app-datepicker',
standalone: true,
imports: [
NgxsmkDatepickerComponent,
MatFormFieldModule,
MatInputModule,
ReactiveFormsModule
],
template: `
<mat-form-field appearance="outline">
<mat-label>Select Date</mat-label>
<ngxsmk-datepicker
[value]="dateControl.value"
(valueChange)="dateControl.setValue($event)"
[theme]="materialTheme"
placeholder="Choose a date">
</ngxsmk-datepicker>
</mat-form-field>
`
})
export class DatepickerComponent {
dateControl = new FormControl<Date | null>(null);
materialTheme = {
colors: {
primary: '#3f51b5',
background: '#ffffff',
text: '#212121',
border: '#e0e0e0',
hover: '#f5f5f5'
},
borderRadius: {
md: '4px'
}
};
}
```
**If you see "mat-form-field must contain a MatFormFieldControl":** Add the **`ngxsmkMatFormFieldControl`** directive to the datepicker (Option A above). Do not use `MAT_FORM_FIELD` or pass the wrong token; the directive is the supported path.
### Integration with Non-Standalone Components (NgModules)
Add the same directive file (see snippet above) to your project, then import it in your NgModule:
```typescript
import { NgModule } from '@angular/core';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
import { NgxsmkDatepickerMatFormFieldControlDirective } from './ngxsmk-mat-form-field.directive'; // your local file
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
NgxsmkDatepickerComponent,
NgxsmkDatepickerMatFormFieldControlDirective,
MatFormFieldModule,
MatInputModule,
ReactiveFormsModule
]
})
export class MyModule { }
```
Use `ngxsmkMatFormFieldControl` on the datepicker in your templates.
Then use it in your component:
```typescript
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-material-form',
template: `
<form [formGroup]="myForm">
<mat-form-field appearance="outline">
<mat-label>Select Date</mat-label>
<ngxsmk-datepicker
mode="single"
formControlName="date"
placeholder="Choose a date">
</ngxsmk-datepicker>
</mat-form-field>
</form>
`
})
export class MaterialFormComponent {
myForm = new FormGroup({
date: new FormControl<Date | null>(null)
});
}
```
### Using with Reactive Forms
```typescript
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
@Component({
selector: 'app-material-form',
standalone: true,
imports: [
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
NgxsmkDatepickerComponent
],
template: `
<form [formGroup]="myForm">
<mat-form-field appearance="outline">
<mat-label>Select Date</mat-label>
<ngxsmk-datepicker
mode="single"
formControlName="date"
placeholder="Choose a date">
</ngxsmk-datepicker>
</mat-form-field>
</form>
`
})
export class MaterialFormComponent {
myForm = new FormGroup({
date: new FormControl<Date | null>(null)
});
}
```
### With Material Datepicker Styling
```typescript
import { Component, inject, ElementRef } from '@angular/core';
import { NgxsmkDatepickerComponent, ThemeBuilderService } from 'ngxsmk-datepicker';
@Component({
selector: 'app-material-datepicker',
standalone: true,
imports: [NgxsmkDatepickerComponent],
template: `
<div class="material-datepicker-wrapper">
<ngxsmk-datepicker
[value]="selectedDate"
(valueChange)="selectedDate = $event"
theme="light"
[classes]="materialClasses">
</ngxsmk-datepicker>
</div>
`,
styles: [`
.material-datepicker-wrapper {
width: 100%;
}
:host ::ng-deep .ngxsmk-input-group {
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 4px;
padding: 8px 12px;
transition: border-color 0.2s;
}
:host ::ng-deep .ngxsmk-input-group:hover {
border-color: rgba(0, 0, 0, 0.24);
}
:host ::ng-deep .ngxsmk-input-group:focus-within {
border-color: #3f51b5;
border-width: 2px;
}
`]
})
export class MaterialDatepickerComponent {
private themeBuilder = inject(ThemeBuilderService);
private hostEl = inject(ElementRef).nativeElement;
selectedDate: Date | null = null;
materialTheme = {
colors: {
primary: '#3f51b5',
primaryContrast: '#ffffff',
background: '#ffffff',
text: '#212121',
border: '#e0e0e0',
hover: '#f5f5f5'
},
borderRadius: { md: '4px', lg: '8px' }
};
ngOnInit() {
this.themeBuilder.applyTheme(this.materialTheme, this.hostEl);
}
materialClasses = {
wrapper: 'material-datepicker',
inputGroup: 'material-input-group',
popover: 'material-popover'
};
}
```
## Ionic
### Installation
```bash
npm install @ionic/angular ngxsmk-datepicker
```
### Basic Integration
```typescript
import { Component } from '@angular/core';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
import { IonItem, IonLabel } from '@ionic/angular/standalone';
@Component({
selector: 'app-ionic-datepicker',
standalone: true,
imports: [NgxsmkDatepickerComponent, IonItem, IonLabel],
template: `
<ion-item>
<ion-label position="stacked">Select Date</ion-label>
<ngxsmk-datepicker
[value]="selectedDate"
(valueChange)="selectedDate = $event"
[theme]="ionicTheme"
[classes]="ionicClasses">
</ngxsmk-datepicker>
</ion-item>
`,
styles: [`
:host ::ng-deep .ngxsmk-input-group {
border: 1px solid var(--ion-color-medium);
border-radius: 8px;
background: var(--ion-background-color);
}
:host ::ng-deep .ngxsmk-popover-container {
--datepicker-primary-color: var(--ion-color-primary);
--datepicker-background: var(--ion-background-color);
--datepicker-text-color: var(--ion-text-color);
}
`]
})
export class IonicDatepickerComponent {
selectedDate: Date | null = null;
ionicTheme = {
colors: {
primary: 'var(--ion-color-primary)',
background: 'var(--ion-background-color)',
text: 'var(--ion-text-color)',
border: 'var(--ion-color-medium)',
hover: 'var(--ion-color-light)'
},
borderRadius: {
md: '8px',
lg: '12px'
}
};
ionicClasses = {
wrapper: 'ionic-datepicker',
inputGroup: 'ionic-input-group'
};
}
```
### With Ionic Popover
```typescript
import { Component } from '@angular/core';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
import { PopoverController } from '@ionic/angular';
@Component({
selector: 'app-datepicker-popover',
standalone: true,
imports: [NgxsmkDatepickerComponent],
template: `
<ion-button (click)="openDatepicker()">
Select Date
</ion-button>
`
})
export class DatepickerPopoverComponent {
constructor(private popoverController: PopoverController) {}
async openDatepicker() {
const popover = await this.popoverController.create({
component: NgxsmkDatepickerComponent,
componentProps: {
inline: true,
value: new Date()
},
cssClass: 'datepicker-popover'
});
await popover.present();
}
}
```
## Tailwind CSS
### Installation
```bash
npm install ngxsmk-datepicker tailwindcss
```
### Basic Integration
```typescript
import { Component } from '@angular/core';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
@Component({
selector: 'app-tailwind-datepicker',
standalone: true,
imports: [NgxsmkDatepickerComponent],
template: `
<div class="w-full max-w-md">
<label class="block text-sm font-medium text-gray-700 mb-2">
Select Date
</label>
<ngxsmk-datepicker
[value]="selectedDate"
(valueChange)="selectedDate = $event"
[classes]="tailwindClasses">
</ngxsmk-datepicker>
</div>
`,
styles: [`
:host ::ng-deep .ngxsmk-input-group {
@apply border border-gray-300 rounded-lg px-4 py-2
focus-within:ring-2 focus-within:ring-blue-500
focus-within:border-blue-500 transition-all;
}
:host ::ng-deep .ngxsmk-display-input {
@apply w-full outline-none text-gray-900;
}
:host ::ng-deep .ngxsmk-popover-container {
@apply shadow-lg rounded-lg border border-gray-200;
}
:host ::ng-deep .ngxsmk-day-cell.selected {
@apply bg-blue-500 text-white;
}
:host ::ng-deep .ngxsmk-day-cell:hover:not(.disabled) {
@apply bg-blue-50;
}
`]
})
export class TailwindDatepickerComponent {
selectedDate: Date | null = null;
tailwindClasses = {
wrapper: 'tailwind-datepicker',
inputGroup: 'tailwind-input-group',
popover: 'tailwind-popover'
};
}
```
### With Tailwind Utility Classes
```typescript
import { Component } from '@angular/core';
import { NgxsmkDatepickerComponent, ThemeBuilderService } from 'ngxsmk-datepicker';
import { inject } from '@angular/core';
@Component({
selector: 'app-tailwind-themed-datepicker',
standalone: true,
imports: [NgxsmkDatepickerComponent],
template: `
<div class="container mx-auto p-4">
<ngxsmk-datepicker
[value]="selectedDate"
(valueChange)="selectedDate = $event"
[theme]="tailwindTheme"
class="w-full">
</ngxsmk-datepicker>
</div>
`
})
export class TailwindThemedDatepickerComponent {
private themeBuilder = inject(ThemeBuilderService);
selectedDate: Date | null = null;
tailwindTheme = {
colors: {
primary: '#3b82f6', // Tailwind blue-500
background: '#ffffff',
text: '#111827', // gray-900
border: '#d1d5db', // gray-300
hover: '#f3f4f6' // gray-100
},
spacing: {
sm: '0.5rem',
md: '1rem',
lg: '1.5rem'
},
borderRadius: {
md: '0.5rem',
lg: '0.75rem'
}
};
constructor() {
// Apply theme globally
this.themeBuilder.applyTheme(this.tailwindTheme);
}
}
```
## Custom Styling Tips
### Using CSS Variables
All datepicker styles can be customized using CSS variables:
```css
:root {
--datepicker-primary-color: #3b82f6;
--datepicker-background: #ffffff;
--datepicker-text-color: #111827;
--datepicker-border-color: #d1d5db;
--datepicker-spacing-md: 1rem;
--datepicker-radius-md: 0.5rem;
}
```
### Scoped Styling
For component-specific styling:
```typescript
@Component({
styles: [`
:host ::ng-deep .ngxsmk-datepicker-wrapper {
/* Your custom styles */
}
`]
})
```
### Theme Builder Service
For dynamic theming:
```typescript
import { ThemeBuilderService } from 'ngxsmk-datepicker';
constructor(private themeBuilder: ThemeBuilderService) {
const theme = {
colors: {
primary: '#your-color',
// ... other colors
}
};
// Apply globally
this.themeBuilder.applyTheme(theme);
// Or get CSS-in-JS style object
const styles = this.themeBuilder.generateStyleObject(theme);
}
```
## Best Practices
1. **Consistent Theming**: Use your framework's design tokens (colors, spacing, etc.) when creating themes
2. **Accessibility**: Ensure your custom themes maintain sufficient color contrast
3. **Responsive Design**: Test datepicker on different screen sizes, especially when using multi-calendar mode
4. **Performance**: Use `OnPush` change detection strategy when possible
5. **Localization**: Set the `locale` input to match your application's locale
## Troubleshooting
### Styles Not Applying
- Ensure your styles are scoped correctly using `::ng-deep` or view encapsulation
- Check that CSS variables are defined in the correct scope
- Verify that the theme object structure matches the `DatepickerTheme` interface
### Integration Issues
- Make sure all required peer dependencies are installed
- Check that Angular version is compatible (Angular 17+)
- Verify that standalone components are properly imported
## React, Vue, & Vanilla JS (Web Components)
Since `ngxsmk-datepicker` is built as a highly isolated Angular library without heavy dependencies, it can be compiled into **Custom Web Components** using Angular Elements. This allows you to use exactly the same datepicker in React, Vue, Svelte, or Vanilla JavaScript.
### 1. Build as Custom Element
You'll need a wrapper to bootstrap the Angular component as a custom element:
```typescript
import { createApplication } from '@angular/platform-browser';
import { createCustomElement } from '@angular/elements';
import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
(async () => {
const app = await createApplication();
const DatepickerElement = createCustomElement(NgxsmkDatepickerComponent, {
injector: app.injector
});
customElements.define('ngxsmk-datepicker', DatepickerElement);
})().catch(err => console.error(err));
```
### 2. Framework-specific Usage
Once registered as `<ngxsmk-datepicker>`, you can use it in any framework.
#### React Example
```jsx
import React, { useEffect, useRef } from 'react';
export function MyView() {
const datepickerRef = useRef(null);
useEffect(() => {
// Listen to native custom events
const picker = datepickerRef.current;
const handleSelect = (e) => console.log('Selected:', e.detail);
picker.addEventListener('dateSelect', handleSelect);
return () => picker.removeEventListener('dateSelect', handleSelect);
}, []);
return (
<ngxsmk-datepicker
ref={datepickerRef}
mode="range"
theme="light">
</ngxsmk-datepicker>
);
}
```
#### Vue Example
```html
<script setup>
import { ref } from 'vue';
const date = ref(null);
const onDateSelect = (e) => {
date.value = e.detail;
};
</script>
<template>
<ngxsmk-datepicker
mode="single"
@dateSelect="onDateSelect"
></ngxsmk-datepicker>
</template>
```
#### Vanilla JS
```html
<ngxsmk-datepicker id="myPicker"></ngxsmk-datepicker>
<script>
const picker = document.getElementById('myPicker');
picker.addEventListener('dateSelect', (e) => {
alert('Date selected: ' + e.detail);
});
</script>
```
## Modals and overlays
When using the datepicker inside a modal, dialog, or overlay (e.g. `role="dialog"`, Angular Material dialog, or a custom modal), set **`[appendToBody]="true"`** so the calendar popover is appended to `document.body`. This avoids stacking-context and overflow issues and ensures the popover positions correctly and does not flash in the wrong place on first open. The library can auto-detect some modal containers and enable append-to-body; for reliability we recommend setting it explicitly:
```html
<ngxsmk-datepicker
[appendToBody]="true"
[(ngModel)]="myDate"
placeholder="Pick a date">
</ngxsmk-datepicker>
```
See the demo app **Integrations** page for a full "Datepicker in a modal" example.
## Additional Resources
- [API Documentation](./API.md)
- [Theme Guide](./THEME-TOKENS.md)
- [Locale Guide](./LOCALE-GUIDE.md)