@luftborn/custom-elements 2.8.1 → 2.8.3

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.
Files changed (24) hide show
  1. package/demo/index.js +680 -2852
  2. package/demo/index.min.js +679 -2851
  3. package/demo/index.min.js.map +1 -1
  4. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepicker.d.ts +48 -0
  5. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepicker.js +333 -0
  6. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepicker.js.map +1 -0
  7. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepickerStyles.d.ts +1 -0
  8. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepickerStyles.js +5 -0
  9. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepickerStyles.js.map +1 -0
  10. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepickerUtils.d.ts +6 -0
  11. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepickerUtils.js +45 -0
  12. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepickerUtils.js.map +1 -0
  13. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/DatepickerTranslations.d.ts +2 -0
  14. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/DatepickerTranslations.js +194 -0
  15. package/dist/elements/CustomFormatDateFieldElement/CustomDatepicker/DatepickerTranslations.js.map +1 -0
  16. package/dist/elements/CustomFormatDateFieldElement/CustomFormatDateFieldElement.d.ts +7 -6
  17. package/dist/elements/CustomFormatDateFieldElement/CustomFormatDateFieldElement.js +27 -59
  18. package/dist/elements/CustomFormatDateFieldElement/CustomFormatDateFieldElement.js.map +1 -1
  19. package/package.json +2 -6
  20. package/src/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepicker.ts +363 -0
  21. package/src/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepickerStyles.ts +101 -0
  22. package/src/elements/CustomFormatDateFieldElement/CustomDatepicker/CustomDatepickerUtils.ts +44 -0
  23. package/src/elements/CustomFormatDateFieldElement/CustomDatepicker/DatepickerTranslations.ts +188 -0
  24. package/src/elements/CustomFormatDateFieldElement/CustomFormatDateFieldElement.ts +30 -67
