ngxsmk-datepicker 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,631 +1,701 @@
1
- # Integration Guides
2
-
3
- **Last updated:** February 25, 2026 · **Current stable:** v2.2.0
4
-
5
- This document provides integration examples for using ngxsmk-datepicker with popular frameworks and libraries.
6
-
7
- ## Table of Contents
8
-
9
- - [Angular Material](#angular-material)
10
- - [Ionic](#ionic)
11
- - [Tailwind CSS](#tailwind-css)
12
- - [React, Vue, & Vanilla JS (Web Components)](#react-vue--vanilla-js-web-components)
13
- - [Modals and overlays](#modals-and-overlays)
14
-
15
- ## Angular Material
16
-
17
- ### Installation
18
-
19
- ```bash
20
- npm install @angular/material @angular/cdk ngxsmk-datepicker
21
- ```
22
-
23
- ### Basic Integration (Standalone Components)
24
-
25
- ```typescript
26
- import { Component } from '@angular/core';
27
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
28
- import { MatFormFieldModule } from '@angular/material/form-field';
29
- import { MatInputModule } from '@angular/material/input';
30
- import { ReactiveFormsModule, FormControl } from '@angular/forms';
31
-
32
- @Component({
33
- selector: 'app-datepicker',
34
- standalone: true,
35
- imports: [
36
- NgxsmkDatepickerComponent,
37
- MatFormFieldModule,
38
- MatInputModule,
39
- ReactiveFormsModule
40
- ],
41
- template: `
42
- <mat-form-field appearance="outline">
43
- <mat-label>Select Date</mat-label>
44
- <ngxsmk-datepicker
45
- [value]="dateControl.value"
46
- (valueChange)="dateControl.setValue($event)"
47
- [theme]="materialTheme"
48
- placeholder="Choose a date">
49
- </ngxsmk-datepicker>
50
- </mat-form-field>
51
- `
52
- })
53
- export class DatepickerComponent {
54
- dateControl = new FormControl<Date | null>(null);
55
-
56
- materialTheme = {
57
- colors: {
58
- primary: '#3f51b5',
59
- background: '#ffffff',
60
- text: '#212121',
61
- border: '#e0e0e0',
62
- hover: '#f5f5f5'
63
- },
64
- borderRadius: {
65
- md: '4px'
66
- }
67
- };
68
- }
69
- ```
70
-
71
- ### Integration with Non-Standalone Components (NgModules)
72
-
73
- For non-standalone components, you need to provide the Material form field control token using the `provideMaterialFormFieldControl` helper:
74
-
75
- ```typescript
76
- import { NgModule } from '@angular/core';
77
- import { MAT_FORM_FIELD_CONTROL } from '@angular/material/form-field';
78
- import { NgxsmkDatepickerComponent, provideMaterialFormFieldControl } from 'ngxsmk-datepicker';
79
- import { MatFormFieldModule } from '@angular/material/form-field';
80
- import { MatInputModule } from '@angular/material/input';
81
- import { ReactiveFormsModule } from '@angular/forms';
82
-
83
- @NgModule({
84
- imports: [
85
- NgxsmkDatepickerComponent,
86
- MatFormFieldModule,
87
- MatInputModule,
88
- ReactiveFormsModule
89
- ],
90
- providers: [provideMaterialFormFieldControl(MAT_FORM_FIELD_CONTROL)]
91
- })
92
- export class MyModule { }
93
- ```
94
-
95
- Then use it in your component:
96
-
97
- ```typescript
98
- import { Component } from '@angular/core';
99
- import { FormControl, FormGroup } from '@angular/forms';
100
-
101
- @Component({
102
- selector: 'app-material-form',
103
- template: `
104
- <form [formGroup]="myForm">
105
- <mat-form-field appearance="outline">
106
- <mat-label>Select Date</mat-label>
107
- <ngxsmk-datepicker
108
- mode="single"
109
- formControlName="date"
110
- placeholder="Choose a date">
111
- </ngxsmk-datepicker>
112
- </mat-form-field>
113
- </form>
114
- `
115
- })
116
- export class MaterialFormComponent {
117
- myForm = new FormGroup({
118
- date: new FormControl<Date | null>(null)
119
- });
120
- }
121
- ```
122
-
123
- ### Using with Reactive Forms
124
-
125
- ```typescript
126
- import { Component } from '@angular/core';
127
- import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
128
- import { MatFormFieldModule } from '@angular/material/form-field';
129
- import { MatInputModule } from '@angular/material/input';
130
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
131
-
132
- @Component({
133
- selector: 'app-material-form',
134
- standalone: true,
135
- imports: [
136
- ReactiveFormsModule,
137
- MatFormFieldModule,
138
- MatInputModule,
139
- NgxsmkDatepickerComponent
140
- ],
141
- template: `
142
- <form [formGroup]="myForm">
143
- <mat-form-field appearance="outline">
144
- <mat-label>Select Date</mat-label>
145
- <ngxsmk-datepicker
146
- mode="single"
147
- formControlName="date"
148
- placeholder="Choose a date">
149
- </ngxsmk-datepicker>
150
- </mat-form-field>
151
- </form>
152
- `
153
- })
154
- export class MaterialFormComponent {
155
- myForm = new FormGroup({
156
- date: new FormControl<Date | null>(null)
157
- });
158
- }
159
- ```
160
-
161
- ### With Material Datepicker Styling
162
-
163
- ```typescript
164
- import { Component } from '@angular/core';
165
- import { NgxsmkDatepickerComponent, ThemeBuilderService } from 'ngxsmk-datepicker';
166
- import { inject } from '@angular/core';
167
-
168
- @Component({
169
- selector: 'app-material-datepicker',
170
- standalone: true,
171
- imports: [NgxsmkDatepickerComponent],
172
- template: `
173
- <div class="material-datepicker-wrapper">
174
- <ngxsmk-datepicker
175
- [value]="selectedDate"
176
- (valueChange)="selectedDate = $event"
177
- [theme]="materialTheme"
178
- [classes]="materialClasses">
179
- </ngxsmk-datepicker>
180
- </div>
181
- `,
182
- styles: [`
183
- .material-datepicker-wrapper {
184
- width: 100%;
185
- }
186
-
187
- :host ::ng-deep .ngxsmk-input-group {
188
- border: 1px solid rgba(0, 0, 0, 0.12);
189
- border-radius: 4px;
190
- padding: 8px 12px;
191
- transition: border-color 0.2s;
192
- }
193
-
194
- :host ::ng-deep .ngxsmk-input-group:hover {
195
- border-color: rgba(0, 0, 0, 0.24);
196
- }
197
-
198
- :host ::ng-deep .ngxsmk-input-group:focus-within {
199
- border-color: #3f51b5;
200
- border-width: 2px;
201
- }
202
- `]
203
- })
204
- export class MaterialDatepickerComponent {
205
- private themeBuilder = inject(ThemeBuilderService);
206
- selectedDate: Date | null = null;
207
-
208
- materialTheme = {
209
- colors: {
210
- primary: '#3f51b5',
211
- primaryContrast: '#ffffff',
212
- background: '#ffffff',
213
- text: '#212121',
214
- border: '#e0e0e0',
215
- hover: '#f5f5f5'
216
- },
217
- borderRadius: {
218
- md: '4px',
219
- lg: '8px'
220
- }
221
- };
222
-
223
- materialClasses = {
224
- wrapper: 'material-datepicker',
225
- inputGroup: 'material-input-group',
226
- popover: 'material-popover'
227
- };
228
- }
229
- ```
230
-
231
- ## Ionic
232
-
233
- ### Installation
234
-
235
- ```bash
236
- npm install @ionic/angular ngxsmk-datepicker
237
- ```
238
-
239
- ### Basic Integration
240
-
241
- ```typescript
242
- import { Component } from '@angular/core';
243
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
244
- import { IonItem, IonLabel } from '@ionic/angular/standalone';
245
-
246
- @Component({
247
- selector: 'app-ionic-datepicker',
248
- standalone: true,
249
- imports: [NgxsmkDatepickerComponent, IonItem, IonLabel],
250
- template: `
251
- <ion-item>
252
- <ion-label position="stacked">Select Date</ion-label>
253
- <ngxsmk-datepicker
254
- [value]="selectedDate"
255
- (valueChange)="selectedDate = $event"
256
- [theme]="ionicTheme"
257
- [classes]="ionicClasses">
258
- </ngxsmk-datepicker>
259
- </ion-item>
260
- `,
261
- styles: [`
262
- :host ::ng-deep .ngxsmk-input-group {
263
- border: 1px solid var(--ion-color-medium);
264
- border-radius: 8px;
265
- background: var(--ion-background-color);
266
- }
267
-
268
- :host ::ng-deep .ngxsmk-popover-container {
269
- --datepicker-primary-color: var(--ion-color-primary);
270
- --datepicker-background: var(--ion-background-color);
271
- --datepicker-text-color: var(--ion-text-color);
272
- }
273
- `]
274
- })
275
- export class IonicDatepickerComponent {
276
- selectedDate: Date | null = null;
277
-
278
- ionicTheme = {
279
- colors: {
280
- primary: 'var(--ion-color-primary)',
281
- background: 'var(--ion-background-color)',
282
- text: 'var(--ion-text-color)',
283
- border: 'var(--ion-color-medium)',
284
- hover: 'var(--ion-color-light)'
285
- },
286
- borderRadius: {
287
- md: '8px',
288
- lg: '12px'
289
- }
290
- };
291
-
292
- ionicClasses = {
293
- wrapper: 'ionic-datepicker',
294
- inputGroup: 'ionic-input-group'
295
- };
296
- }
297
- ```
298
-
299
- ### With Ionic Popover
300
-
301
- ```typescript
302
- import { Component } from '@angular/core';
303
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
304
- import { PopoverController } from '@ionic/angular';
305
-
306
- @Component({
307
- selector: 'app-datepicker-popover',
308
- standalone: true,
309
- imports: [NgxsmkDatepickerComponent],
310
- template: `
311
- <ion-button (click)="openDatepicker()">
312
- Select Date
313
- </ion-button>
314
- `
315
- })
316
- export class DatepickerPopoverComponent {
317
- constructor(private popoverController: PopoverController) {}
318
-
319
- async openDatepicker() {
320
- const popover = await this.popoverController.create({
321
- component: NgxsmkDatepickerComponent,
322
- componentProps: {
323
- inline: true,
324
- value: new Date()
325
- },
326
- cssClass: 'datepicker-popover'
327
- });
328
-
329
- await popover.present();
330
- }
331
- }
332
- ```
333
-
334
- ## Tailwind CSS
335
-
336
- ### Installation
337
-
338
- ```bash
339
- npm install ngxsmk-datepicker tailwindcss
340
- ```
341
-
342
- ### Basic Integration
343
-
344
- ```typescript
345
- import { Component } from '@angular/core';
346
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
347
-
348
- @Component({
349
- selector: 'app-tailwind-datepicker',
350
- standalone: true,
351
- imports: [NgxsmkDatepickerComponent],
352
- template: `
353
- <div class="w-full max-w-md">
354
- <label class="block text-sm font-medium text-gray-700 mb-2">
355
- Select Date
356
- </label>
357
- <ngxsmk-datepicker
358
- [value]="selectedDate"
359
- (valueChange)="selectedDate = $event"
360
- [classes]="tailwindClasses">
361
- </ngxsmk-datepicker>
362
- </div>
363
- `,
364
- styles: [`
365
- :host ::ng-deep .ngxsmk-input-group {
366
- @apply border border-gray-300 rounded-lg px-4 py-2
367
- focus-within:ring-2 focus-within:ring-blue-500
368
- focus-within:border-blue-500 transition-all;
369
- }
370
-
371
- :host ::ng-deep .ngxsmk-display-input {
372
- @apply w-full outline-none text-gray-900;
373
- }
374
-
375
- :host ::ng-deep .ngxsmk-popover-container {
376
- @apply shadow-lg rounded-lg border border-gray-200;
377
- }
378
-
379
- :host ::ng-deep .ngxsmk-day-cell.selected {
380
- @apply bg-blue-500 text-white;
381
- }
382
-
383
- :host ::ng-deep .ngxsmk-day-cell:hover:not(.disabled) {
384
- @apply bg-blue-50;
385
- }
386
- `]
387
- })
388
- export class TailwindDatepickerComponent {
389
- selectedDate: Date | null = null;
390
-
391
- tailwindClasses = {
392
- wrapper: 'tailwind-datepicker',
393
- inputGroup: 'tailwind-input-group',
394
- popover: 'tailwind-popover'
395
- };
396
- }
397
- ```
398
-
399
- ### With Tailwind Utility Classes
400
-
401
- ```typescript
402
- import { Component } from '@angular/core';
403
- import { NgxsmkDatepickerComponent, ThemeBuilderService } from 'ngxsmk-datepicker';
404
- import { inject } from '@angular/core';
405
-
406
- @Component({
407
- selector: 'app-tailwind-themed-datepicker',
408
- standalone: true,
409
- imports: [NgxsmkDatepickerComponent],
410
- template: `
411
- <div class="container mx-auto p-4">
412
- <ngxsmk-datepicker
413
- [value]="selectedDate"
414
- (valueChange)="selectedDate = $event"
415
- [theme]="tailwindTheme"
416
- class="w-full">
417
- </ngxsmk-datepicker>
418
- </div>
419
- `
420
- })
421
- export class TailwindThemedDatepickerComponent {
422
- private themeBuilder = inject(ThemeBuilderService);
423
- selectedDate: Date | null = null;
424
-
425
- tailwindTheme = {
426
- colors: {
427
- primary: '#3b82f6', // Tailwind blue-500
428
- background: '#ffffff',
429
- text: '#111827', // gray-900
430
- border: '#d1d5db', // gray-300
431
- hover: '#f3f4f6' // gray-100
432
- },
433
- spacing: {
434
- sm: '0.5rem',
435
- md: '1rem',
436
- lg: '1.5rem'
437
- },
438
- borderRadius: {
439
- md: '0.5rem',
440
- lg: '0.75rem'
441
- }
442
- };
443
-
444
- constructor() {
445
- // Apply theme globally
446
- this.themeBuilder.applyTheme(this.tailwindTheme);
447
- }
448
- }
449
- ```
450
-
451
- ## Custom Styling Tips
452
-
453
- ### Using CSS Variables
454
-
455
- All datepicker styles can be customized using CSS variables:
456
-
457
- ```css
458
- :root {
459
- --datepicker-primary-color: #3b82f6;
460
- --datepicker-background: #ffffff;
461
- --datepicker-text-color: #111827;
462
- --datepicker-border-color: #d1d5db;
463
- --datepicker-spacing-md: 1rem;
464
- --datepicker-radius-md: 0.5rem;
465
- }
466
- ```
467
-
468
- ### Scoped Styling
469
-
470
- For component-specific styling:
471
-
472
- ```typescript
473
- @Component({
474
- styles: [`
475
- :host ::ng-deep .ngxsmk-datepicker-wrapper {
476
- /* Your custom styles */
477
- }
478
- `]
479
- })
480
- ```
481
-
482
- ### Theme Builder Service
483
-
484
- For dynamic theming:
485
-
486
- ```typescript
487
- import { ThemeBuilderService } from 'ngxsmk-datepicker';
488
-
489
- constructor(private themeBuilder: ThemeBuilderService) {
490
- const theme = {
491
- colors: {
492
- primary: '#your-color',
493
- // ... other colors
494
- }
495
- };
496
-
497
- // Apply globally
498
- this.themeBuilder.applyTheme(theme);
499
-
500
- // Or get CSS-in-JS style object
501
- const styles = this.themeBuilder.generateStyleObject(theme);
502
- }
503
- ```
504
-
505
- ## Best Practices
506
-
507
- 1. **Consistent Theming**: Use your framework's design tokens (colors, spacing, etc.) when creating themes
508
- 2. **Accessibility**: Ensure your custom themes maintain sufficient color contrast
509
- 3. **Responsive Design**: Test datepicker on different screen sizes, especially when using multi-calendar mode
510
- 4. **Performance**: Use `OnPush` change detection strategy when possible
511
- 5. **Localization**: Set the `locale` input to match your application's locale
512
-
513
- ## Troubleshooting
514
-
515
- ### Styles Not Applying
516
-
517
- - Ensure your styles are scoped correctly using `::ng-deep` or view encapsulation
518
- - Check that CSS variables are defined in the correct scope
519
- - Verify that the theme object structure matches the `DatepickerTheme` interface
520
-
521
- ### Integration Issues
522
-
523
- - Make sure all required peer dependencies are installed
524
- - Check that Angular version is compatible (Angular 17+)
525
- - Verify that standalone components are properly imported
526
-
527
- ## React, Vue, & Vanilla JS (Web Components)
528
-
529
- 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.
530
-
531
- ### 1. Build as Custom Element
532
-
533
- You'll need a wrapper to bootstrap the Angular component as a custom element:
534
-
535
- ```typescript
536
- import { createApplication } from '@angular/platform-browser';
537
- import { createCustomElement } from '@angular/elements';
538
- import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
539
-
540
- (async () => {
541
- const app = await createApplication();
542
-
543
- const DatepickerElement = createCustomElement(NgxsmkDatepickerComponent, {
544
- injector: app.injector
545
- });
546
-
547
- customElements.define('ngxsmk-datepicker', DatepickerElement);
548
- })().catch(err => console.error(err));
549
- ```
550
-
551
- ### 2. Framework-specific Usage
552
-
553
- Once registered as `<ngxsmk-datepicker>`, you can use it in any framework.
554
-
555
- #### React Example
556
- ```jsx
557
- import React, { useEffect, useRef } from 'react';
558
-
559
- export function MyView() {
560
- const datepickerRef = useRef(null);
561
-
562
- useEffect(() => {
563
- // Listen to native custom events
564
- const picker = datepickerRef.current;
565
- const handleSelect = (e) => console.log('Selected:', e.detail);
566
-
567
- picker.addEventListener('dateSelect', handleSelect);
568
- return () => picker.removeEventListener('dateSelect', handleSelect);
569
- }, []);
570
-
571
- return (
572
- <ngxsmk-datepicker
573
- ref={datepickerRef}
574
- mode="range"
575
- theme="light">
576
- </ngxsmk-datepicker>
577
- );
578
- }
579
- ```
580
-
581
- #### Vue Example
582
- ```html
583
- <script setup>
584
- import { ref } from 'vue';
585
-
586
- const date = ref(null);
587
- const onDateSelect = (e) => {
588
- date.value = e.detail;
589
- };
590
- </script>
591
-
592
- <template>
593
- <ngxsmk-datepicker
594
- mode="single"
595
- @dateSelect="onDateSelect"
596
- ></ngxsmk-datepicker>
597
- </template>
598
- ```
599
-
600
- #### Vanilla JS
601
- ```html
602
- <ngxsmk-datepicker id="myPicker"></ngxsmk-datepicker>
603
-
604
- <script>
605
- const picker = document.getElementById('myPicker');
606
- picker.addEventListener('dateSelect', (e) => {
607
- alert('Date selected: ' + e.detail);
608
- });
609
- </script>
610
- ```
611
-
612
- ## Modals and overlays
613
-
614
- 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:
615
-
616
- ```html
617
- <ngxsmk-datepicker
618
- [appendToBody]="true"
619
- [(ngModel)]="myDate"
620
- placeholder="Pick a date">
621
- </ngxsmk-datepicker>
622
- ```
623
-
624
- See the demo app **Integrations** page for a full "Datepicker in a modal" example.
625
-
626
- ## Additional Resources
627
-
628
- - [API Documentation](./API.md)
629
- - [Theme Guide](./THEME-TOKENS.md)
630
- - [Locale Guide](./LOCALE-GUIDE.md)
631
-
1
+ # Integration Guides
2
+
3
+ **Last updated:** March 3, 2026 · **Current stable:** v2.2.2
4
+
5
+ This document provides integration examples for using ngxsmk-datepicker with popular frameworks and libraries.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Theming](#theming)
10
+ - [Accessibility](#accessibility)
11
+ - [Input sanitization and CSP](#input-sanitization-and-csp)
12
+ - [Angular Material](#angular-material)
13
+ - [Ionic](#ionic)
14
+ - [Tailwind CSS](#tailwind-css)
15
+ - [React, Vue, & Vanilla JS (Web Components)](#react-vue--vanilla-js-web-components)
16
+ - [Modals and overlays](#modals-and-overlays)
17
+
18
+ ## Theming
19
+
20
+ Two different mechanisms apply:
21
+
22
+ - **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'"`).
23
+ - **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).
24
+ - 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.
25
+ - 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.
26
+
27
+ Do not pass a theme object to the `[theme]` input; use `ThemeBuilderService` for that.
28
+
29
+ ## Accessibility
30
+
31
+ 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.
32
+
33
+ ## Input sanitization and CSP
34
+
35
+ - **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.
36
+ - **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.
37
+
38
+ ## Angular Material
39
+
40
+ 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.
41
+
42
+ ### Installation
43
+
44
+ ```bash
45
+ npm install @angular/material @angular/cdk ngxsmk-datepicker
46
+ ```
47
+
48
+ ### Basic Integration (Standalone Components)
49
+
50
+ **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`:
51
+
52
+ ```typescript
53
+ // ngxsmk-mat-form-field.directive.ts
54
+ import { Directive, forwardRef } from '@angular/core';
55
+ import { MatFormFieldControl } from '@angular/material/form-field';
56
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
57
+
58
+ @Directive({
59
+ selector: 'ngxsmk-datepicker[ngxsmkMatFormFieldControl]',
60
+ standalone: true,
61
+ providers: [
62
+ { provide: MatFormFieldControl, useExisting: forwardRef(() => NgxsmkDatepickerComponent) },
63
+ ],
64
+ })
65
+ export class NgxsmkDatepickerMatFormFieldControlDirective {}
66
+ ```
67
+
68
+ Then in your component:
69
+
70
+ ```typescript
71
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
72
+ import { NgxsmkDatepickerMatFormFieldControlDirective } from './ngxsmk-mat-form-field.directive'; // your local file
73
+ import { MatFormFieldModule } from '@angular/material/form-field';
74
+
75
+ @Component({
76
+ imports: [MatFormFieldModule, NgxsmkDatepickerComponent, NgxsmkDatepickerMatFormFieldControlDirective, ...],
77
+ template: `
78
+ <mat-form-field appearance="outline">
79
+ <mat-label>Select Date</mat-label>
80
+ <ngxsmk-datepicker ngxsmkMatFormFieldControl [value]="dateControl.value" (valueChange)="dateControl.setValue($event)" ... />
81
+ </mat-form-field>
82
+ `
83
+ })
84
+ ```
85
+
86
+ If you see "mat-form-field must contain a MatFormFieldControl", add the directive to the datepicker host (see Issue #187).
87
+
88
+ **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.
89
+
90
+ ```typescript
91
+ import { Component } from '@angular/core';
92
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
93
+ import { MatFormFieldModule } from '@angular/material/form-field';
94
+ import { MatInputModule } from '@angular/material/input';
95
+ import { ReactiveFormsModule, FormControl } from '@angular/forms';
96
+
97
+ @Component({
98
+ selector: 'app-datepicker',
99
+ standalone: true,
100
+ imports: [
101
+ NgxsmkDatepickerComponent,
102
+ MatFormFieldModule,
103
+ MatInputModule,
104
+ ReactiveFormsModule
105
+ ],
106
+ template: `
107
+ <mat-form-field appearance="outline">
108
+ <mat-label>Select Date</mat-label>
109
+ <ngxsmk-datepicker
110
+ [value]="dateControl.value"
111
+ (valueChange)="dateControl.setValue($event)"
112
+ [theme]="materialTheme"
113
+ placeholder="Choose a date">
114
+ </ngxsmk-datepicker>
115
+ </mat-form-field>
116
+ `
117
+ })
118
+ export class DatepickerComponent {
119
+ dateControl = new FormControl<Date | null>(null);
120
+
121
+ materialTheme = {
122
+ colors: {
123
+ primary: '#3f51b5',
124
+ background: '#ffffff',
125
+ text: '#212121',
126
+ border: '#e0e0e0',
127
+ hover: '#f5f5f5'
128
+ },
129
+ borderRadius: {
130
+ md: '4px'
131
+ }
132
+ };
133
+ }
134
+ ```
135
+
136
+ **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.
137
+
138
+ ### Integration with Non-Standalone Components (NgModules)
139
+
140
+ Add the same directive file (see snippet above) to your project, then import it in your NgModule:
141
+
142
+ ```typescript
143
+ import { NgModule } from '@angular/core';
144
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
145
+ import { NgxsmkDatepickerMatFormFieldControlDirective } from './ngxsmk-mat-form-field.directive'; // your local file
146
+ import { MatFormFieldModule } from '@angular/material/form-field';
147
+ import { MatInputModule } from '@angular/material/input';
148
+ import { ReactiveFormsModule } from '@angular/forms';
149
+
150
+ @NgModule({
151
+ imports: [
152
+ NgxsmkDatepickerComponent,
153
+ NgxsmkDatepickerMatFormFieldControlDirective,
154
+ MatFormFieldModule,
155
+ MatInputModule,
156
+ ReactiveFormsModule
157
+ ]
158
+ })
159
+ export class MyModule { }
160
+ ```
161
+
162
+ Use `ngxsmkMatFormFieldControl` on the datepicker in your templates.
163
+
164
+ Then use it in your component:
165
+
166
+ ```typescript
167
+ import { Component } from '@angular/core';
168
+ import { FormControl, FormGroup } from '@angular/forms';
169
+
170
+ @Component({
171
+ selector: 'app-material-form',
172
+ template: `
173
+ <form [formGroup]="myForm">
174
+ <mat-form-field appearance="outline">
175
+ <mat-label>Select Date</mat-label>
176
+ <ngxsmk-datepicker
177
+ mode="single"
178
+ formControlName="date"
179
+ placeholder="Choose a date">
180
+ </ngxsmk-datepicker>
181
+ </mat-form-field>
182
+ </form>
183
+ `
184
+ })
185
+ export class MaterialFormComponent {
186
+ myForm = new FormGroup({
187
+ date: new FormControl<Date | null>(null)
188
+ });
189
+ }
190
+ ```
191
+
192
+ ### Using with Reactive Forms
193
+
194
+ ```typescript
195
+ import { Component } from '@angular/core';
196
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
197
+ import { MatFormFieldModule } from '@angular/material/form-field';
198
+ import { MatInputModule } from '@angular/material/input';
199
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
200
+
201
+ @Component({
202
+ selector: 'app-material-form',
203
+ standalone: true,
204
+ imports: [
205
+ ReactiveFormsModule,
206
+ MatFormFieldModule,
207
+ MatInputModule,
208
+ NgxsmkDatepickerComponent
209
+ ],
210
+ template: `
211
+ <form [formGroup]="myForm">
212
+ <mat-form-field appearance="outline">
213
+ <mat-label>Select Date</mat-label>
214
+ <ngxsmk-datepicker
215
+ mode="single"
216
+ formControlName="date"
217
+ placeholder="Choose a date">
218
+ </ngxsmk-datepicker>
219
+ </mat-form-field>
220
+ </form>
221
+ `
222
+ })
223
+ export class MaterialFormComponent {
224
+ myForm = new FormGroup({
225
+ date: new FormControl<Date | null>(null)
226
+ });
227
+ }
228
+ ```
229
+
230
+ ### With Material Datepicker Styling
231
+
232
+ ```typescript
233
+ import { Component, inject, ElementRef } from '@angular/core';
234
+ import { NgxsmkDatepickerComponent, ThemeBuilderService } from 'ngxsmk-datepicker';
235
+
236
+ @Component({
237
+ selector: 'app-material-datepicker',
238
+ standalone: true,
239
+ imports: [NgxsmkDatepickerComponent],
240
+ template: `
241
+ <div class="material-datepicker-wrapper">
242
+ <ngxsmk-datepicker
243
+ [value]="selectedDate"
244
+ (valueChange)="selectedDate = $event"
245
+ theme="light"
246
+ [classes]="materialClasses">
247
+ </ngxsmk-datepicker>
248
+ </div>
249
+ `,
250
+ styles: [`
251
+ .material-datepicker-wrapper {
252
+ width: 100%;
253
+ }
254
+
255
+ :host ::ng-deep .ngxsmk-input-group {
256
+ border: 1px solid rgba(0, 0, 0, 0.12);
257
+ border-radius: 4px;
258
+ padding: 8px 12px;
259
+ transition: border-color 0.2s;
260
+ }
261
+
262
+ :host ::ng-deep .ngxsmk-input-group:hover {
263
+ border-color: rgba(0, 0, 0, 0.24);
264
+ }
265
+
266
+ :host ::ng-deep .ngxsmk-input-group:focus-within {
267
+ border-color: #3f51b5;
268
+ border-width: 2px;
269
+ }
270
+ `]
271
+ })
272
+ export class MaterialDatepickerComponent {
273
+ private themeBuilder = inject(ThemeBuilderService);
274
+ private hostEl = inject(ElementRef).nativeElement;
275
+ selectedDate: Date | null = null;
276
+
277
+ materialTheme = {
278
+ colors: {
279
+ primary: '#3f51b5',
280
+ primaryContrast: '#ffffff',
281
+ background: '#ffffff',
282
+ text: '#212121',
283
+ border: '#e0e0e0',
284
+ hover: '#f5f5f5'
285
+ },
286
+ borderRadius: { md: '4px', lg: '8px' }
287
+ };
288
+
289
+ ngOnInit() {
290
+ this.themeBuilder.applyTheme(this.materialTheme, this.hostEl);
291
+ }
292
+
293
+ materialClasses = {
294
+ wrapper: 'material-datepicker',
295
+ inputGroup: 'material-input-group',
296
+ popover: 'material-popover'
297
+ };
298
+ }
299
+ ```
300
+
301
+ ## Ionic
302
+
303
+ ### Installation
304
+
305
+ ```bash
306
+ npm install @ionic/angular ngxsmk-datepicker
307
+ ```
308
+
309
+ ### Basic Integration
310
+
311
+ ```typescript
312
+ import { Component } from '@angular/core';
313
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
314
+ import { IonItem, IonLabel } from '@ionic/angular/standalone';
315
+
316
+ @Component({
317
+ selector: 'app-ionic-datepicker',
318
+ standalone: true,
319
+ imports: [NgxsmkDatepickerComponent, IonItem, IonLabel],
320
+ template: `
321
+ <ion-item>
322
+ <ion-label position="stacked">Select Date</ion-label>
323
+ <ngxsmk-datepicker
324
+ [value]="selectedDate"
325
+ (valueChange)="selectedDate = $event"
326
+ [theme]="ionicTheme"
327
+ [classes]="ionicClasses">
328
+ </ngxsmk-datepicker>
329
+ </ion-item>
330
+ `,
331
+ styles: [`
332
+ :host ::ng-deep .ngxsmk-input-group {
333
+ border: 1px solid var(--ion-color-medium);
334
+ border-radius: 8px;
335
+ background: var(--ion-background-color);
336
+ }
337
+
338
+ :host ::ng-deep .ngxsmk-popover-container {
339
+ --datepicker-primary-color: var(--ion-color-primary);
340
+ --datepicker-background: var(--ion-background-color);
341
+ --datepicker-text-color: var(--ion-text-color);
342
+ }
343
+ `]
344
+ })
345
+ export class IonicDatepickerComponent {
346
+ selectedDate: Date | null = null;
347
+
348
+ ionicTheme = {
349
+ colors: {
350
+ primary: 'var(--ion-color-primary)',
351
+ background: 'var(--ion-background-color)',
352
+ text: 'var(--ion-text-color)',
353
+ border: 'var(--ion-color-medium)',
354
+ hover: 'var(--ion-color-light)'
355
+ },
356
+ borderRadius: {
357
+ md: '8px',
358
+ lg: '12px'
359
+ }
360
+ };
361
+
362
+ ionicClasses = {
363
+ wrapper: 'ionic-datepicker',
364
+ inputGroup: 'ionic-input-group'
365
+ };
366
+ }
367
+ ```
368
+
369
+ ### With Ionic Popover
370
+
371
+ ```typescript
372
+ import { Component } from '@angular/core';
373
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
374
+ import { PopoverController } from '@ionic/angular';
375
+
376
+ @Component({
377
+ selector: 'app-datepicker-popover',
378
+ standalone: true,
379
+ imports: [NgxsmkDatepickerComponent],
380
+ template: `
381
+ <ion-button (click)="openDatepicker()">
382
+ Select Date
383
+ </ion-button>
384
+ `
385
+ })
386
+ export class DatepickerPopoverComponent {
387
+ constructor(private popoverController: PopoverController) {}
388
+
389
+ async openDatepicker() {
390
+ const popover = await this.popoverController.create({
391
+ component: NgxsmkDatepickerComponent,
392
+ componentProps: {
393
+ inline: true,
394
+ value: new Date()
395
+ },
396
+ cssClass: 'datepicker-popover'
397
+ });
398
+
399
+ await popover.present();
400
+ }
401
+ }
402
+ ```
403
+
404
+ ## Tailwind CSS
405
+
406
+ ### Installation
407
+
408
+ ```bash
409
+ npm install ngxsmk-datepicker tailwindcss
410
+ ```
411
+
412
+ ### Basic Integration
413
+
414
+ ```typescript
415
+ import { Component } from '@angular/core';
416
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
417
+
418
+ @Component({
419
+ selector: 'app-tailwind-datepicker',
420
+ standalone: true,
421
+ imports: [NgxsmkDatepickerComponent],
422
+ template: `
423
+ <div class="w-full max-w-md">
424
+ <label class="block text-sm font-medium text-gray-700 mb-2">
425
+ Select Date
426
+ </label>
427
+ <ngxsmk-datepicker
428
+ [value]="selectedDate"
429
+ (valueChange)="selectedDate = $event"
430
+ [classes]="tailwindClasses">
431
+ </ngxsmk-datepicker>
432
+ </div>
433
+ `,
434
+ styles: [`
435
+ :host ::ng-deep .ngxsmk-input-group {
436
+ @apply border border-gray-300 rounded-lg px-4 py-2
437
+ focus-within:ring-2 focus-within:ring-blue-500
438
+ focus-within:border-blue-500 transition-all;
439
+ }
440
+
441
+ :host ::ng-deep .ngxsmk-display-input {
442
+ @apply w-full outline-none text-gray-900;
443
+ }
444
+
445
+ :host ::ng-deep .ngxsmk-popover-container {
446
+ @apply shadow-lg rounded-lg border border-gray-200;
447
+ }
448
+
449
+ :host ::ng-deep .ngxsmk-day-cell.selected {
450
+ @apply bg-blue-500 text-white;
451
+ }
452
+
453
+ :host ::ng-deep .ngxsmk-day-cell:hover:not(.disabled) {
454
+ @apply bg-blue-50;
455
+ }
456
+ `]
457
+ })
458
+ export class TailwindDatepickerComponent {
459
+ selectedDate: Date | null = null;
460
+
461
+ tailwindClasses = {
462
+ wrapper: 'tailwind-datepicker',
463
+ inputGroup: 'tailwind-input-group',
464
+ popover: 'tailwind-popover'
465
+ };
466
+ }
467
+ ```
468
+
469
+ ### With Tailwind Utility Classes
470
+
471
+ ```typescript
472
+ import { Component } from '@angular/core';
473
+ import { NgxsmkDatepickerComponent, ThemeBuilderService } from 'ngxsmk-datepicker';
474
+ import { inject } from '@angular/core';
475
+
476
+ @Component({
477
+ selector: 'app-tailwind-themed-datepicker',
478
+ standalone: true,
479
+ imports: [NgxsmkDatepickerComponent],
480
+ template: `
481
+ <div class="container mx-auto p-4">
482
+ <ngxsmk-datepicker
483
+ [value]="selectedDate"
484
+ (valueChange)="selectedDate = $event"
485
+ [theme]="tailwindTheme"
486
+ class="w-full">
487
+ </ngxsmk-datepicker>
488
+ </div>
489
+ `
490
+ })
491
+ export class TailwindThemedDatepickerComponent {
492
+ private themeBuilder = inject(ThemeBuilderService);
493
+ selectedDate: Date | null = null;
494
+
495
+ tailwindTheme = {
496
+ colors: {
497
+ primary: '#3b82f6', // Tailwind blue-500
498
+ background: '#ffffff',
499
+ text: '#111827', // gray-900
500
+ border: '#d1d5db', // gray-300
501
+ hover: '#f3f4f6' // gray-100
502
+ },
503
+ spacing: {
504
+ sm: '0.5rem',
505
+ md: '1rem',
506
+ lg: '1.5rem'
507
+ },
508
+ borderRadius: {
509
+ md: '0.5rem',
510
+ lg: '0.75rem'
511
+ }
512
+ };
513
+
514
+ constructor() {
515
+ // Apply theme globally
516
+ this.themeBuilder.applyTheme(this.tailwindTheme);
517
+ }
518
+ }
519
+ ```
520
+
521
+ ## Custom Styling Tips
522
+
523
+ ### Using CSS Variables
524
+
525
+ All datepicker styles can be customized using CSS variables:
526
+
527
+ ```css
528
+ :root {
529
+ --datepicker-primary-color: #3b82f6;
530
+ --datepicker-background: #ffffff;
531
+ --datepicker-text-color: #111827;
532
+ --datepicker-border-color: #d1d5db;
533
+ --datepicker-spacing-md: 1rem;
534
+ --datepicker-radius-md: 0.5rem;
535
+ }
536
+ ```
537
+
538
+ ### Scoped Styling
539
+
540
+ For component-specific styling:
541
+
542
+ ```typescript
543
+ @Component({
544
+ styles: [`
545
+ :host ::ng-deep .ngxsmk-datepicker-wrapper {
546
+ /* Your custom styles */
547
+ }
548
+ `]
549
+ })
550
+ ```
551
+
552
+ ### Theme Builder Service
553
+
554
+ For dynamic theming:
555
+
556
+ ```typescript
557
+ import { ThemeBuilderService } from 'ngxsmk-datepicker';
558
+
559
+ constructor(private themeBuilder: ThemeBuilderService) {
560
+ const theme = {
561
+ colors: {
562
+ primary: '#your-color',
563
+ // ... other colors
564
+ }
565
+ };
566
+
567
+ // Apply globally
568
+ this.themeBuilder.applyTheme(theme);
569
+
570
+ // Or get CSS-in-JS style object
571
+ const styles = this.themeBuilder.generateStyleObject(theme);
572
+ }
573
+ ```
574
+
575
+ ## Best Practices
576
+
577
+ 1. **Consistent Theming**: Use your framework's design tokens (colors, spacing, etc.) when creating themes
578
+ 2. **Accessibility**: Ensure your custom themes maintain sufficient color contrast
579
+ 3. **Responsive Design**: Test datepicker on different screen sizes, especially when using multi-calendar mode
580
+ 4. **Performance**: Use `OnPush` change detection strategy when possible
581
+ 5. **Localization**: Set the `locale` input to match your application's locale
582
+
583
+ ## Troubleshooting
584
+
585
+ ### Styles Not Applying
586
+
587
+ - Ensure your styles are scoped correctly using `::ng-deep` or view encapsulation
588
+ - Check that CSS variables are defined in the correct scope
589
+ - Verify that the theme object structure matches the `DatepickerTheme` interface
590
+
591
+ ### Integration Issues
592
+
593
+ - Make sure all required peer dependencies are installed
594
+ - Check that Angular version is compatible (Angular 17+)
595
+ - Verify that standalone components are properly imported
596
+
597
+ ## React, Vue, & Vanilla JS (Web Components)
598
+
599
+ 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.
600
+
601
+ ### 1. Build as Custom Element
602
+
603
+ You'll need a wrapper to bootstrap the Angular component as a custom element:
604
+
605
+ ```typescript
606
+ import { createApplication } from '@angular/platform-browser';
607
+ import { createCustomElement } from '@angular/elements';
608
+ import { NgxsmkDatepickerComponent } from 'ngxsmk-datepicker';
609
+
610
+ (async () => {
611
+ const app = await createApplication();
612
+
613
+ const DatepickerElement = createCustomElement(NgxsmkDatepickerComponent, {
614
+ injector: app.injector
615
+ });
616
+
617
+ customElements.define('ngxsmk-datepicker', DatepickerElement);
618
+ })().catch(err => console.error(err));
619
+ ```
620
+
621
+ ### 2. Framework-specific Usage
622
+
623
+ Once registered as `<ngxsmk-datepicker>`, you can use it in any framework.
624
+
625
+ #### React Example
626
+ ```jsx
627
+ import React, { useEffect, useRef } from 'react';
628
+
629
+ export function MyView() {
630
+ const datepickerRef = useRef(null);
631
+
632
+ useEffect(() => {
633
+ // Listen to native custom events
634
+ const picker = datepickerRef.current;
635
+ const handleSelect = (e) => console.log('Selected:', e.detail);
636
+
637
+ picker.addEventListener('dateSelect', handleSelect);
638
+ return () => picker.removeEventListener('dateSelect', handleSelect);
639
+ }, []);
640
+
641
+ return (
642
+ <ngxsmk-datepicker
643
+ ref={datepickerRef}
644
+ mode="range"
645
+ theme="light">
646
+ </ngxsmk-datepicker>
647
+ );
648
+ }
649
+ ```
650
+
651
+ #### Vue Example
652
+ ```html
653
+ <script setup>
654
+ import { ref } from 'vue';
655
+
656
+ const date = ref(null);
657
+ const onDateSelect = (e) => {
658
+ date.value = e.detail;
659
+ };
660
+ </script>
661
+
662
+ <template>
663
+ <ngxsmk-datepicker
664
+ mode="single"
665
+ @dateSelect="onDateSelect"
666
+ ></ngxsmk-datepicker>
667
+ </template>
668
+ ```
669
+
670
+ #### Vanilla JS
671
+ ```html
672
+ <ngxsmk-datepicker id="myPicker"></ngxsmk-datepicker>
673
+
674
+ <script>
675
+ const picker = document.getElementById('myPicker');
676
+ picker.addEventListener('dateSelect', (e) => {
677
+ alert('Date selected: ' + e.detail);
678
+ });
679
+ </script>
680
+ ```
681
+
682
+ ## Modals and overlays
683
+
684
+ 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:
685
+
686
+ ```html
687
+ <ngxsmk-datepicker
688
+ [appendToBody]="true"
689
+ [(ngModel)]="myDate"
690
+ placeholder="Pick a date">
691
+ </ngxsmk-datepicker>
692
+ ```
693
+
694
+ See the demo app **Integrations** page for a full "Datepicker in a modal" example.
695
+
696
+ ## Additional Resources
697
+
698
+ - [API Documentation](./API.md)
699
+ - [Theme Guide](./THEME-TOKENS.md)
700
+ - [Locale Guide](./LOCALE-GUIDE.md)
701
+