@bloc-ui/date-picker 0.0.1
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 +117 -0
- package/fesm2022/bloc-ui-date-picker.mjs +355 -0
- package/fesm2022/bloc-ui-date-picker.mjs.map +1 -0
- package/package.json +29 -0
- package/types/bloc-ui-date-picker.d.ts +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @bloc-ui/date-picker
|
|
2
|
+
|
|
3
|
+
> **Latest:** v0.0.1
|
|
4
|
+
|
|
5
|
+
Date picker component with calendar dropdown for Angular — part of the [Bloc UI](https://github.com/debasish1996/BLOC-UI) component library. Supports template-driven and reactive forms, min/max date enforcement, and customisable display format.
|
|
6
|
+
|
|
7
|
+
**[Live Documentation & Demos](https://debasish1996.github.io/BLOC-UI/)**
|
|
8
|
+
|
|
9
|
+
> **Tip:** You can also install [`@bloc-ui/kit`](https://www.npmjs.com/package/@bloc-ui/kit) to get this package along with every other Bloc UI component in a single import.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @bloc-ui/date-picker
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Peer dependencies:** `@angular/common`, `@angular/core`, and `@angular/forms` ≥ 21.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { BlocDatePickerComponent, BlocDatePickerModule } from '@bloc-ui/date-picker';
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Basic
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<bloc-date-picker /> <bloc-date-picker placeholder="Pick a date" />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### With ngModel
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<bloc-date-picker [(ngModel)]="selectedDate" />
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### With Reactive Forms
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<bloc-date-picker [formControl]="dateCtrl" />
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Sizes
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<bloc-date-picker size="sm" /> <bloc-date-picker size="lg" />
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Min / Max
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<bloc-date-picker [minDate]="minDate" [maxDate]="maxDate" />
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## `BlocDatePickerComponent` inputs
|
|
62
|
+
|
|
63
|
+
| Input | Type | Default | Description |
|
|
64
|
+
| ------------- | ---------------------- | --------------- | ----------------------- |
|
|
65
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Density preset |
|
|
66
|
+
| `placeholder` | `string` | `'Select date'` | Input placeholder text |
|
|
67
|
+
| `format` | `string` | `'yyyy-MM-dd'` | Display format |
|
|
68
|
+
| `disabled` | `boolean` | `false` | Disable the picker |
|
|
69
|
+
| `minDate` | `Date \| null` | `null` | Minimum selectable date |
|
|
70
|
+
| `maxDate` | `Date \| null` | `null` | Maximum selectable date |
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## CSS tokens
|
|
75
|
+
|
|
76
|
+
| Token | Fallback | Description |
|
|
77
|
+
| ------------------------------------ | ------------------------------ | ------------------------ |
|
|
78
|
+
| `--bloc-date-picker-padding` | `8px 36px 8px 12px` | Input padding |
|
|
79
|
+
| `--bloc-date-picker-font-size` | `0.875rem` | Input font size |
|
|
80
|
+
| `--bloc-date-picker-radius` | `6px` | Input border radius |
|
|
81
|
+
| `--bloc-date-picker-color` | `#374151` | Input text colour |
|
|
82
|
+
| `--bloc-date-picker-bg` | `#ffffff` | Input background |
|
|
83
|
+
| `--bloc-date-picker-border` | `var(--bloc-border, #d1d5db)` | Input border colour |
|
|
84
|
+
| `--bloc-date-picker-focus-border` | `var(--bloc-primary, #6b7280)` | Focus border colour |
|
|
85
|
+
| `--bloc-date-picker-focus-ring` | `rgba(107,114,128,0.2)` | Focus ring colour |
|
|
86
|
+
| `--bloc-date-picker-placeholder` | `#9ca3af` | Placeholder colour |
|
|
87
|
+
| `--bloc-date-picker-icon-color` | `#9ca3af` | Calendar icon colour |
|
|
88
|
+
| `--bloc-date-picker-z-index` | `100` | Dropdown z-index |
|
|
89
|
+
| `--bloc-date-picker-dropdown-bg` | `#ffffff` | Dropdown background |
|
|
90
|
+
| `--bloc-date-picker-dropdown-border` | `var(--bloc-border, #d1d5db)` | Dropdown border |
|
|
91
|
+
| `--bloc-date-picker-dropdown-shadow` | `0 4px 16px rgba(0,0,0,0.1)` | Dropdown shadow |
|
|
92
|
+
| `--bloc-date-picker-day-color` | `#374151` | Day text colour |
|
|
93
|
+
| `--bloc-date-picker-day-hover-bg` | `#f3f4f6` | Day hover background |
|
|
94
|
+
| `--bloc-date-picker-today-border` | `var(--bloc-primary, #6b7280)` | Today border colour |
|
|
95
|
+
| `--bloc-date-picker-selected-bg` | `var(--bloc-primary, #6b7280)` | Selected day background |
|
|
96
|
+
| `--bloc-date-picker-selected-color` | `#ffffff` | Selected day text colour |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Accessibility
|
|
101
|
+
|
|
102
|
+
- Input sets `aria-expanded` and `aria-haspopup="dialog"` for the calendar dropdown.
|
|
103
|
+
- Dropdown uses `role="dialog"` with `aria-label="Date picker"`.
|
|
104
|
+
- Closes on <kbd>Escape</kbd> key and outside click.
|
|
105
|
+
- Calendar includes a "Today" button for quick navigation.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Theming
|
|
110
|
+
|
|
111
|
+
All visual tokens (`--bloc-primary`, `--bloc-surface`, colour scales, dark-mode) are provided by the optional [`@bloc-ui/theme`](https://www.npmjs.com/package/@bloc-ui/theme) package. The date picker works without it — neutral fallbacks are applied automatically.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, ElementRef, input, signal, computed, forwardRef, HostListener, Component, NgModule } from '@angular/core';
|
|
3
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
|
+
|
|
5
|
+
const DAYS_OF_WEEK = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
|
|
6
|
+
/**
|
|
7
|
+
* Date picker component with calendar dropdown.
|
|
8
|
+
*
|
|
9
|
+
* ```html
|
|
10
|
+
* <bloc-date-picker [(ngModel)]="selectedDate" />
|
|
11
|
+
* <bloc-date-picker [formControl]="dateCtrl" placeholder="Choose date" />
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
class BlocDatePickerComponent {
|
|
15
|
+
_el = inject((ElementRef));
|
|
16
|
+
/** Preset size. Defaults to `'md'`. */
|
|
17
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
18
|
+
/** Placeholder text for the input. */
|
|
19
|
+
placeholder = input('Select date', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
20
|
+
/** Date format for display. Defaults to `'yyyy-MM-dd'`. */
|
|
21
|
+
format = input('yyyy-MM-dd', ...(ngDevMode ? [{ debugName: "format" }] : /* istanbul ignore next */ []));
|
|
22
|
+
/** Disable the date picker via template binding. */
|
|
23
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
24
|
+
/** Minimum selectable date. */
|
|
25
|
+
minDate = input(null, ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
|
|
26
|
+
/** Maximum selectable date. */
|
|
27
|
+
maxDate = input(null, ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
|
|
28
|
+
weekdays = DAYS_OF_WEEK;
|
|
29
|
+
/** Currently selected date. */
|
|
30
|
+
_selectedDate = signal(null, ...(ngDevMode ? [{ debugName: "_selectedDate" }] : /* istanbul ignore next */ []));
|
|
31
|
+
/** Whether the calendar dropdown is open. */
|
|
32
|
+
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
|
|
33
|
+
/** The month being viewed (year + month). */
|
|
34
|
+
_viewDate = signal(new Date(), ...(ngDevMode ? [{ debugName: "_viewDate" }] : /* istanbul ignore next */ []));
|
|
35
|
+
_formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_formDisabled" }] : /* istanbul ignore next */ []));
|
|
36
|
+
isDisabled = computed(() => this.disabled() || this._formDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
37
|
+
_onChange = () => { };
|
|
38
|
+
_onTouched = () => { };
|
|
39
|
+
displayValue = computed(() => {
|
|
40
|
+
const d = this._selectedDate();
|
|
41
|
+
if (!d)
|
|
42
|
+
return '';
|
|
43
|
+
return this._formatDate(d);
|
|
44
|
+
}, ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
|
|
45
|
+
monthYearLabel = computed(() => {
|
|
46
|
+
const d = this._viewDate();
|
|
47
|
+
return d.toLocaleString('default', { month: 'long', year: 'numeric' });
|
|
48
|
+
}, ...(ngDevMode ? [{ debugName: "monthYearLabel" }] : /* istanbul ignore next */ []));
|
|
49
|
+
calendarDays = computed(() => {
|
|
50
|
+
const viewDate = this._viewDate();
|
|
51
|
+
const selected = this._selectedDate();
|
|
52
|
+
const today = new Date();
|
|
53
|
+
const min = this.minDate();
|
|
54
|
+
const max = this.maxDate();
|
|
55
|
+
const year = viewDate.getFullYear();
|
|
56
|
+
const month = viewDate.getMonth();
|
|
57
|
+
const firstOfMonth = new Date(year, month, 1);
|
|
58
|
+
const startDay = firstOfMonth.getDay();
|
|
59
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
60
|
+
// Fill from previous month
|
|
61
|
+
const prevMonthDays = new Date(year, month, 0).getDate();
|
|
62
|
+
const days = [];
|
|
63
|
+
for (let i = startDay - 1; i >= 0; i--) {
|
|
64
|
+
const date = new Date(year, month - 1, prevMonthDays - i);
|
|
65
|
+
days.push(this._buildDay(date, false, today, selected, min, max));
|
|
66
|
+
}
|
|
67
|
+
for (let d = 1; d <= daysInMonth; d++) {
|
|
68
|
+
const date = new Date(year, month, d);
|
|
69
|
+
days.push(this._buildDay(date, true, today, selected, min, max));
|
|
70
|
+
}
|
|
71
|
+
// Fill remaining to complete 6 rows (42 cells)
|
|
72
|
+
const remaining = 42 - days.length;
|
|
73
|
+
for (let d = 1; d <= remaining; d++) {
|
|
74
|
+
const date = new Date(year, month + 1, d);
|
|
75
|
+
days.push(this._buildDay(date, false, today, selected, min, max));
|
|
76
|
+
}
|
|
77
|
+
return days;
|
|
78
|
+
}, ...(ngDevMode ? [{ debugName: "calendarDays" }] : /* istanbul ignore next */ []));
|
|
79
|
+
toggleCalendar() {
|
|
80
|
+
if (this.isDisabled())
|
|
81
|
+
return;
|
|
82
|
+
this.isOpen.update((v) => !v);
|
|
83
|
+
if (this.isOpen()) {
|
|
84
|
+
const selected = this._selectedDate();
|
|
85
|
+
if (selected) {
|
|
86
|
+
this._viewDate.set(new Date(selected.getFullYear(), selected.getMonth(), 1));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
selectDay(day) {
|
|
91
|
+
if (day.isDisabled)
|
|
92
|
+
return;
|
|
93
|
+
this._selectedDate.set(day.date);
|
|
94
|
+
this._onChange(day.date);
|
|
95
|
+
this.isOpen.set(false);
|
|
96
|
+
}
|
|
97
|
+
prevMonth() {
|
|
98
|
+
const d = this._viewDate();
|
|
99
|
+
this._viewDate.set(new Date(d.getFullYear(), d.getMonth() - 1, 1));
|
|
100
|
+
}
|
|
101
|
+
nextMonth() {
|
|
102
|
+
const d = this._viewDate();
|
|
103
|
+
this._viewDate.set(new Date(d.getFullYear(), d.getMonth() + 1, 1));
|
|
104
|
+
}
|
|
105
|
+
goToToday() {
|
|
106
|
+
const today = new Date();
|
|
107
|
+
this._selectedDate.set(today);
|
|
108
|
+
this._viewDate.set(new Date(today.getFullYear(), today.getMonth(), 1));
|
|
109
|
+
this._onChange(today);
|
|
110
|
+
this.isOpen.set(false);
|
|
111
|
+
}
|
|
112
|
+
clear() {
|
|
113
|
+
this._selectedDate.set(null);
|
|
114
|
+
this._onChange(null);
|
|
115
|
+
}
|
|
116
|
+
_onDocClick(event) {
|
|
117
|
+
if (!this._el.nativeElement.contains(event.target)) {
|
|
118
|
+
this.isOpen.set(false);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
_onEsc() {
|
|
122
|
+
this.isOpen.set(false);
|
|
123
|
+
}
|
|
124
|
+
// — ControlValueAccessor —
|
|
125
|
+
writeValue(val) {
|
|
126
|
+
this._selectedDate.set(val instanceof Date ? val : val ? new Date(val) : null);
|
|
127
|
+
}
|
|
128
|
+
registerOnChange(fn) {
|
|
129
|
+
this._onChange = fn;
|
|
130
|
+
}
|
|
131
|
+
registerOnTouched(fn) {
|
|
132
|
+
this._onTouched = fn;
|
|
133
|
+
}
|
|
134
|
+
setDisabledState(isDisabled) {
|
|
135
|
+
this._formDisabled.set(isDisabled);
|
|
136
|
+
}
|
|
137
|
+
// — Private helpers —
|
|
138
|
+
_buildDay(date, isCurrentMonth, today, selected, min, max) {
|
|
139
|
+
const isToday = date.getDate() === today.getDate() &&
|
|
140
|
+
date.getMonth() === today.getMonth() &&
|
|
141
|
+
date.getFullYear() === today.getFullYear();
|
|
142
|
+
const isSelected = selected
|
|
143
|
+
? date.getDate() === selected.getDate() &&
|
|
144
|
+
date.getMonth() === selected.getMonth() &&
|
|
145
|
+
date.getFullYear() === selected.getFullYear()
|
|
146
|
+
: false;
|
|
147
|
+
let isDisabled = false;
|
|
148
|
+
if (min) {
|
|
149
|
+
const minDay = new Date(min.getFullYear(), min.getMonth(), min.getDate());
|
|
150
|
+
isDisabled = date < minDay;
|
|
151
|
+
}
|
|
152
|
+
if (max && !isDisabled) {
|
|
153
|
+
const maxDay = new Date(max.getFullYear(), max.getMonth(), max.getDate());
|
|
154
|
+
isDisabled = date > maxDay;
|
|
155
|
+
}
|
|
156
|
+
return { date, day: date.getDate(), isCurrentMonth, isToday, isSelected, isDisabled };
|
|
157
|
+
}
|
|
158
|
+
_formatDate(d) {
|
|
159
|
+
const yyyy = d.getFullYear();
|
|
160
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
161
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
162
|
+
return this.format()
|
|
163
|
+
.replace('yyyy', String(yyyy))
|
|
164
|
+
.replace('MM', mm)
|
|
165
|
+
.replace('dd', dd);
|
|
166
|
+
}
|
|
167
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BlocDatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
168
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: BlocDatePickerComponent, isStandalone: true, selector: "bloc-date-picker", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:click": "_onDocClick($event)", "keydown.escape": "_onEsc()" }, properties: { "class.bloc-date-picker": "true", "class.bloc-date-picker--sm": "size() === \"sm\"", "class.bloc-date-picker--lg": "size() === \"lg\"", "class.bloc-date-picker--disabled": "isDisabled()", "class.bloc-date-picker--open": "isOpen()" } }, providers: [
|
|
169
|
+
{
|
|
170
|
+
provide: NG_VALUE_ACCESSOR,
|
|
171
|
+
useExisting: forwardRef(() => BlocDatePickerComponent),
|
|
172
|
+
multi: true,
|
|
173
|
+
},
|
|
174
|
+
], ngImport: i0, template: `
|
|
175
|
+
<div class="bloc-date-picker__input-wrapper" (click)="toggleCalendar()">
|
|
176
|
+
<input
|
|
177
|
+
class="bloc-date-picker__input"
|
|
178
|
+
type="text"
|
|
179
|
+
readonly
|
|
180
|
+
[value]="displayValue()"
|
|
181
|
+
[placeholder]="placeholder()"
|
|
182
|
+
[disabled]="isDisabled()"
|
|
183
|
+
[attr.aria-expanded]="isOpen()"
|
|
184
|
+
aria-haspopup="dialog" />
|
|
185
|
+
<span class="bloc-date-picker__icon" aria-hidden="true">
|
|
186
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
187
|
+
<rect x="2" y="3" width="12" height="11" rx="1.5" stroke="currentColor" stroke-width="1.3"/>
|
|
188
|
+
<line x1="2" y1="6" x2="14" y2="6" stroke="currentColor" stroke-width="1.3"/>
|
|
189
|
+
<line x1="5" y1="1.5" x2="5" y2="4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
|
190
|
+
<line x1="11" y1="1.5" x2="11" y2="4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
|
191
|
+
</svg>
|
|
192
|
+
</span>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
@if (isOpen()) {
|
|
196
|
+
<div class="bloc-date-picker__dropdown" role="dialog" aria-label="Date picker">
|
|
197
|
+
<div class="bloc-date-picker__header">
|
|
198
|
+
<button type="button" class="bloc-date-picker__nav" (click)="prevMonth()" aria-label="Previous month">
|
|
199
|
+
‹
|
|
200
|
+
</button>
|
|
201
|
+
<span class="bloc-date-picker__month-year">
|
|
202
|
+
{{ monthYearLabel() }}
|
|
203
|
+
</span>
|
|
204
|
+
<button type="button" class="bloc-date-picker__nav" (click)="nextMonth()" aria-label="Next month">
|
|
205
|
+
›
|
|
206
|
+
</button>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<div class="bloc-date-picker__weekdays">
|
|
210
|
+
@for (day of weekdays; track day) {
|
|
211
|
+
<span class="bloc-date-picker__weekday">{{ day }}</span>
|
|
212
|
+
}
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<div class="bloc-date-picker__days">
|
|
216
|
+
@for (day of calendarDays(); track day.date.getTime()) {
|
|
217
|
+
<button
|
|
218
|
+
type="button"
|
|
219
|
+
class="bloc-date-picker__day"
|
|
220
|
+
[class.bloc-date-picker__day--other]="!day.isCurrentMonth"
|
|
221
|
+
[class.bloc-date-picker__day--today]="day.isToday"
|
|
222
|
+
[class.bloc-date-picker__day--selected]="day.isSelected"
|
|
223
|
+
[class.bloc-date-picker__day--disabled]="day.isDisabled"
|
|
224
|
+
[disabled]="day.isDisabled"
|
|
225
|
+
(click)="selectDay(day)">
|
|
226
|
+
{{ day.day }}
|
|
227
|
+
</button>
|
|
228
|
+
}
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div class="bloc-date-picker__footer">
|
|
232
|
+
<button type="button" class="bloc-date-picker__today-btn" (click)="goToToday()">
|
|
233
|
+
Today
|
|
234
|
+
</button>
|
|
235
|
+
@if (_selectedDate()) {
|
|
236
|
+
<button type="button" class="bloc-date-picker__clear-btn" (click)="clear()">
|
|
237
|
+
Clear
|
|
238
|
+
</button>
|
|
239
|
+
}
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
}
|
|
243
|
+
`, isInline: true, styles: [":host{display:inline-block;position:relative;font-family:inherit}.bloc-date-picker__input-wrapper{display:flex;align-items:center;cursor:pointer;position:relative}.bloc-date-picker__input{appearance:none;border-width:1px;border-style:solid;font-family:inherit;cursor:pointer;outline:none;width:100%;box-sizing:border-box;padding:var(--bloc-date-picker-padding, 8px 36px 8px 12px);font-size:var(--bloc-date-picker-font-size, .875rem);border-radius:var(--bloc-date-picker-radius, 6px);transition:border-color .15s ease}:where(.bloc-date-picker__input){color:var(--bloc-date-picker-color, #374151);background-color:var(--bloc-date-picker-bg, #ffffff);border-color:var(--bloc-date-picker-border, var(--bloc-border, #d1d5db))}:where(.bloc-date-picker__input:focus){border-color:var(--bloc-date-picker-focus-border, var(--bloc-primary, #6b7280));box-shadow:0 0 0 2px var(--bloc-date-picker-focus-ring, rgba(107, 114, 128, .2))}:where(.bloc-date-picker__input::placeholder){color:var(--bloc-date-picker-placeholder, #9ca3af)}.bloc-date-picker__icon{position:absolute;right:10px;top:50%;transform:translateY(-50%);pointer-events:none;display:flex;align-items:center}:where(.bloc-date-picker__icon){color:var(--bloc-date-picker-icon-color, #9ca3af)}:host.bloc-date-picker--disabled{opacity:.5;pointer-events:none}:host.bloc-date-picker--disabled .bloc-date-picker__input{cursor:not-allowed}.bloc-date-picker__dropdown{position:absolute;top:calc(100% + 4px);left:0;z-index:var(--bloc-date-picker-z-index, 100);min-width:280px;border-width:1px;border-style:solid;border-radius:var(--bloc-date-picker-dropdown-radius, 8px);padding:12px;animation:bloc-dp-in .15s ease}:where(.bloc-date-picker__dropdown){background-color:var(--bloc-date-picker-dropdown-bg, #ffffff);border-color:var(--bloc-date-picker-dropdown-border, var(--bloc-border, #d1d5db));box-shadow:var(--bloc-date-picker-dropdown-shadow, 0 4px 16px rgba(0, 0, 0, .1))}.bloc-date-picker__header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.bloc-date-picker__month-year{font-weight:600;font-size:.875rem}:where(.bloc-date-picker__month-year){color:var(--bloc-date-picker-header-color, #374151)}.bloc-date-picker__nav{appearance:none;background:transparent;border:none;cursor:pointer;font-size:1.25rem;line-height:1;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background-color .1s ease}:where(.bloc-date-picker__nav){color:var(--bloc-date-picker-nav-color, #6b7280)}:where(.bloc-date-picker__nav):hover{background-color:var(--bloc-date-picker-nav-hover-bg, #f3f4f6)}.bloc-date-picker__weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:0;margin-bottom:4px}.bloc-date-picker__weekday{font-size:.75rem;font-weight:600;text-align:center;padding:4px 0;-webkit-user-select:none;user-select:none}:where(.bloc-date-picker__weekday){color:var(--bloc-date-picker-weekday-color, #9ca3af)}.bloc-date-picker__days{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}.bloc-date-picker__day{appearance:none;background:transparent;border:none;cursor:pointer;font-family:inherit;font-size:.8125rem;width:32px;height:32px;display:flex;align-items:center;justify-content:center;margin:0 auto;border-radius:var(--bloc-date-picker-day-radius, 6px);transition:background-color .1s ease,color .1s ease}:where(.bloc-date-picker__day){color:var(--bloc-date-picker-day-color, #374151)}:where(.bloc-date-picker__day):hover:not(:disabled){background-color:var(--bloc-date-picker-day-hover-bg, #f3f4f6)}.bloc-date-picker__day--other{opacity:.35}.bloc-date-picker__day--today{font-weight:700;border:1px solid var(--bloc-date-picker-today-border, var(--bloc-primary, #6b7280))}.bloc-date-picker__day--selected{background-color:var(--bloc-date-picker-selected-bg, var(--bloc-primary, #6b7280))!important;color:var(--bloc-date-picker-selected-color, #ffffff)!important;font-weight:600}.bloc-date-picker__day--disabled{cursor:not-allowed;opacity:.3}.bloc-date-picker__footer{display:flex;justify-content:space-between;margin-top:8px;padding-top:8px;border-top:1px solid var(--bloc-date-picker-footer-border, var(--bloc-border, #d1d5db))}.bloc-date-picker__today-btn,.bloc-date-picker__clear-btn{appearance:none;background:transparent;border:none;cursor:pointer;font-family:inherit;font-size:.8125rem;font-weight:500;padding:4px 8px;border-radius:4px;transition:background-color .1s ease}:where(.bloc-date-picker__today-btn){color:var(--bloc-date-picker-action-color, var(--bloc-primary, #6b7280))}:where(.bloc-date-picker__today-btn):hover{background-color:var(--bloc-date-picker-action-hover-bg, #f3f4f6)}:where(.bloc-date-picker__clear-btn){color:var(--bloc-date-picker-clear-color, #9ca3af)}:where(.bloc-date-picker__clear-btn):hover{background-color:var(--bloc-date-picker-action-hover-bg, #f3f4f6);color:var(--bloc-date-picker-clear-hover-color, #374151)}:host.bloc-date-picker--sm .bloc-date-picker__input{padding:var(--bloc-date-picker-padding, 6px 32px 6px 10px);font-size:var(--bloc-date-picker-font-size, .8125rem)}:host.bloc-date-picker--lg .bloc-date-picker__input{padding:var(--bloc-date-picker-padding, 12px 40px 12px 14px);font-size:var(--bloc-date-picker-font-size, 1rem)}@keyframes bloc-dp-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"] });
|
|
244
|
+
}
|
|
245
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BlocDatePickerComponent, decorators: [{
|
|
246
|
+
type: Component,
|
|
247
|
+
args: [{ selector: 'bloc-date-picker', standalone: true, template: `
|
|
248
|
+
<div class="bloc-date-picker__input-wrapper" (click)="toggleCalendar()">
|
|
249
|
+
<input
|
|
250
|
+
class="bloc-date-picker__input"
|
|
251
|
+
type="text"
|
|
252
|
+
readonly
|
|
253
|
+
[value]="displayValue()"
|
|
254
|
+
[placeholder]="placeholder()"
|
|
255
|
+
[disabled]="isDisabled()"
|
|
256
|
+
[attr.aria-expanded]="isOpen()"
|
|
257
|
+
aria-haspopup="dialog" />
|
|
258
|
+
<span class="bloc-date-picker__icon" aria-hidden="true">
|
|
259
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
260
|
+
<rect x="2" y="3" width="12" height="11" rx="1.5" stroke="currentColor" stroke-width="1.3"/>
|
|
261
|
+
<line x1="2" y1="6" x2="14" y2="6" stroke="currentColor" stroke-width="1.3"/>
|
|
262
|
+
<line x1="5" y1="1.5" x2="5" y2="4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
|
263
|
+
<line x1="11" y1="1.5" x2="11" y2="4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
|
264
|
+
</svg>
|
|
265
|
+
</span>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
@if (isOpen()) {
|
|
269
|
+
<div class="bloc-date-picker__dropdown" role="dialog" aria-label="Date picker">
|
|
270
|
+
<div class="bloc-date-picker__header">
|
|
271
|
+
<button type="button" class="bloc-date-picker__nav" (click)="prevMonth()" aria-label="Previous month">
|
|
272
|
+
‹
|
|
273
|
+
</button>
|
|
274
|
+
<span class="bloc-date-picker__month-year">
|
|
275
|
+
{{ monthYearLabel() }}
|
|
276
|
+
</span>
|
|
277
|
+
<button type="button" class="bloc-date-picker__nav" (click)="nextMonth()" aria-label="Next month">
|
|
278
|
+
›
|
|
279
|
+
</button>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<div class="bloc-date-picker__weekdays">
|
|
283
|
+
@for (day of weekdays; track day) {
|
|
284
|
+
<span class="bloc-date-picker__weekday">{{ day }}</span>
|
|
285
|
+
}
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
<div class="bloc-date-picker__days">
|
|
289
|
+
@for (day of calendarDays(); track day.date.getTime()) {
|
|
290
|
+
<button
|
|
291
|
+
type="button"
|
|
292
|
+
class="bloc-date-picker__day"
|
|
293
|
+
[class.bloc-date-picker__day--other]="!day.isCurrentMonth"
|
|
294
|
+
[class.bloc-date-picker__day--today]="day.isToday"
|
|
295
|
+
[class.bloc-date-picker__day--selected]="day.isSelected"
|
|
296
|
+
[class.bloc-date-picker__day--disabled]="day.isDisabled"
|
|
297
|
+
[disabled]="day.isDisabled"
|
|
298
|
+
(click)="selectDay(day)">
|
|
299
|
+
{{ day.day }}
|
|
300
|
+
</button>
|
|
301
|
+
}
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
<div class="bloc-date-picker__footer">
|
|
305
|
+
<button type="button" class="bloc-date-picker__today-btn" (click)="goToToday()">
|
|
306
|
+
Today
|
|
307
|
+
</button>
|
|
308
|
+
@if (_selectedDate()) {
|
|
309
|
+
<button type="button" class="bloc-date-picker__clear-btn" (click)="clear()">
|
|
310
|
+
Clear
|
|
311
|
+
</button>
|
|
312
|
+
}
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
}
|
|
316
|
+
`, providers: [
|
|
317
|
+
{
|
|
318
|
+
provide: NG_VALUE_ACCESSOR,
|
|
319
|
+
useExisting: forwardRef(() => BlocDatePickerComponent),
|
|
320
|
+
multi: true,
|
|
321
|
+
},
|
|
322
|
+
], host: {
|
|
323
|
+
'[class.bloc-date-picker]': 'true',
|
|
324
|
+
'[class.bloc-date-picker--sm]': 'size() === "sm"',
|
|
325
|
+
'[class.bloc-date-picker--lg]': 'size() === "lg"',
|
|
326
|
+
'[class.bloc-date-picker--disabled]': 'isDisabled()',
|
|
327
|
+
'[class.bloc-date-picker--open]': 'isOpen()',
|
|
328
|
+
}, styles: [":host{display:inline-block;position:relative;font-family:inherit}.bloc-date-picker__input-wrapper{display:flex;align-items:center;cursor:pointer;position:relative}.bloc-date-picker__input{appearance:none;border-width:1px;border-style:solid;font-family:inherit;cursor:pointer;outline:none;width:100%;box-sizing:border-box;padding:var(--bloc-date-picker-padding, 8px 36px 8px 12px);font-size:var(--bloc-date-picker-font-size, .875rem);border-radius:var(--bloc-date-picker-radius, 6px);transition:border-color .15s ease}:where(.bloc-date-picker__input){color:var(--bloc-date-picker-color, #374151);background-color:var(--bloc-date-picker-bg, #ffffff);border-color:var(--bloc-date-picker-border, var(--bloc-border, #d1d5db))}:where(.bloc-date-picker__input:focus){border-color:var(--bloc-date-picker-focus-border, var(--bloc-primary, #6b7280));box-shadow:0 0 0 2px var(--bloc-date-picker-focus-ring, rgba(107, 114, 128, .2))}:where(.bloc-date-picker__input::placeholder){color:var(--bloc-date-picker-placeholder, #9ca3af)}.bloc-date-picker__icon{position:absolute;right:10px;top:50%;transform:translateY(-50%);pointer-events:none;display:flex;align-items:center}:where(.bloc-date-picker__icon){color:var(--bloc-date-picker-icon-color, #9ca3af)}:host.bloc-date-picker--disabled{opacity:.5;pointer-events:none}:host.bloc-date-picker--disabled .bloc-date-picker__input{cursor:not-allowed}.bloc-date-picker__dropdown{position:absolute;top:calc(100% + 4px);left:0;z-index:var(--bloc-date-picker-z-index, 100);min-width:280px;border-width:1px;border-style:solid;border-radius:var(--bloc-date-picker-dropdown-radius, 8px);padding:12px;animation:bloc-dp-in .15s ease}:where(.bloc-date-picker__dropdown){background-color:var(--bloc-date-picker-dropdown-bg, #ffffff);border-color:var(--bloc-date-picker-dropdown-border, var(--bloc-border, #d1d5db));box-shadow:var(--bloc-date-picker-dropdown-shadow, 0 4px 16px rgba(0, 0, 0, .1))}.bloc-date-picker__header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.bloc-date-picker__month-year{font-weight:600;font-size:.875rem}:where(.bloc-date-picker__month-year){color:var(--bloc-date-picker-header-color, #374151)}.bloc-date-picker__nav{appearance:none;background:transparent;border:none;cursor:pointer;font-size:1.25rem;line-height:1;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background-color .1s ease}:where(.bloc-date-picker__nav){color:var(--bloc-date-picker-nav-color, #6b7280)}:where(.bloc-date-picker__nav):hover{background-color:var(--bloc-date-picker-nav-hover-bg, #f3f4f6)}.bloc-date-picker__weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:0;margin-bottom:4px}.bloc-date-picker__weekday{font-size:.75rem;font-weight:600;text-align:center;padding:4px 0;-webkit-user-select:none;user-select:none}:where(.bloc-date-picker__weekday){color:var(--bloc-date-picker-weekday-color, #9ca3af)}.bloc-date-picker__days{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}.bloc-date-picker__day{appearance:none;background:transparent;border:none;cursor:pointer;font-family:inherit;font-size:.8125rem;width:32px;height:32px;display:flex;align-items:center;justify-content:center;margin:0 auto;border-radius:var(--bloc-date-picker-day-radius, 6px);transition:background-color .1s ease,color .1s ease}:where(.bloc-date-picker__day){color:var(--bloc-date-picker-day-color, #374151)}:where(.bloc-date-picker__day):hover:not(:disabled){background-color:var(--bloc-date-picker-day-hover-bg, #f3f4f6)}.bloc-date-picker__day--other{opacity:.35}.bloc-date-picker__day--today{font-weight:700;border:1px solid var(--bloc-date-picker-today-border, var(--bloc-primary, #6b7280))}.bloc-date-picker__day--selected{background-color:var(--bloc-date-picker-selected-bg, var(--bloc-primary, #6b7280))!important;color:var(--bloc-date-picker-selected-color, #ffffff)!important;font-weight:600}.bloc-date-picker__day--disabled{cursor:not-allowed;opacity:.3}.bloc-date-picker__footer{display:flex;justify-content:space-between;margin-top:8px;padding-top:8px;border-top:1px solid var(--bloc-date-picker-footer-border, var(--bloc-border, #d1d5db))}.bloc-date-picker__today-btn,.bloc-date-picker__clear-btn{appearance:none;background:transparent;border:none;cursor:pointer;font-family:inherit;font-size:.8125rem;font-weight:500;padding:4px 8px;border-radius:4px;transition:background-color .1s ease}:where(.bloc-date-picker__today-btn){color:var(--bloc-date-picker-action-color, var(--bloc-primary, #6b7280))}:where(.bloc-date-picker__today-btn):hover{background-color:var(--bloc-date-picker-action-hover-bg, #f3f4f6)}:where(.bloc-date-picker__clear-btn){color:var(--bloc-date-picker-clear-color, #9ca3af)}:where(.bloc-date-picker__clear-btn):hover{background-color:var(--bloc-date-picker-action-hover-bg, #f3f4f6);color:var(--bloc-date-picker-clear-hover-color, #374151)}:host.bloc-date-picker--sm .bloc-date-picker__input{padding:var(--bloc-date-picker-padding, 6px 32px 6px 10px);font-size:var(--bloc-date-picker-font-size, .8125rem)}:host.bloc-date-picker--lg .bloc-date-picker__input{padding:var(--bloc-date-picker-padding, 12px 40px 12px 14px);font-size:var(--bloc-date-picker-font-size, 1rem)}@keyframes bloc-dp-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
|
|
329
|
+
}], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], _onDocClick: [{
|
|
330
|
+
type: HostListener,
|
|
331
|
+
args: ['document:click', ['$event']]
|
|
332
|
+
}], _onEsc: [{
|
|
333
|
+
type: HostListener,
|
|
334
|
+
args: ['keydown.escape']
|
|
335
|
+
}] } });
|
|
336
|
+
|
|
337
|
+
class BlocDatePickerModule {
|
|
338
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BlocDatePickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
339
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.5", ngImport: i0, type: BlocDatePickerModule, imports: [BlocDatePickerComponent], exports: [BlocDatePickerComponent] });
|
|
340
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BlocDatePickerModule });
|
|
341
|
+
}
|
|
342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BlocDatePickerModule, decorators: [{
|
|
343
|
+
type: NgModule,
|
|
344
|
+
args: [{
|
|
345
|
+
imports: [BlocDatePickerComponent],
|
|
346
|
+
exports: [BlocDatePickerComponent],
|
|
347
|
+
}]
|
|
348
|
+
}] });
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Generated bundle index. Do not edit.
|
|
352
|
+
*/
|
|
353
|
+
|
|
354
|
+
export { BlocDatePickerComponent, BlocDatePickerModule };
|
|
355
|
+
//# sourceMappingURL=bloc-ui-date-picker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bloc-ui-date-picker.mjs","sources":["../../../projects/bloc-ui-date-picker/src/date-picker.component.ts","../../../projects/bloc-ui-date-picker/src/date-picker.module.ts","../../../projects/bloc-ui-date-picker/src/bloc-ui-date-picker.ts"],"sourcesContent":["import {\r\n Component,\r\n computed,\r\n ElementRef,\r\n forwardRef,\r\n HostListener,\r\n inject,\r\n input,\r\n signal,\r\n} from '@angular/core';\r\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\r\n\r\nconst DAYS_OF_WEEK = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];\r\n\r\ninterface CalendarDay {\r\n date: Date;\r\n day: number;\r\n isCurrentMonth: boolean;\r\n isToday: boolean;\r\n isSelected: boolean;\r\n isDisabled: boolean;\r\n}\r\n\r\n/**\r\n * Date picker component with calendar dropdown.\r\n *\r\n * ```html\r\n * <bloc-date-picker [(ngModel)]=\"selectedDate\" />\r\n * <bloc-date-picker [formControl]=\"dateCtrl\" placeholder=\"Choose date\" />\r\n * ```\r\n */\r\n@Component({\r\n selector: 'bloc-date-picker',\r\n standalone: true,\r\n template: `\r\n <div class=\"bloc-date-picker__input-wrapper\" (click)=\"toggleCalendar()\">\r\n <input\r\n class=\"bloc-date-picker__input\"\r\n type=\"text\"\r\n readonly\r\n [value]=\"displayValue()\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"isDisabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-haspopup=\"dialog\" />\r\n <span class=\"bloc-date-picker__icon\" aria-hidden=\"true\">\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <rect x=\"2\" y=\"3\" width=\"12\" height=\"11\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\r\n <line x1=\"2\" y1=\"6\" x2=\"14\" y2=\"6\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\r\n <line x1=\"5\" y1=\"1.5\" x2=\"5\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\r\n <line x1=\"11\" y1=\"1.5\" x2=\"11\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\r\n </svg>\r\n </span>\r\n </div>\r\n\r\n @if (isOpen()) {\r\n <div class=\"bloc-date-picker__dropdown\" role=\"dialog\" aria-label=\"Date picker\">\r\n <div class=\"bloc-date-picker__header\">\r\n <button type=\"button\" class=\"bloc-date-picker__nav\" (click)=\"prevMonth()\" aria-label=\"Previous month\">\r\n ‹\r\n </button>\r\n <span class=\"bloc-date-picker__month-year\">\r\n {{ monthYearLabel() }}\r\n </span>\r\n <button type=\"button\" class=\"bloc-date-picker__nav\" (click)=\"nextMonth()\" aria-label=\"Next month\">\r\n ›\r\n </button>\r\n </div>\r\n\r\n <div class=\"bloc-date-picker__weekdays\">\r\n @for (day of weekdays; track day) {\r\n <span class=\"bloc-date-picker__weekday\">{{ day }}</span>\r\n }\r\n </div>\r\n\r\n <div class=\"bloc-date-picker__days\">\r\n @for (day of calendarDays(); track day.date.getTime()) {\r\n <button\r\n type=\"button\"\r\n class=\"bloc-date-picker__day\"\r\n [class.bloc-date-picker__day--other]=\"!day.isCurrentMonth\"\r\n [class.bloc-date-picker__day--today]=\"day.isToday\"\r\n [class.bloc-date-picker__day--selected]=\"day.isSelected\"\r\n [class.bloc-date-picker__day--disabled]=\"day.isDisabled\"\r\n [disabled]=\"day.isDisabled\"\r\n (click)=\"selectDay(day)\">\r\n {{ day.day }}\r\n </button>\r\n }\r\n </div>\r\n\r\n <div class=\"bloc-date-picker__footer\">\r\n <button type=\"button\" class=\"bloc-date-picker__today-btn\" (click)=\"goToToday()\">\r\n Today\r\n </button>\r\n @if (_selectedDate()) {\r\n <button type=\"button\" class=\"bloc-date-picker__clear-btn\" (click)=\"clear()\">\r\n Clear\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n `,\r\n styleUrl: './date-picker.component.scss',\r\n providers: [\r\n {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => BlocDatePickerComponent),\r\n multi: true,\r\n },\r\n ],\r\n host: {\r\n '[class.bloc-date-picker]': 'true',\r\n '[class.bloc-date-picker--sm]': 'size() === \"sm\"',\r\n '[class.bloc-date-picker--lg]': 'size() === \"lg\"',\r\n '[class.bloc-date-picker--disabled]': 'isDisabled()',\r\n '[class.bloc-date-picker--open]': 'isOpen()',\r\n },\r\n})\r\nexport class BlocDatePickerComponent implements ControlValueAccessor {\r\n private readonly _el = inject(ElementRef<HTMLElement>);\r\n\r\n /** Preset size. Defaults to `'md'`. */\r\n readonly size = input<'sm' | 'md' | 'lg'>('md');\r\n\r\n /** Placeholder text for the input. */\r\n readonly placeholder = input<string>('Select date');\r\n\r\n /** Date format for display. Defaults to `'yyyy-MM-dd'`. */\r\n readonly format = input<string>('yyyy-MM-dd');\r\n\r\n /** Disable the date picker via template binding. */\r\n readonly disabled = input<boolean>(false);\r\n\r\n /** Minimum selectable date. */\r\n readonly minDate = input<Date | null>(null);\r\n\r\n /** Maximum selectable date. */\r\n readonly maxDate = input<Date | null>(null);\r\n\r\n readonly weekdays = DAYS_OF_WEEK;\r\n\r\n /** Currently selected date. */\r\n readonly _selectedDate = signal<Date | null>(null);\r\n\r\n /** Whether the calendar dropdown is open. */\r\n readonly isOpen = signal<boolean>(false);\r\n\r\n /** The month being viewed (year + month). */\r\n readonly _viewDate = signal<Date>(new Date());\r\n\r\n private readonly _formDisabled = signal<boolean>(false);\r\n readonly isDisabled = computed(() => this.disabled() || this._formDisabled());\r\n\r\n private _onChange: (val: Date | null) => void = () => { };\r\n _onTouched: () => void = () => { };\r\n\r\n readonly displayValue = computed(() => {\r\n const d = this._selectedDate();\r\n if (!d) return '';\r\n return this._formatDate(d);\r\n });\r\n\r\n readonly monthYearLabel = computed(() => {\r\n const d = this._viewDate();\r\n return d.toLocaleString('default', { month: 'long', year: 'numeric' });\r\n });\r\n\r\n readonly calendarDays = computed<CalendarDay[]>(() => {\r\n const viewDate = this._viewDate();\r\n const selected = this._selectedDate();\r\n const today = new Date();\r\n const min = this.minDate();\r\n const max = this.maxDate();\r\n\r\n const year = viewDate.getFullYear();\r\n const month = viewDate.getMonth();\r\n\r\n const firstOfMonth = new Date(year, month, 1);\r\n const startDay = firstOfMonth.getDay();\r\n const daysInMonth = new Date(year, month + 1, 0).getDate();\r\n\r\n // Fill from previous month\r\n const prevMonthDays = new Date(year, month, 0).getDate();\r\n const days: CalendarDay[] = [];\r\n\r\n for (let i = startDay - 1; i >= 0; i--) {\r\n const date = new Date(year, month - 1, prevMonthDays - i);\r\n days.push(this._buildDay(date, false, today, selected, min, max));\r\n }\r\n\r\n for (let d = 1; d <= daysInMonth; d++) {\r\n const date = new Date(year, month, d);\r\n days.push(this._buildDay(date, true, today, selected, min, max));\r\n }\r\n\r\n // Fill remaining to complete 6 rows (42 cells)\r\n const remaining = 42 - days.length;\r\n for (let d = 1; d <= remaining; d++) {\r\n const date = new Date(year, month + 1, d);\r\n days.push(this._buildDay(date, false, today, selected, min, max));\r\n }\r\n\r\n return days;\r\n });\r\n\r\n toggleCalendar(): void {\r\n if (this.isDisabled()) return;\r\n this.isOpen.update((v) => !v);\r\n if (this.isOpen()) {\r\n const selected = this._selectedDate();\r\n if (selected) {\r\n this._viewDate.set(new Date(selected.getFullYear(), selected.getMonth(), 1));\r\n }\r\n }\r\n }\r\n\r\n selectDay(day: CalendarDay): void {\r\n if (day.isDisabled) return;\r\n this._selectedDate.set(day.date);\r\n this._onChange(day.date);\r\n this.isOpen.set(false);\r\n }\r\n\r\n prevMonth(): void {\r\n const d = this._viewDate();\r\n this._viewDate.set(new Date(d.getFullYear(), d.getMonth() - 1, 1));\r\n }\r\n\r\n nextMonth(): void {\r\n const d = this._viewDate();\r\n this._viewDate.set(new Date(d.getFullYear(), d.getMonth() + 1, 1));\r\n }\r\n\r\n goToToday(): void {\r\n const today = new Date();\r\n this._selectedDate.set(today);\r\n this._viewDate.set(new Date(today.getFullYear(), today.getMonth(), 1));\r\n this._onChange(today);\r\n this.isOpen.set(false);\r\n }\r\n\r\n clear(): void {\r\n this._selectedDate.set(null);\r\n this._onChange(null);\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n _onDocClick(event: MouseEvent): void {\r\n if (!this._el.nativeElement.contains(event.target as Node)) {\r\n this.isOpen.set(false);\r\n }\r\n }\r\n\r\n @HostListener('keydown.escape')\r\n _onEsc(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n // — ControlValueAccessor —\r\n\r\n writeValue(val: unknown): void {\r\n this._selectedDate.set(val instanceof Date ? val : val ? new Date(val as string) : null);\r\n }\r\n\r\n registerOnChange(fn: (val: Date | null) => void): void {\r\n this._onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: () => void): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n setDisabledState(isDisabled: boolean): void {\r\n this._formDisabled.set(isDisabled);\r\n }\r\n\r\n // — Private helpers —\r\n\r\n private _buildDay(\r\n date: Date,\r\n isCurrentMonth: boolean,\r\n today: Date,\r\n selected: Date | null,\r\n min: Date | null,\r\n max: Date | null,\r\n ): CalendarDay {\r\n const isToday =\r\n date.getDate() === today.getDate() &&\r\n date.getMonth() === today.getMonth() &&\r\n date.getFullYear() === today.getFullYear();\r\n const isSelected = selected\r\n ? date.getDate() === selected.getDate() &&\r\n date.getMonth() === selected.getMonth() &&\r\n date.getFullYear() === selected.getFullYear()\r\n : false;\r\n\r\n let isDisabled = false;\r\n if (min) {\r\n const minDay = new Date(min.getFullYear(), min.getMonth(), min.getDate());\r\n isDisabled = date < minDay;\r\n }\r\n if (max && !isDisabled) {\r\n const maxDay = new Date(max.getFullYear(), max.getMonth(), max.getDate());\r\n isDisabled = date > maxDay;\r\n }\r\n\r\n return { date, day: date.getDate(), isCurrentMonth, isToday, isSelected, isDisabled };\r\n }\r\n\r\n private _formatDate(d: Date): string {\r\n const yyyy = d.getFullYear();\r\n const mm = String(d.getMonth() + 1).padStart(2, '0');\r\n const dd = String(d.getDate()).padStart(2, '0');\r\n\r\n return this.format()\r\n .replace('yyyy', String(yyyy))\r\n .replace('MM', mm)\r\n .replace('dd', dd);\r\n }\r\n}\r\n","import { NgModule } from '@angular/core';\r\nimport { BlocDatePickerComponent } from './date-picker.component';\r\n\r\n@NgModule({\r\n imports: [BlocDatePickerComponent],\r\n exports: [BlocDatePickerComponent],\r\n})\r\nexport class BlocDatePickerModule { }\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bloc-ui/date-picker",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/debasish1996/BLOC-UI.git",
|
|
7
|
+
"directory": "projects/bloc-ui-date-picker"
|
|
8
|
+
},
|
|
9
|
+
"peerDependencies": {
|
|
10
|
+
"@angular/common": "^21.2.0",
|
|
11
|
+
"@angular/core": "^21.2.0",
|
|
12
|
+
"@angular/forms": "^21.2.0"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"tslib": "^2.3.0"
|
|
16
|
+
},
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"module": "fesm2022/bloc-ui-date-picker.mjs",
|
|
19
|
+
"typings": "types/bloc-ui-date-picker.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
"./package.json": {
|
|
22
|
+
"default": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./types/bloc-ui-date-picker.d.ts",
|
|
26
|
+
"default": "./fesm2022/bloc-ui-date-picker.mjs"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { ControlValueAccessor } from '@angular/forms';
|
|
3
|
+
|
|
4
|
+
interface CalendarDay {
|
|
5
|
+
date: Date;
|
|
6
|
+
day: number;
|
|
7
|
+
isCurrentMonth: boolean;
|
|
8
|
+
isToday: boolean;
|
|
9
|
+
isSelected: boolean;
|
|
10
|
+
isDisabled: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Date picker component with calendar dropdown.
|
|
14
|
+
*
|
|
15
|
+
* ```html
|
|
16
|
+
* <bloc-date-picker [(ngModel)]="selectedDate" />
|
|
17
|
+
* <bloc-date-picker [formControl]="dateCtrl" placeholder="Choose date" />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare class BlocDatePickerComponent implements ControlValueAccessor {
|
|
21
|
+
private readonly _el;
|
|
22
|
+
/** Preset size. Defaults to `'md'`. */
|
|
23
|
+
readonly size: _angular_core.InputSignal<"sm" | "md" | "lg">;
|
|
24
|
+
/** Placeholder text for the input. */
|
|
25
|
+
readonly placeholder: _angular_core.InputSignal<string>;
|
|
26
|
+
/** Date format for display. Defaults to `'yyyy-MM-dd'`. */
|
|
27
|
+
readonly format: _angular_core.InputSignal<string>;
|
|
28
|
+
/** Disable the date picker via template binding. */
|
|
29
|
+
readonly disabled: _angular_core.InputSignal<boolean>;
|
|
30
|
+
/** Minimum selectable date. */
|
|
31
|
+
readonly minDate: _angular_core.InputSignal<Date | null>;
|
|
32
|
+
/** Maximum selectable date. */
|
|
33
|
+
readonly maxDate: _angular_core.InputSignal<Date | null>;
|
|
34
|
+
readonly weekdays: string[];
|
|
35
|
+
/** Currently selected date. */
|
|
36
|
+
readonly _selectedDate: _angular_core.WritableSignal<Date | null>;
|
|
37
|
+
/** Whether the calendar dropdown is open. */
|
|
38
|
+
readonly isOpen: _angular_core.WritableSignal<boolean>;
|
|
39
|
+
/** The month being viewed (year + month). */
|
|
40
|
+
readonly _viewDate: _angular_core.WritableSignal<Date>;
|
|
41
|
+
private readonly _formDisabled;
|
|
42
|
+
readonly isDisabled: _angular_core.Signal<boolean>;
|
|
43
|
+
private _onChange;
|
|
44
|
+
_onTouched: () => void;
|
|
45
|
+
readonly displayValue: _angular_core.Signal<string>;
|
|
46
|
+
readonly monthYearLabel: _angular_core.Signal<string>;
|
|
47
|
+
readonly calendarDays: _angular_core.Signal<CalendarDay[]>;
|
|
48
|
+
toggleCalendar(): void;
|
|
49
|
+
selectDay(day: CalendarDay): void;
|
|
50
|
+
prevMonth(): void;
|
|
51
|
+
nextMonth(): void;
|
|
52
|
+
goToToday(): void;
|
|
53
|
+
clear(): void;
|
|
54
|
+
_onDocClick(event: MouseEvent): void;
|
|
55
|
+
_onEsc(): void;
|
|
56
|
+
writeValue(val: unknown): void;
|
|
57
|
+
registerOnChange(fn: (val: Date | null) => void): void;
|
|
58
|
+
registerOnTouched(fn: () => void): void;
|
|
59
|
+
setDisabledState(isDisabled: boolean): void;
|
|
60
|
+
private _buildDay;
|
|
61
|
+
private _formatDate;
|
|
62
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<BlocDatePickerComponent, never>;
|
|
63
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<BlocDatePickerComponent, "bloc-date-picker", never, { "size": { "alias": "size"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "minDate": { "alias": "minDate"; "required": false; "isSignal": true; }; "maxDate": { "alias": "maxDate"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
declare class BlocDatePickerModule {
|
|
67
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<BlocDatePickerModule, never>;
|
|
68
|
+
static ɵmod: _angular_core.ɵɵNgModuleDeclaration<BlocDatePickerModule, never, [typeof BlocDatePickerComponent], [typeof BlocDatePickerComponent]>;
|
|
69
|
+
static ɵinj: _angular_core.ɵɵInjectorDeclaration<BlocDatePickerModule>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { BlocDatePickerComponent, BlocDatePickerModule };
|