@@ -0,0 +1,363 @@
1
+ import { CustomDatepickerStyles } from "./CustomDatepickerStyles";
2
+ import { defaultDateFormat, formatDate, isMobileDevice, supportedDateFormats } from "./CustomDatepickerUtils";
3
+ import { GetMonths, GetWeekdays } from "./DatepickerTranslations";
4
+
5
+ type CustomDatepickerOptions = {
6
+ input: HTMLInputElement;
7
+ dateFormat: string;
8
+ language?: string;
9
+ otherTriggers?: HTMLElement[];
10
+ parentElement?: ShadowRoot | HTMLElement;
11
+ }
12
+
13
+ export default class CustomDatepicker {
14
+ private input: HTMLInputElement;
15
+ private otherTriggers: HTMLElement[];
16
+ private parentElement: ShadowRoot | HTMLElement;
17
+
18
+ private datepicker: HTMLElement;
19
+ private header: HTMLElement;
20
+ private monthYear: HTMLElement;
21
+ private selectNextMonth: HTMLElement;
22
+ private selectPrevMonth: HTMLElement;
23
+ private weekdays: HTMLElement;
24
+ private days: HTMLElement;
25
+ private selectMonthYear: HTMLElement;
26
+ private selectDate: HTMLElement;
27
+ private currentDate: Date;
28
+ private currentMonth: number;
29
+ private currentYear: number;
30
+ private selectedDate: Date;
31
+ private selectedMonth: number;
32
+ private selectedYear: number;
33
+ private selectedDay: number;
34
+ private firstDayOfMonth: Date;
35
+ private lastDayOfMonth: Date;
36
+ private firstDay: number;
37
+ private lastDay: number;
38
+
39
+ private months: string[] = [];
40
+ private daysOfWeek: string[] = [];
41
+
42
+ private dateFormat: string;
43
+
44
+ constructor(options: CustomDatepickerOptions) {
45
+ this.input = options.input;
46
+ this.dateFormat = supportedDateFormats.includes(options.dateFormat) ? options.dateFormat : defaultDateFormat;
47
+ this.otherTriggers = options.otherTriggers || [];
48
+ this.parentElement = options.parentElement;
49
+
50
+ this.currentDate = new Date();
51
+ this.currentMonth = this.currentDate.getMonth();
52
+ this.currentYear = this.currentDate.getFullYear();
53
+ this.selectedDate = new Date();
54
+ this.selectedMonth = this.selectedDate.getMonth();
55
+ this.selectedYear = this.selectedDate.getFullYear();
56
+ this.selectedDay = this.selectedDate.getDate();
57
+ this.firstDayOfMonth = new Date(this.currentYear, this.currentMonth, 1);
58
+ this.lastDayOfMonth = new Date(this.currentYear, this.currentMonth + 1, 0);
59
+ this.firstDay = this.firstDayOfMonth.getDay();
60
+ this.lastDay = this.lastDayOfMonth.getDate();
61
+
62
+ this.months = GetMonths(options.language);
63
+ this.daysOfWeek = GetWeekdays(options.language, 'short');
64
+
65
+ this.createDatePickerElements();
66
+ this.renderCalendar();
67
+ this.initSelectMonthYear();
68
+ this.addEventListeners();
69
+ }
70
+
71
+ private createDatePickerElements() {
72
+ this.datepicker = document.createElement('div');
73
+ this.datepicker.classList.add('datepicker');
74
+ this.header = document.createElement('div');
75
+ this.header.classList.add('header');
76
+ this.monthYear = document.createElement('span');
77
+ this.monthYear.classList.add('month-year');
78
+ this.monthYear.onclick = () => this.toggleSelectMonthYear();
79
+ this.selectNextMonth = document.createElement('span');
80
+ this.selectNextMonth.classList.add('next-month');
81
+ this.selectNextMonth.innerHTML = '▶';
82
+ this.selectNextMonth.onclick = () => this.moveMonth(1);
83
+ this.selectPrevMonth = document.createElement('span');
84
+ this.selectPrevMonth.classList.add('prev-month');
85
+ this.selectPrevMonth.innerHTML = '◀';
86
+ this.selectPrevMonth.onclick = () => this.moveMonth(-1);
87
+ this.header.appendChild(this.selectPrevMonth);
88
+ this.header.appendChild(this.monthYear);
89
+ this.header.appendChild(this.selectNextMonth);
90
+
91
+ this.weekdays = document.createElement('div');
92
+ this.weekdays.classList.add('weekdays');
93
+ this.days = document.createElement('div');
94
+ this.days.classList.add('days');
95
+ this.selectMonthYear = document.createElement('div');
96
+ this.selectMonthYear.classList.add('select-month-year');
97
+ this.selectDate = document.createElement('div');
98
+ this.selectDate.classList.add('select-date');
99
+ this.datepicker.appendChild(this.header);
100
+ this.datepicker.appendChild(this.selectDate);
101
+ this.selectDate.appendChild(this.weekdays);
102
+ this.selectDate.appendChild(this.days);
103
+ this.datepicker.appendChild(this.selectMonthYear);
104
+
105
+ let actions = document.createElement('div');
106
+ actions.classList.add('actions');
107
+ this.selectDate.appendChild(actions);
108
+ let cancel = document.createElement('input');
109
+ cancel.type = 'reset';
110
+ cancel.value = 'Cancel';
111
+ cancel.onclick = () => this.showPicker(false);
112
+ actions.appendChild(cancel);
113
+ let today = document.createElement('input');
114
+ today.type = 'reset';
115
+ today.value = 'Today';
116
+ today.onclick = () => this.setDateForToday();
117
+ actions.appendChild(today);
118
+
119
+ if (this.input.parentElement) {
120
+ this.input.parentElement.appendChild(this.datepicker);
121
+ } else {
122
+ document.body.appendChild(this.datepicker);
123
+ }
124
+
125
+ this.applyStyles();
126
+ }
127
+
128
+ private applyStyles() {
129
+ const style = document.createElement('style');
130
+ style.innerHTML = CustomDatepickerStyles;
131
+ this.datepicker.appendChild(style);
132
+ }
133
+
134
+ private moveMonth(steps: number) {
135
+ let newMonth = this.currentMonth + steps;
136
+ let newYear = this.currentYear;
137
+ if (newMonth < 0) {
138
+ newYear = this.currentYear + Math.floor(newMonth / 12);
139
+ newMonth = 12 + (newMonth % 12);
140
+ } else if (newMonth > 11) {
141
+ newYear = this.currentYear + Math.floor(newMonth / 12);
142
+ newMonth = newMonth % 12;
143
+ }
144
+ this.currentMonth = newMonth;
145
+ this.currentYear = newYear;
146
+ this.firstDayOfMonth = new Date(this.currentYear, this.currentMonth, 1);
147
+ this.lastDayOfMonth = new Date(this.currentYear, this.currentMonth + 1, 0);
148
+ this.firstDay = this.firstDayOfMonth.getDay();
149
+ this.lastDay = this.lastDayOfMonth.getDate();
150
+ this.renderCalendar();
151
+ }
152
+
153
+ private renderCalendar() {
154
+ this.monthYear.innerHTML = this.months[this.currentMonth] + ' ' + this.currentYear;
155
+ this.weekdays.innerHTML = '';
156
+ for (let i = 0; i < this.daysOfWeek.length; i++) {
157
+ let span = document.createElement('span');
158
+ span.innerHTML = this.daysOfWeek[i];
159
+ this.weekdays.appendChild(span);
160
+ }
161
+ this.days.innerHTML = '';
162
+ for (let i = 0; i < 42; i++) {
163
+ let span = document.createElement('span');
164
+ if (i < this.firstDay) {
165
+ span.classList.add('day-of-previous-month');
166
+ span.style.color = '#ccc';
167
+ let currentDate = new Date(this.currentYear, this.currentMonth, 0);
168
+ span.innerHTML = (currentDate.getDate() - this.firstDay + i + 1).toString();
169
+ } else if (i < this.lastDay + this.firstDay) {
170
+ span.classList.add('day-of-current-month');
171
+ span.innerHTML = (i - this.firstDay + 1).toString();
172
+ if (i - this.firstDay + 1 === this.selectedDay && this.currentMonth === this.selectedMonth && this.currentYear === this.selectedYear) {
173
+ span.classList.add('selected-day')
174
+ }
175
+ } else {
176
+ span.classList.add('day-of-next-month');
177
+ span.style.color = '#ccc';
178
+ span.innerHTML = (i - this.lastDay - this.firstDay + 1).toString();
179
+ }
180
+ span.onclick = () => {
181
+ let moveMonthSteps = 0;
182
+ this.selectedDay = parseInt(span.innerHTML);
183
+ if (span.classList.contains('day-of-previous-month')) {
184
+ this.selectedMonth = this.currentMonth - 1;
185
+ this.selectedYear = this.currentYear;
186
+ moveMonthSteps = -1;
187
+ } else if (span.classList.contains('day-of-current-month')) {
188
+ this.selectedMonth = this.currentMonth;
189
+ this.selectedYear = this.currentYear;
190
+ } else {
191
+ this.selectedMonth = this.currentMonth + 1;
192
+ this.selectedYear = this.currentYear;
193
+ moveMonthSteps = 1;
194
+ }
195
+ this.selectedDate = new Date(this.selectedYear, this.selectedMonth, this.selectedDay);
196
+ let selectedDayElement = this.days.querySelector('.selected-day');
197
+ if (selectedDayElement) {
198
+ selectedDayElement.classList.remove('selected-day');
199
+ }
200
+ span.classList.add('selected-day');
201
+
202
+ const formattedDate = formatDate(this.selectedDate, this.dateFormat);
203
+ this.input.value = formattedDate;
204
+
205
+ this.showPicker(false);
206
+ };
207
+ this.days.appendChild(span);
208
+ }
209
+
210
+ this.positionPicker();
211
+ }
212
+
213
+ private initSelectMonthYear() {
214
+ this.selectMonthYear.style.display = 'none';
215
+ this.selectMonthYear.style.height = '200px';
216
+ if (isMobileDevice()) {
217
+ this.selectMonthYear.style.height = '300px';
218
+ }
219
+ this.selectMonthYear.innerHTML = '';
220
+ let monthsContainer = document.createElement('div');
221
+ monthsContainer.classList.add('months');
222
+ for (let i = 0; i < this.months.length; i++) {
223
+ let span = document.createElement('span');
224
+ span.innerHTML = this.months[i].substring(0, 3);
225
+ span.classList.add('select-month');
226
+ monthsContainer.appendChild(span);
227
+ span.onclick = () => {
228
+ this.selectedMonth = this.months.findIndex(month => month.substring(0, 3) === span.innerHTML);
229
+ let moveMonthSteps = (this.selectedYear - this.currentYear) * 12 + this.selectedMonth - this.currentMonth;
230
+ this.moveMonth(moveMonthSteps);
231
+ this.hideSelectMonthYear();
232
+ }
233
+ }
234
+ for (let i = this.currentYear - 100; i < this.currentYear + 100; i++) {
235
+ let year = document.createElement('div');
236
+ year.classList.add('year');
237
+ year.innerHTML = i.toString();
238
+ this.selectMonthYear.appendChild(year);
239
+ if (i === this.currentYear) {
240
+ year.appendChild(monthsContainer);
241
+ }
242
+ }
243
+ let years = this.selectMonthYear.querySelectorAll('.year');
244
+ let tempSelectedYear = this.selectedYear;
245
+ for (let i = 0; i < years.length; i++) {
246
+ years[i].addEventListener('click', () => {
247
+ let year = parseInt(years[i].innerHTML);
248
+ if (year === tempSelectedYear) {
249
+ return;
250
+ } else {
251
+ tempSelectedYear = year;
252
+ this.selectedYear = year;
253
+ let monthsContainers = this.selectMonthYear.querySelectorAll('.months');
254
+ for (let i = 0; i < monthsContainers.length; i++) {
255
+ monthsContainers[i].remove();
256
+ }
257
+ years[i].appendChild(monthsContainer);
258
+ }
259
+ });
260
+ }
261
+ }
262
+
263
+ private toggleSelectMonthYear() {
264
+ if (this.selectMonthYear.style.display === 'none') {
265
+ this.selectDate.style.display = 'none';
266
+ this.selectMonthYear.style.display = 'block';
267
+ this.header.style.color = '#ccc';
268
+ let years = this.selectMonthYear.querySelectorAll('.year');
269
+ for (let i = 0; i < years.length; i++) {
270
+ if (years[i].querySelector('.months')) {
271
+ years[i].scrollIntoView();
272
+ break;
273
+ }
274
+ }
275
+ } else {
276
+ this.hideSelectMonthYear();
277
+ }
278
+ }
279
+
280
+ private hideSelectMonthYear() {
281
+ this.selectDate.style.display = 'block';
282
+ this.selectMonthYear.style.display = 'none';
283
+ this.header.style.color = '#000';
284
+ }
285
+
286
+ private showPicker(showpicker: boolean = true) {
287
+ if (showpicker) {
288
+ this.positionPicker();
289
+ this.datepicker.style.display = 'block';
290
+ } else {
291
+ this.datepicker.style.display = 'none';
292
+ this.hideSelectMonthYear();
293
+ return;
294
+ }
295
+ }
296
+
297
+ private positionPicker() {
298
+ this.datepicker.style.position = 'fixed';
299
+
300
+ let datepickerHeight = 250;
301
+ if (isMobileDevice()) {
302
+ this.datepicker.style.height = '400px';
303
+ this.datepicker.style.top = '50%';
304
+ this.datepicker.style.left = '50%';
305
+ this.datepicker.style.transform = 'translate(-50%, -50%)';
306
+ this.datepicker.style.width = '90%';
307
+ this.datepicker.style.maxWidth = '300px';
308
+ this.datepicker.style.fontSize = '16px';
309
+ this.datepicker.style.padding = '30px';
310
+ this.header.style.marginBottom = '20px';
311
+ let weekdaysSpans = this.weekdays.querySelectorAll('span');
312
+ let daysSpans = this.days.querySelectorAll('span');
313
+ weekdaysSpans.forEach(span => span.style.marginBottom = '10px');
314
+ daysSpans.forEach(span => span.style.marginBottom = '10px');
315
+ } else {
316
+ this.datepicker.style.height = datepickerHeight + 'px';
317
+ let dateInputRect = this.input.getBoundingClientRect();
318
+ datepickerHeight += 20;
319
+ if (window.innerHeight - dateInputRect.bottom < datepickerHeight) {
320
+ this.datepicker.style.top = 'auto';
321
+ this.datepicker.style.bottom = window.innerHeight - dateInputRect.top + 'px';
322
+ } else {
323
+ this.datepicker.style.bottom = 'auto';
324
+ this.datepicker.style.top = dateInputRect.bottom + 'px';
325
+ }
326
+ this.datepicker.style.left = dateInputRect.left + 'px';
327
+ }
328
+ }
329
+
330
+ private setDateForToday() {
331
+ this.input.value = formatDate(this.selectedDate, this.dateFormat);
332
+ this.showPicker(false);
333
+ }
334
+
335
+ private addEventListeners() {
336
+ this.input.addEventListener('click', () => this.showPicker(true));
337
+ this.input.addEventListener('focus', () => this.showPicker(true));
338
+ this.input.addEventListener('change', () => {
339
+ const date = new Date(this.input.value);
340
+ if (date.toString() !== 'Invalid Date') {
341
+ this.selectedDate = date;
342
+ this.selectedMonth = date.getMonth();
343
+ this.selectedYear = date.getFullYear();
344
+ this.selectedDay = date.getDate();
345
+ this.renderCalendar();
346
+ }
347
+ });
348
+
349
+ this.otherTriggers.forEach(trigger => {
350
+ trigger.addEventListener('click', () => this.showPicker(true));
351
+ });
352
+
353
+ document.onclick = (event: Event) => {
354
+ const elementsToNeglect = [this.input, this.datepicker, this.monthYear, this.weekdays, this.days, this.header, this.selectMonthYear];
355
+ const element = event.target as HTMLElement;
356
+ const isNeglected = elementsToNeglect.includes(element) || elementsToNeglect.includes(element.parentElement) || (element).classList.contains('select-month') || element === this.parentElement;
357
+
358
+ if (!isNeglected){
359
+ this.showPicker(false);
360
+ }
361
+ };
362
+ }
363
+ }
@@ -0,0 +1,101 @@
1
+ export const CustomDatepickerStyles = `
2
+ .datepicker {
3
+ position: fixed;
4
+ background-color: #fff;
5
+ display: none;
6
+ width: 200px;
7
+ border: 1px solid #ccc;
8
+ border-radius: 5px;
9
+ padding: 10px;
10
+ margin: 0 auto;
11
+ box-shadow: 0 0 10px 0 #ccc;
12
+ font: 12px Arial, sans-serif;
13
+ }
14
+
15
+ .header {
16
+ display: flex;
17
+ justify-content: space-between;
18
+ align-items: center;
19
+ margin-bottom: 10px;
20
+ }
21
+ .weekdays {
22
+ display: flex;
23
+ justify-content: space-between;
24
+ }
25
+ .weekdays span {
26
+ width: 14.28%;
27
+ text-align: center;
28
+ padding: 5px 0;
29
+ }
30
+ .days {
31
+ display: flex;
32
+ flex-wrap: wrap;
33
+ }
34
+ .days span {
35
+ width: 14.28%;
36
+ text-align: center;
37
+ padding: 5px 0;
38
+ }
39
+ .days span:hover {
40
+ background-color: #B2d5ff;
41
+ cursor: pointer;
42
+ }
43
+ .days span.today {
44
+ border: 1px solid #000;
45
+ }
46
+ .days span.selected-day {
47
+ background-color: #0075ff;
48
+ color: #fff;
49
+ }
50
+ .prev-month, .next-month {
51
+ cursor: pointer;
52
+ }
53
+ .month-year {
54
+ font-weight: bold;
55
+ }
56
+ .month-year:hover {
57
+ cursor: pointer;
58
+ }
59
+ .select-month-year {
60
+ display: none;
61
+ width: 100%;
62
+ overflow-y: auto;
63
+ }
64
+ .select-month-year .year {
65
+ text-align: center;
66
+ background-color: #f0f0f0;
67
+ border-bottom: 1px solid #3f3d3d;
68
+ padding: 5px 0;
69
+ cursor: pointer;
70
+ }
71
+ .select-month-year .year .months {
72
+ display: flex;
73
+ flex-wrap: wrap;
74
+ }
75
+ .select-month-year .year .months span {
76
+ width: 25%;
77
+ text-align: center;
78
+ padding: 10px 0;
79
+ background-color: #fff;
80
+ }
81
+ .select-month-year .year .months span:hover {
82
+ background-color: #B2d5ff;
83
+ cursor: pointer;
84
+ }
85
+ .actions {
86
+ display: flex;
87
+ justify-content: space-between;
88
+ margin-top: 10px;
89
+ }
90
+ .actions input {
91
+ color: #0075ff;
92
+ border: none;
93
+ background-color: transparent;
94
+ padding: 5px;
95
+ border: 1px solid transparent;
96
+ }
97
+ .actions input:hover {
98
+ background-color: #B2d5ff;
99
+ border-color: #000;
100
+ }
101
+ `;
@@ -0,0 +1,44 @@
1
+ export const months: string[] = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
2
+ export const daysOfWeek: string[] = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
3
+
4
+ export const defaultDateFormat = 'yyyy-mm-dd';
5
+ export const supportedDateFormats = [
6
+ 'ddmmyyyy',
7
+ 'mmddyyyy',
8
+ 'dd/mm/yyyy',
9
+ 'mm/dd/yyyy',
10
+ 'dd-mm-yyyy',
11
+ 'mm-dd-yyyy',
12
+ 'yyyy-mm-dd',
13
+ 'yyyy-dd-mm',
14
+ 'Month dd, yyyy',
15
+ 'mm/dd/yy',
16
+ 'dd/mm/yy',
17
+ ];
18
+
19
+ export function formatDate(date: Date, dateFormat: string): string {
20
+ const year = date.getFullYear();
21
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
22
+ const day = date.getDate().toString().padStart(2, '0');
23
+ const monthName = date.toLocaleString('default', { month: 'long' });
24
+
25
+ const dateFormats = {
26
+ 'ddmmyyyy': `${day}${month}${year}`,
27
+ 'mmddyyyy': `${month}${day}${year}`,
28
+ 'dd/mm/yyyy': `${day}/${month}/${year}`,
29
+ 'mm/dd/yyyy': `${month}/${day}/${year}`,
30
+ 'dd-mm-yyyy': `${day}-${month}-${year}`,
31
+ 'mm-dd-yyyy': `${month}-${day}-${year}`,
32
+ 'yyyy-mm-dd': `${year}-${month}-${day}`,
33
+ 'yyyy-dd-mm': `${year}-${day}-${month}`,
34
+ 'Month dd, yyyy': `${monthName} ${day}, ${year}`,
35
+ 'mm/dd/yy': `${month}/${day}/${year.toString().slice(-2)}`,
36
+ 'dd/mm/yy': `${day}/${month}/${year.toString().slice(-2)}`,
37
+ }
38
+
39
+ return dateFormats[dateFormat];
40
+ }
41
+
42
+ export function isMobileDevice() {
43
+ return /Android|webOS|iPhone|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth < 480;
44
+ }