mtrl 0.2.5 → 0.2.7
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/index.ts +18 -0
- package/package.json +1 -1
- package/src/components/badge/_styles.scss +123 -115
- package/src/components/badge/api.ts +57 -59
- package/src/components/badge/badge.ts +16 -2
- package/src/components/badge/config.ts +65 -11
- package/src/components/badge/constants.ts +22 -12
- package/src/components/badge/features.ts +44 -40
- package/src/components/badge/types.ts +42 -30
- package/src/components/bottom-app-bar/_styles.scss +103 -0
- package/src/components/bottom-app-bar/bottom-app-bar.ts +196 -0
- package/src/components/bottom-app-bar/config.ts +73 -0
- package/src/components/bottom-app-bar/index.ts +11 -0
- package/src/components/bottom-app-bar/types.ts +108 -0
- package/src/components/button/_styles.scss +0 -66
- package/src/components/button/api.ts +5 -0
- package/src/components/button/button.ts +0 -2
- package/src/components/button/config.ts +5 -0
- package/src/components/button/constants.ts +0 -6
- package/src/components/button/index.ts +2 -2
- package/src/components/button/types.ts +7 -7
- package/src/components/card/_styles.scss +67 -25
- package/src/components/card/api.ts +54 -3
- package/src/components/card/card.ts +25 -6
- package/src/components/card/config.ts +189 -22
- package/src/components/card/constants.ts +20 -19
- package/src/components/card/content.ts +299 -2
- package/src/components/card/features.ts +158 -4
- package/src/components/card/index.ts +31 -9
- package/src/components/card/types.ts +166 -15
- package/src/components/checkbox/_styles.scss +0 -2
- package/src/components/chip/chip.ts +1 -9
- package/src/components/chip/constants.ts +0 -10
- package/src/components/chip/index.ts +1 -1
- package/src/components/chip/types.ts +1 -4
- package/src/components/datepicker/_styles.scss +358 -0
- package/src/components/datepicker/api.ts +272 -0
- package/src/components/datepicker/config.ts +144 -0
- package/src/components/datepicker/constants.ts +98 -0
- package/src/components/datepicker/datepicker.ts +346 -0
- package/src/components/datepicker/index.ts +9 -0
- package/src/components/datepicker/render.ts +452 -0
- package/src/components/datepicker/types.ts +268 -0
- package/src/components/datepicker/utils.ts +290 -0
- package/src/components/dialog/_styles.scss +174 -128
- package/src/components/dialog/api.ts +48 -13
- package/src/components/dialog/config.ts +9 -5
- package/src/components/dialog/dialog.ts +6 -3
- package/src/components/dialog/features.ts +290 -130
- package/src/components/dialog/types.ts +7 -4
- package/src/components/divider/_styles.scss +57 -0
- package/src/components/divider/config.ts +81 -0
- package/src/components/divider/divider.ts +37 -0
- package/src/components/divider/features.ts +207 -0
- package/src/components/divider/index.ts +5 -0
- package/src/components/divider/types.ts +55 -0
- package/src/components/extended-fab/_styles.scss +267 -0
- package/src/components/extended-fab/api.ts +141 -0
- package/src/components/extended-fab/config.ts +108 -0
- package/src/components/extended-fab/constants.ts +36 -0
- package/src/components/extended-fab/extended-fab.ts +125 -0
- package/src/components/extended-fab/index.ts +4 -0
- package/src/components/extended-fab/types.ts +287 -0
- package/src/components/fab/_styles.scss +225 -0
- package/src/components/fab/api.ts +97 -0
- package/src/components/fab/config.ts +94 -0
- package/src/components/fab/constants.ts +41 -0
- package/src/components/fab/fab.ts +67 -0
- package/src/components/fab/index.ts +4 -0
- package/src/components/fab/types.ts +234 -0
- package/src/components/navigation/_styles.scss +1 -0
- package/src/components/navigation/api.ts +78 -50
- package/src/components/navigation/features/items.ts +280 -0
- package/src/components/navigation/nav-item.ts +72 -23
- package/src/components/navigation/navigation.ts +54 -2
- package/src/components/navigation/types.ts +210 -188
- package/src/components/progress/_styles.scss +0 -65
- package/src/components/progress/config.ts +1 -2
- package/src/components/progress/constants.ts +0 -14
- package/src/components/progress/index.ts +1 -1
- package/src/components/progress/progress.ts +1 -4
- package/src/components/progress/types.ts +1 -4
- package/src/components/radios/_styles.scss +0 -45
- package/src/components/radios/api.ts +85 -60
- package/src/components/radios/config.ts +1 -2
- package/src/components/radios/constants.ts +0 -9
- package/src/components/radios/index.ts +1 -1
- package/src/components/radios/radio.ts +34 -11
- package/src/components/radios/radios.ts +2 -1
- package/src/components/radios/types.ts +1 -7
- package/src/components/search/_styles.scss +306 -0
- package/src/components/search/api.ts +203 -0
- package/src/components/search/config.ts +87 -0
- package/src/components/search/constants.ts +21 -0
- package/src/components/search/features/index.ts +4 -0
- package/src/components/search/features/search.ts +718 -0
- package/src/components/search/features/states.ts +165 -0
- package/src/components/search/features/structure.ts +198 -0
- package/src/components/search/index.ts +10 -0
- package/src/components/search/search.ts +52 -0
- package/src/components/search/types.ts +163 -0
- package/src/components/segmented-button/_styles.scss +117 -0
- package/src/components/segmented-button/config.ts +67 -0
- package/src/components/segmented-button/constants.ts +42 -0
- package/src/components/segmented-button/index.ts +4 -0
- package/src/components/segmented-button/segment.ts +155 -0
- package/src/components/segmented-button/segmented-button.ts +250 -0
- package/src/components/segmented-button/types.ts +219 -0
- package/src/components/slider/_styles.scss +221 -168
- package/src/components/slider/accessibility.md +59 -0
- package/src/components/slider/api.ts +41 -120
- package/src/components/slider/config.ts +51 -49
- package/src/components/slider/features/handlers.ts +495 -0
- package/src/components/slider/features/index.ts +1 -2
- package/src/components/slider/features/slider.ts +66 -84
- package/src/components/slider/features/states.ts +195 -0
- package/src/components/slider/features/structure.ts +141 -184
- package/src/components/slider/features/ui.ts +150 -201
- package/src/components/slider/index.ts +2 -11
- package/src/components/slider/slider.ts +9 -12
- package/src/components/slider/types.ts +39 -24
- package/src/components/switch/_styles.scss +0 -2
- package/src/components/tabs/_styles.scss +346 -154
- package/src/components/tabs/api.ts +178 -400
- package/src/components/tabs/config.ts +46 -52
- package/src/components/tabs/constants.ts +85 -8
- package/src/components/tabs/features.ts +403 -0
- package/src/components/tabs/index.ts +60 -3
- package/src/components/tabs/indicator.ts +285 -0
- package/src/components/tabs/responsive.ts +144 -0
- package/src/components/tabs/scroll-indicators.ts +149 -0
- package/src/components/tabs/state.ts +186 -0
- package/src/components/tabs/tab-api.ts +258 -0
- package/src/components/tabs/tab.ts +255 -0
- package/src/components/tabs/tabs.ts +50 -31
- package/src/components/tabs/types.ts +332 -128
- package/src/components/tabs/utils.ts +107 -0
- package/src/components/textfield/_styles.scss +0 -98
- package/src/components/textfield/config.ts +2 -3
- package/src/components/textfield/constants.ts +0 -14
- package/src/components/textfield/index.ts +2 -2
- package/src/components/textfield/textfield.ts +0 -2
- package/src/components/textfield/types.ts +1 -4
- package/src/components/timepicker/README.md +277 -0
- package/src/components/timepicker/_styles.scss +451 -0
- package/src/components/timepicker/api.ts +632 -0
- package/src/components/timepicker/clockdial.ts +482 -0
- package/src/components/timepicker/config.ts +130 -0
- package/src/components/timepicker/constants.ts +138 -0
- package/src/components/timepicker/index.ts +8 -0
- package/src/components/timepicker/render.ts +613 -0
- package/src/components/timepicker/timepicker.ts +117 -0
- package/src/components/timepicker/types.ts +336 -0
- package/src/components/timepicker/utils.ts +241 -0
- package/src/components/top-app-bar/_styles.scss +225 -0
- package/src/components/top-app-bar/config.ts +83 -0
- package/src/components/top-app-bar/index.ts +11 -0
- package/src/components/top-app-bar/top-app-bar.ts +316 -0
- package/src/components/top-app-bar/types.ts +140 -0
- package/src/core/build/_ripple.scss +6 -6
- package/src/core/build/ripple.ts +72 -95
- package/src/core/compose/component.ts +1 -1
- package/src/core/compose/features/badge.ts +79 -0
- package/src/core/compose/features/icon.ts +3 -1
- package/src/core/compose/features/index.ts +3 -1
- package/src/core/compose/features/ripple.ts +4 -1
- package/src/core/compose/features/textlabel.ts +26 -2
- package/src/core/dom/create.ts +5 -0
- package/src/index.ts +9 -0
- package/src/styles/abstract/_theme.scss +115 -3
- package/src/styles/themes/_autumn.scss +21 -0
- package/src/styles/themes/_base-theme.scss +61 -0
- package/src/styles/themes/_baseline.scss +58 -0
- package/src/styles/themes/_bluekhaki.scss +125 -0
- package/src/styles/themes/_brownbeige.scss +125 -0
- package/src/styles/themes/_browngreen.scss +125 -0
- package/src/styles/themes/_forest.scss +6 -0
- package/src/styles/themes/_greenbeige.scss +125 -0
- package/src/styles/themes/_material.scss +125 -0
- package/src/styles/themes/_ocean.scss +6 -0
- package/src/styles/themes/_sageivory.scss +125 -0
- package/src/styles/themes/_spring.scss +6 -0
- package/src/styles/themes/_summer.scss +5 -0
- package/src/styles/themes/_sunset.scss +5 -0
- package/src/styles/themes/_tealcaramel.scss +125 -0
- package/src/styles/themes/_winter.scss +6 -0
- package/src/components/card/actions.ts +0 -48
- package/src/components/card/header.ts +0 -88
- package/src/components/card/media.ts +0 -52
- package/src/components/navigation/features/items.js +0 -192
- package/src/components/slider/features/appearance.ts +0 -94
- package/src/components/slider/features/disabled.ts +0 -43
- package/src/components/slider/features/events.ts +0 -164
- package/src/components/slider/features/interactions.ts +0 -261
- package/src/components/slider/features/keyboard.ts +0 -112
- package/src/core/collection/adapters/mongodb.js +0 -232
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
// src/components/datepicker/render.ts
|
|
2
|
+
import {
|
|
3
|
+
MONTH_NAMES,
|
|
4
|
+
MONTH_NAMES_SHORT,
|
|
5
|
+
DAY_NAMES,
|
|
6
|
+
TODAY_CLASS,
|
|
7
|
+
SELECTED_CLASS,
|
|
8
|
+
OUTSIDE_MONTH_CLASS,
|
|
9
|
+
RANGE_START_CLASS,
|
|
10
|
+
RANGE_END_CLASS,
|
|
11
|
+
RANGE_MIDDLE_CLASS,
|
|
12
|
+
DATEPICKER_VIEWS,
|
|
13
|
+
DATEPICKER_VARIANTS
|
|
14
|
+
} from './constants';
|
|
15
|
+
import { CalendarDate } from './types';
|
|
16
|
+
import {
|
|
17
|
+
generateCalendarDates,
|
|
18
|
+
generateYearRange
|
|
19
|
+
} from './utils';
|
|
20
|
+
import { createElement } from '../../core/dom/create';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Renders the calendar header with navigation controls
|
|
24
|
+
* @param {Object} params - Rendering parameters
|
|
25
|
+
* @returns {HTMLElement} Calendar header element
|
|
26
|
+
*/
|
|
27
|
+
export const renderHeader = ({
|
|
28
|
+
currentMonth,
|
|
29
|
+
currentYear,
|
|
30
|
+
currentView,
|
|
31
|
+
prefix,
|
|
32
|
+
emit
|
|
33
|
+
}: any): HTMLElement => {
|
|
34
|
+
const header = createElement({
|
|
35
|
+
tag: 'div',
|
|
36
|
+
className: `${prefix}-datepicker-header`
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Month selector
|
|
40
|
+
const monthButton = createElement({
|
|
41
|
+
tag: 'button',
|
|
42
|
+
className: `${prefix}-datepicker-month-selector`,
|
|
43
|
+
text: MONTH_NAMES[currentMonth],
|
|
44
|
+
attrs: {
|
|
45
|
+
type: 'button',
|
|
46
|
+
'aria-label': 'Select month'
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Year selector
|
|
51
|
+
const yearButton = createElement({
|
|
52
|
+
tag: 'button',
|
|
53
|
+
className: `${prefix}-datepicker-year-selector`,
|
|
54
|
+
text: currentYear.toString(),
|
|
55
|
+
attrs: {
|
|
56
|
+
type: 'button',
|
|
57
|
+
'aria-label': 'Select year'
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Navigation controls container
|
|
62
|
+
const navControls = createElement({
|
|
63
|
+
tag: 'div',
|
|
64
|
+
className: `${prefix}-datepicker-nav-controls`
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Previous button
|
|
68
|
+
const prevButton = createElement({
|
|
69
|
+
tag: 'button',
|
|
70
|
+
className: `${prefix}-datepicker-prev-btn`,
|
|
71
|
+
html: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>',
|
|
72
|
+
attrs: {
|
|
73
|
+
type: 'button',
|
|
74
|
+
'aria-label': currentView === DATEPICKER_VIEWS.DAY ? 'Previous month' :
|
|
75
|
+
currentView === DATEPICKER_VIEWS.MONTH ? 'Previous year' : 'Previous year range'
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Next button
|
|
80
|
+
const nextButton = createElement({
|
|
81
|
+
tag: 'button',
|
|
82
|
+
className: `${prefix}-datepicker-next-btn`,
|
|
83
|
+
html: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>',
|
|
84
|
+
attrs: {
|
|
85
|
+
type: 'button',
|
|
86
|
+
'aria-label': currentView === DATEPICKER_VIEWS.DAY ? 'Next month' :
|
|
87
|
+
currentView === DATEPICKER_VIEWS.MONTH ? 'Next year' : 'Next year range'
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Event listeners
|
|
92
|
+
monthButton.addEventListener('click', (event) => {
|
|
93
|
+
// Prevent event from bubbling up
|
|
94
|
+
event.stopPropagation();
|
|
95
|
+
emit('viewChange', { view: DATEPICKER_VIEWS.MONTH });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
yearButton.addEventListener('click', (event) => {
|
|
99
|
+
// Prevent event from bubbling up
|
|
100
|
+
event.stopPropagation();
|
|
101
|
+
emit('viewChange', { view: DATEPICKER_VIEWS.YEAR });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
prevButton.addEventListener('click', (event) => {
|
|
105
|
+
// Prevent event from bubbling up
|
|
106
|
+
event.stopPropagation();
|
|
107
|
+
|
|
108
|
+
if (currentView === DATEPICKER_VIEWS.DAY) {
|
|
109
|
+
emit('prevMonth');
|
|
110
|
+
} else if (currentView === DATEPICKER_VIEWS.MONTH) {
|
|
111
|
+
emit('prevYear');
|
|
112
|
+
} else {
|
|
113
|
+
emit('prevYearRange');
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
nextButton.addEventListener('click', (event) => {
|
|
118
|
+
// Prevent event from bubbling up
|
|
119
|
+
event.stopPropagation();
|
|
120
|
+
|
|
121
|
+
if (currentView === DATEPICKER_VIEWS.DAY) {
|
|
122
|
+
emit('nextMonth');
|
|
123
|
+
} else if (currentView === DATEPICKER_VIEWS.MONTH) {
|
|
124
|
+
emit('nextYear');
|
|
125
|
+
} else {
|
|
126
|
+
emit('nextYearRange');
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Add buttons to container
|
|
131
|
+
header.appendChild(monthButton);
|
|
132
|
+
header.appendChild(yearButton);
|
|
133
|
+
|
|
134
|
+
navControls.appendChild(prevButton);
|
|
135
|
+
navControls.appendChild(nextButton);
|
|
136
|
+
header.appendChild(navControls);
|
|
137
|
+
|
|
138
|
+
return header;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Renders the days of the week (S, M, T, etc.)
|
|
143
|
+
* @param {string} prefix - CSS class prefix
|
|
144
|
+
* @returns {HTMLElement} Weekdays element
|
|
145
|
+
*/
|
|
146
|
+
export const renderWeekdays = (prefix: string): HTMLElement => {
|
|
147
|
+
const weekdaysRow = createElement({
|
|
148
|
+
tag: 'div',
|
|
149
|
+
className: `${prefix}-datepicker-weekdays`
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
DAY_NAMES.forEach(day => {
|
|
153
|
+
const dayElement = createElement({
|
|
154
|
+
tag: 'span',
|
|
155
|
+
className: `${prefix}-datepicker-weekday`,
|
|
156
|
+
text: day
|
|
157
|
+
});
|
|
158
|
+
weekdaysRow.appendChild(dayElement);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return weekdaysRow;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Renders the calendar days
|
|
166
|
+
* @param {Object} params - Rendering parameters
|
|
167
|
+
* @returns {HTMLElement} Calendar days element
|
|
168
|
+
*/
|
|
169
|
+
export const renderDays = ({
|
|
170
|
+
currentYear,
|
|
171
|
+
currentMonth,
|
|
172
|
+
selectedDate,
|
|
173
|
+
rangeEndDate,
|
|
174
|
+
minDate,
|
|
175
|
+
maxDate,
|
|
176
|
+
prefix,
|
|
177
|
+
emit
|
|
178
|
+
}: any): HTMLElement => {
|
|
179
|
+
const daysGrid = createElement({
|
|
180
|
+
tag: 'div',
|
|
181
|
+
className: `${prefix}-datepicker-days`
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const calendarDates = generateCalendarDates(
|
|
185
|
+
currentYear,
|
|
186
|
+
currentMonth,
|
|
187
|
+
selectedDate,
|
|
188
|
+
rangeEndDate,
|
|
189
|
+
minDate,
|
|
190
|
+
maxDate
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
calendarDates.forEach((calendarDate: CalendarDate) => {
|
|
194
|
+
const classNames = [
|
|
195
|
+
`${prefix}-datepicker-day`,
|
|
196
|
+
calendarDate.isCurrentMonth ? '' : OUTSIDE_MONTH_CLASS,
|
|
197
|
+
calendarDate.isToday ? TODAY_CLASS : '',
|
|
198
|
+
calendarDate.isSelected ? SELECTED_CLASS : '',
|
|
199
|
+
calendarDate.isDisabled ? 'disabled' : '',
|
|
200
|
+
calendarDate.isRangeStart ? RANGE_START_CLASS : '',
|
|
201
|
+
calendarDate.isRangeEnd ? RANGE_END_CLASS : '',
|
|
202
|
+
calendarDate.isRangeMiddle ? RANGE_MIDDLE_CLASS : ''
|
|
203
|
+
].filter(Boolean).join(' ');
|
|
204
|
+
|
|
205
|
+
const dayElement = createElement({
|
|
206
|
+
tag: 'button',
|
|
207
|
+
className: classNames,
|
|
208
|
+
text: calendarDate.day.toString(),
|
|
209
|
+
attrs: {
|
|
210
|
+
type: 'button',
|
|
211
|
+
'aria-label': calendarDate.date.toLocaleDateString(),
|
|
212
|
+
'aria-selected': calendarDate.isSelected ? 'true' : 'false',
|
|
213
|
+
'aria-disabled': calendarDate.isDisabled ? 'true' : 'false',
|
|
214
|
+
'data-date': calendarDate.date.toISOString(),
|
|
215
|
+
disabled: calendarDate.isDisabled
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Event listener
|
|
220
|
+
dayElement.addEventListener('click', (event) => {
|
|
221
|
+
// Prevent event from bubbling up
|
|
222
|
+
event.stopPropagation();
|
|
223
|
+
|
|
224
|
+
if (!calendarDate.isDisabled) {
|
|
225
|
+
emit('dateSelected', { date: calendarDate.date });
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
daysGrid.appendChild(dayElement);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return daysGrid;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Renders the month selection view
|
|
237
|
+
* @param {Object} params - Rendering parameters
|
|
238
|
+
* @returns {HTMLElement} Month selection element
|
|
239
|
+
*/
|
|
240
|
+
export const renderMonthSelection = ({
|
|
241
|
+
currentYear,
|
|
242
|
+
currentMonth,
|
|
243
|
+
prefix,
|
|
244
|
+
emit
|
|
245
|
+
}: any): HTMLElement => {
|
|
246
|
+
const monthsGrid = createElement({
|
|
247
|
+
tag: 'div',
|
|
248
|
+
className: `${prefix}-datepicker-months`
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
MONTH_NAMES_SHORT.forEach((month, index) => {
|
|
252
|
+
const isSelected = index === currentMonth;
|
|
253
|
+
|
|
254
|
+
const monthElement = createElement({
|
|
255
|
+
tag: 'button',
|
|
256
|
+
className: `${prefix}-datepicker-month ${isSelected ? SELECTED_CLASS : ''}`,
|
|
257
|
+
text: month,
|
|
258
|
+
attrs: {
|
|
259
|
+
type: 'button',
|
|
260
|
+
'aria-selected': isSelected ? 'true' : 'false',
|
|
261
|
+
'data-month': index.toString()
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
monthElement.addEventListener('click', (event) => {
|
|
266
|
+
// Prevent event from bubbling up
|
|
267
|
+
event.stopPropagation();
|
|
268
|
+
emit('monthSelected', { month: index });
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
monthsGrid.appendChild(monthElement);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return monthsGrid;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Renders the year selection view
|
|
279
|
+
* @param {Object} params - Rendering parameters
|
|
280
|
+
* @returns {HTMLElement} Year selection element
|
|
281
|
+
*/
|
|
282
|
+
export const renderYearSelection = ({
|
|
283
|
+
currentYear,
|
|
284
|
+
prefix,
|
|
285
|
+
emit
|
|
286
|
+
}: any): HTMLElement => {
|
|
287
|
+
const yearsGrid = createElement({
|
|
288
|
+
tag: 'div',
|
|
289
|
+
className: `${prefix}-datepicker-years`
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const yearRange = generateYearRange(currentYear, 10);
|
|
293
|
+
|
|
294
|
+
yearRange.forEach(year => {
|
|
295
|
+
const isSelected = year === currentYear;
|
|
296
|
+
|
|
297
|
+
const yearElement = createElement({
|
|
298
|
+
tag: 'button',
|
|
299
|
+
className: `${prefix}-datepicker-year ${isSelected ? SELECTED_CLASS : ''}`,
|
|
300
|
+
text: year.toString(),
|
|
301
|
+
attrs: {
|
|
302
|
+
type: 'button',
|
|
303
|
+
'aria-selected': isSelected ? 'true' : 'false',
|
|
304
|
+
'data-year': year.toString()
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
yearElement.addEventListener('click', (event) => {
|
|
309
|
+
// Prevent event from bubbling up
|
|
310
|
+
event.stopPropagation();
|
|
311
|
+
emit('yearSelected', { year });
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
yearsGrid.appendChild(yearElement);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
return yearsGrid;
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Renders the footer with action buttons
|
|
322
|
+
* @param {Object} params - Rendering parameters
|
|
323
|
+
* @returns {HTMLElement} Footer element
|
|
324
|
+
*/
|
|
325
|
+
export const renderFooter = ({
|
|
326
|
+
variant,
|
|
327
|
+
prefix,
|
|
328
|
+
emit
|
|
329
|
+
}: any): HTMLElement => {
|
|
330
|
+
const footer = createElement({
|
|
331
|
+
tag: 'div',
|
|
332
|
+
className: `${prefix}-datepicker-footer`
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Cancel button
|
|
336
|
+
const cancelButton = createElement({
|
|
337
|
+
tag: 'button',
|
|
338
|
+
className: `${prefix}-datepicker-cancel ${prefix}-button ${prefix}-button--text`,
|
|
339
|
+
text: 'Cancel',
|
|
340
|
+
attrs: {
|
|
341
|
+
type: 'button'
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// OK button
|
|
346
|
+
const okButton = createElement({
|
|
347
|
+
tag: 'button',
|
|
348
|
+
className: `${prefix}-datepicker-ok ${prefix}-button ${prefix}-button--text`,
|
|
349
|
+
text: 'OK',
|
|
350
|
+
attrs: {
|
|
351
|
+
type: 'button'
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
cancelButton.addEventListener('click', (event) => {
|
|
356
|
+
// Prevent event from bubbling up
|
|
357
|
+
event.stopPropagation();
|
|
358
|
+
emit('cancel');
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
okButton.addEventListener('click', (event) => {
|
|
362
|
+
// Prevent event from bubbling up
|
|
363
|
+
event.stopPropagation();
|
|
364
|
+
emit('confirm');
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
footer.appendChild(cancelButton);
|
|
368
|
+
footer.appendChild(okButton);
|
|
369
|
+
|
|
370
|
+
return footer;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Renders a complete calendar view
|
|
375
|
+
* @param {Object} state - Current datepicker state
|
|
376
|
+
* @param {Function} emit - Event emission function
|
|
377
|
+
* @returns {HTMLElement} Rendered calendar
|
|
378
|
+
*/
|
|
379
|
+
export const renderCalendar = (state: any, emit: Function): HTMLElement => {
|
|
380
|
+
const {
|
|
381
|
+
prefix,
|
|
382
|
+
currentView,
|
|
383
|
+
currentMonth,
|
|
384
|
+
currentYear,
|
|
385
|
+
selectedDate,
|
|
386
|
+
rangeEndDate,
|
|
387
|
+
minDate,
|
|
388
|
+
maxDate,
|
|
389
|
+
variant
|
|
390
|
+
} = state;
|
|
391
|
+
|
|
392
|
+
// Create calendar container
|
|
393
|
+
const calendar = createElement({
|
|
394
|
+
tag: 'div',
|
|
395
|
+
className: `${prefix}-datepicker-calendar-content`
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Render header
|
|
399
|
+
const header = renderHeader({
|
|
400
|
+
currentMonth,
|
|
401
|
+
currentYear,
|
|
402
|
+
currentView,
|
|
403
|
+
prefix,
|
|
404
|
+
emit
|
|
405
|
+
});
|
|
406
|
+
calendar.appendChild(header);
|
|
407
|
+
|
|
408
|
+
// Render content based on current view
|
|
409
|
+
if (currentView === DATEPICKER_VIEWS.DAY) {
|
|
410
|
+
const weekdays = renderWeekdays(prefix);
|
|
411
|
+
calendar.appendChild(weekdays);
|
|
412
|
+
|
|
413
|
+
const days = renderDays({
|
|
414
|
+
currentYear,
|
|
415
|
+
currentMonth,
|
|
416
|
+
selectedDate,
|
|
417
|
+
rangeEndDate,
|
|
418
|
+
minDate,
|
|
419
|
+
maxDate,
|
|
420
|
+
prefix,
|
|
421
|
+
emit
|
|
422
|
+
});
|
|
423
|
+
calendar.appendChild(days);
|
|
424
|
+
} else if (currentView === DATEPICKER_VIEWS.MONTH) {
|
|
425
|
+
const months = renderMonthSelection({
|
|
426
|
+
currentYear,
|
|
427
|
+
currentMonth,
|
|
428
|
+
prefix,
|
|
429
|
+
emit
|
|
430
|
+
});
|
|
431
|
+
calendar.appendChild(months);
|
|
432
|
+
} else if (currentView === DATEPICKER_VIEWS.YEAR) {
|
|
433
|
+
const years = renderYearSelection({
|
|
434
|
+
currentYear,
|
|
435
|
+
prefix,
|
|
436
|
+
emit
|
|
437
|
+
});
|
|
438
|
+
calendar.appendChild(years);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Only add footer for modal variants
|
|
442
|
+
if (variant !== DATEPICKER_VARIANTS.DOCKED) {
|
|
443
|
+
const footer = renderFooter({
|
|
444
|
+
variant,
|
|
445
|
+
prefix,
|
|
446
|
+
emit
|
|
447
|
+
});
|
|
448
|
+
calendar.appendChild(footer);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return calendar;
|
|
452
|
+
};
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
// src/components/datepicker/types.ts
|
|
2
|
+
import {
|
|
3
|
+
DATEPICKER_VARIANTS,
|
|
4
|
+
DATEPICKER_VIEWS,
|
|
5
|
+
DATEPICKER_SELECTION_MODES
|
|
6
|
+
} from './constants';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configuration interface for the DatePicker component
|
|
10
|
+
* @category Components
|
|
11
|
+
*/
|
|
12
|
+
export interface DatePickerConfig {
|
|
13
|
+
/**
|
|
14
|
+
* DatePicker variant that determines display style
|
|
15
|
+
* @default 'docked'
|
|
16
|
+
*/
|
|
17
|
+
variant?: keyof typeof DATEPICKER_VARIANTS | string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Whether the datepicker is initially disabled
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Initial view to display (day, month, year)
|
|
27
|
+
* @default 'day'
|
|
28
|
+
*/
|
|
29
|
+
initialView?: keyof typeof DATEPICKER_VIEWS | string;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Selection mode for the datepicker (single or range)
|
|
33
|
+
* @default 'single'
|
|
34
|
+
*/
|
|
35
|
+
selectionMode?: keyof typeof DATEPICKER_SELECTION_MODES | string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Initial selected date(s)
|
|
39
|
+
* Accepts a Date object, Date string, or two dates for range selection
|
|
40
|
+
*/
|
|
41
|
+
value?: Date | string | [Date | string, Date | string];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Minimum selectable date
|
|
45
|
+
*/
|
|
46
|
+
minDate?: Date | string;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Maximum selectable date
|
|
50
|
+
*/
|
|
51
|
+
maxDate?: Date | string;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Format for displaying dates
|
|
55
|
+
* @default 'MM/DD/YYYY'
|
|
56
|
+
*/
|
|
57
|
+
dateFormat?: string;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Label text for the datepicker
|
|
61
|
+
* @example 'Select Date'
|
|
62
|
+
*/
|
|
63
|
+
label?: string;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Placeholder text for the input field
|
|
67
|
+
* @example 'MM/DD/YYYY'
|
|
68
|
+
*/
|
|
69
|
+
placeholder?: string;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Additional CSS classes to add to the datepicker
|
|
73
|
+
* @example 'form-field event-date'
|
|
74
|
+
*/
|
|
75
|
+
class?: string;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Component prefix for class names
|
|
79
|
+
* @default 'mtrl'
|
|
80
|
+
*/
|
|
81
|
+
prefix?: string;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Component name used in class generation
|
|
85
|
+
*/
|
|
86
|
+
componentName?: string;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Whether to enable animation effects
|
|
90
|
+
* @default true
|
|
91
|
+
*/
|
|
92
|
+
animate?: boolean;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Whether to close the picker when a date is selected
|
|
96
|
+
* @default true for modal variants, false for docked
|
|
97
|
+
*/
|
|
98
|
+
closeOnSelect?: boolean;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Array of dates to highlight or disable
|
|
102
|
+
*/
|
|
103
|
+
specialDates?: Array<{
|
|
104
|
+
date: Date | string;
|
|
105
|
+
highlight?: boolean;
|
|
106
|
+
disabled?: boolean;
|
|
107
|
+
tooltip?: string;
|
|
108
|
+
}>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Date object with additional properties for rendering
|
|
113
|
+
* @internal
|
|
114
|
+
*/
|
|
115
|
+
export interface CalendarDate {
|
|
116
|
+
/** JavaScript Date object */
|
|
117
|
+
date: Date;
|
|
118
|
+
|
|
119
|
+
/** Day of the month (1-31) */
|
|
120
|
+
day: number;
|
|
121
|
+
|
|
122
|
+
/** Whether the date is in the current month being displayed */
|
|
123
|
+
isCurrentMonth: boolean;
|
|
124
|
+
|
|
125
|
+
/** Whether the date is today */
|
|
126
|
+
isToday: boolean;
|
|
127
|
+
|
|
128
|
+
/** Whether the date is selected */
|
|
129
|
+
isSelected: boolean;
|
|
130
|
+
|
|
131
|
+
/** Whether the date is disabled */
|
|
132
|
+
isDisabled: boolean;
|
|
133
|
+
|
|
134
|
+
/** Whether the date is the first date in a range */
|
|
135
|
+
isRangeStart?: boolean;
|
|
136
|
+
|
|
137
|
+
/** Whether the date is the last date in a range */
|
|
138
|
+
isRangeEnd?: boolean;
|
|
139
|
+
|
|
140
|
+
/** Whether the date is between start and end in a range */
|
|
141
|
+
isRangeMiddle?: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* DatePicker component interface
|
|
146
|
+
* @category Components
|
|
147
|
+
*/
|
|
148
|
+
export interface DatePickerComponent {
|
|
149
|
+
/** The datepicker's main DOM element */
|
|
150
|
+
element: HTMLElement;
|
|
151
|
+
|
|
152
|
+
/** The input field DOM element */
|
|
153
|
+
input: HTMLInputElement;
|
|
154
|
+
|
|
155
|
+
/** API for managing calendar and selected dates */
|
|
156
|
+
calendar: {
|
|
157
|
+
/** Updates the calendar view to display a specific month/year */
|
|
158
|
+
goToDate: (date: Date) => void;
|
|
159
|
+
|
|
160
|
+
/** Moves to the next month */
|
|
161
|
+
nextMonth: () => void;
|
|
162
|
+
|
|
163
|
+
/** Moves to the previous month */
|
|
164
|
+
prevMonth: () => void;
|
|
165
|
+
|
|
166
|
+
/** Moves to the next year */
|
|
167
|
+
nextYear: () => void;
|
|
168
|
+
|
|
169
|
+
/** Moves to the previous year */
|
|
170
|
+
prevYear: () => void;
|
|
171
|
+
|
|
172
|
+
/** Switches to day selection view */
|
|
173
|
+
showDayView: () => void;
|
|
174
|
+
|
|
175
|
+
/** Switches to month selection view */
|
|
176
|
+
showMonthView: () => void;
|
|
177
|
+
|
|
178
|
+
/** Switches to year selection view */
|
|
179
|
+
showYearView: () => void;
|
|
180
|
+
|
|
181
|
+
/** Gets the current calendar view */
|
|
182
|
+
getCurrentView: () => string;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Opens the datepicker dropdown/modal
|
|
187
|
+
* @returns The datepicker component for chaining
|
|
188
|
+
*/
|
|
189
|
+
open: () => DatePickerComponent;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Closes the datepicker dropdown/modal
|
|
193
|
+
* @returns The datepicker component for chaining
|
|
194
|
+
*/
|
|
195
|
+
close: () => DatePickerComponent;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Gets the selected date(s)
|
|
199
|
+
* @returns Date object, array of two Date objects for range, or null if none selected
|
|
200
|
+
*/
|
|
201
|
+
getValue: () => Date | [Date, Date] | null;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Sets the selected date(s)
|
|
205
|
+
* @param value - Date, string, or array of dates for range selection
|
|
206
|
+
* @returns The datepicker component for chaining
|
|
207
|
+
*/
|
|
208
|
+
setValue: (value: Date | string | [Date | string, Date | string]) => DatePickerComponent;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Gets the formatted date string based on the selected date(s)
|
|
212
|
+
* @returns Formatted date string or empty string if none selected
|
|
213
|
+
*/
|
|
214
|
+
getFormattedValue: () => string;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Clears the selected date(s)
|
|
218
|
+
* @returns The datepicker component for chaining
|
|
219
|
+
*/
|
|
220
|
+
clear: () => DatePickerComponent;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Enables the datepicker
|
|
224
|
+
* @returns The datepicker component for chaining
|
|
225
|
+
*/
|
|
226
|
+
enable: () => DatePickerComponent;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Disables the datepicker
|
|
230
|
+
* @returns The datepicker component for chaining
|
|
231
|
+
*/
|
|
232
|
+
disable: () => DatePickerComponent;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Sets the minimum selectable date
|
|
236
|
+
* @param date - Date object or string
|
|
237
|
+
* @returns The datepicker component for chaining
|
|
238
|
+
*/
|
|
239
|
+
setMinDate: (date: Date | string) => DatePickerComponent;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Sets the maximum selectable date
|
|
243
|
+
* @param date - Date object or string
|
|
244
|
+
* @returns The datepicker component for chaining
|
|
245
|
+
*/
|
|
246
|
+
setMaxDate: (date: Date | string) => DatePickerComponent;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Destroys the datepicker component and cleans up resources
|
|
250
|
+
*/
|
|
251
|
+
destroy: () => void;
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Adds an event listener to the datepicker
|
|
255
|
+
* @param event - Event name ('change', 'open', 'close', etc.)
|
|
256
|
+
* @param handler - Event handler function
|
|
257
|
+
* @returns The datepicker component for chaining
|
|
258
|
+
*/
|
|
259
|
+
on: (event: string, handler: Function) => DatePickerComponent;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Removes an event listener from the datepicker
|
|
263
|
+
* @param event - Event name
|
|
264
|
+
* @param handler - Event handler function
|
|
265
|
+
* @returns The datepicker component for chaining
|
|
266
|
+
*/
|
|
267
|
+
off: (event: string, handler: Function) => DatePickerComponent;
|
|
268
|
+
}
|