ngxsmk-datepicker 2.2.2 → 2.2.6
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.
- package/README.md +1317 -1313
- package/docs/API.md +2 -1
- package/docs/COMPATIBILITY.md +2 -1
- package/docs/INTEGRATION.md +702 -701
- package/docs/IONIC_INTEGRATION.md +2 -1
- package/docs/IONIC_TESTING.md +2 -1
- package/docs/LOCALE-GUIDE.md +2 -1
- package/docs/PLUGIN-ARCHITECTURE.md +2 -1
- package/docs/SEO.md +2 -1
- package/docs/SSR-EXAMPLE.md +2 -1
- package/docs/THEME-TOKENS.md +2 -1
- package/docs/TIMEZONE.md +2 -1
- package/docs/extension-points.md +2 -1
- package/docs/signal-forms.md +2 -1
- package/docs/signals.md +2 -1
- package/docs/ssr.md +2 -1
- package/package.json +85 -92
- package/styles/ionic-integration.css +220 -220
- package/fesm2022/ngxsmk-datepicker.mjs +0 -14153
- package/types/ngxsmk-datepicker.d.ts +0 -2412
package/docs/INTEGRATION.md
CHANGED
|
@@ -1,701 +1,702 @@
|
|
|
1
|
-
# Integration Guides
|
|
2
|
-
|
|
3
|
-
**Last updated:** March
|
|
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
|
-
|
|
1
|
+
# Integration Guides
|
|
2
|
+
|
|
3
|
+
**Last updated:** March 10, 2026 · **Current stable:** v2.2.6
|
|
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
|
+
|
|
702
|
+
|