v-calendar-3 1.0.0

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 (147) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +142 -0
  3. package/dist/cjs/index.css +1133 -0
  4. package/dist/cjs/index.js +10699 -0
  5. package/dist/cjs/index.js.map +1 -0
  6. package/dist/cjs/package.json +1 -0
  7. package/dist/es/index.js +10678 -0
  8. package/dist/es/index.js.map +1 -0
  9. package/dist/es/style.css +1134 -0
  10. package/dist/iife/index.js +2 -0
  11. package/dist/iife/index.js.map +1 -0
  12. package/dist/mjs/index.mjs +10678 -0
  13. package/dist/mjs/index.mjs.map +1 -0
  14. package/dist/mjs/style.css +1134 -0
  15. package/dist/style.css +1134 -0
  16. package/dist/types/components/BaseIcon/BaseIcon.vue.d.ts +41 -0
  17. package/dist/types/components/BaseIcon/icons/IconChevronDown.vue.d.ts +3 -0
  18. package/dist/types/components/BaseIcon/icons/IconChevronLeft.vue.d.ts +3 -0
  19. package/dist/types/components/BaseIcon/icons/IconChevronRight.vue.d.ts +3 -0
  20. package/dist/types/components/BaseIcon/icons/IconClock.vue.d.ts +3 -0
  21. package/dist/types/components/BaseIcon/icons/index.d.ts +4 -0
  22. package/dist/types/components/BaseSelect/BaseSelect.vue.d.ts +26 -0
  23. package/dist/types/components/Calendar/Calendar.vue.d.ts +2526 -0
  24. package/dist/types/components/Calendar/CalendarDay.vue.d.ts +83 -0
  25. package/dist/types/components/Calendar/CalendarDayPopover.vue.d.ts +3 -0
  26. package/dist/types/components/Calendar/CalendarHeader.vue.d.ts +13 -0
  27. package/dist/types/components/Calendar/CalendarNav.vue.d.ts +4 -0
  28. package/dist/types/components/Calendar/CalendarNavPopover.vue.d.ts +3 -0
  29. package/dist/types/components/Calendar/CalendarPage.vue.d.ts +3 -0
  30. package/dist/types/components/Calendar/CalendarPageProvider.vue.d.ts +17 -0
  31. package/dist/types/components/Calendar/CalendarSlot.vue.d.ts +15 -0
  32. package/dist/types/components/Calendar/CalendarTitleSelect.vue.d.ts +15 -0
  33. package/dist/types/components/CalendarGrid/CalendarCell.vue.d.ts +9 -0
  34. package/dist/types/components/CalendarGrid/CalendarCellPopover.vue.d.ts +209 -0
  35. package/dist/types/components/CalendarGrid/CalendarDayCell.vue.d.ts +8 -0
  36. package/dist/types/components/CalendarGrid/CalendarEventDetails.vue.d.ts +15 -0
  37. package/dist/types/components/CalendarGrid/CalendarEventEdit.vue.d.ts +26 -0
  38. package/dist/types/components/CalendarGrid/CalendarGrid.vue.d.ts +134 -0
  39. package/dist/types/components/CalendarGrid/CalendarGridWeek.vue.d.ts +8 -0
  40. package/dist/types/components/CalendarGrid/CalendarViewSelect.vue.d.ts +3 -0
  41. package/dist/types/components/CalendarGrid/CalendarWeekCell.vue.d.ts +10 -0
  42. package/dist/types/components/CalendarGrid/Constraints.d.ts +16 -0
  43. package/dist/types/components/DatePicker/DatePicker.vue.d.ts +15251 -0
  44. package/dist/types/components/DatePicker/DatePickerBase.vue.d.ts +3 -0
  45. package/dist/types/components/DatePicker/DatePickerPopover.vue.d.ts +3 -0
  46. package/dist/types/components/DatePicker/TimePicker.vue.d.ts +10089 -0
  47. package/dist/types/components/Popover/Popover.vue.d.ts +72 -0
  48. package/dist/types/components/Popover/PopoverRow.vue.d.ts +17 -0
  49. package/dist/types/components/index.d.ts +4 -0
  50. package/dist/types/index.d.cts +40981 -0
  51. package/dist/types/index.d.mts +40981 -0
  52. package/dist/types/index.d.ts +40981 -0
  53. package/dist/types/use/base.d.ts +67 -0
  54. package/dist/types/use/calendar.d.ts +2422 -0
  55. package/dist/types/use/calendarGrid.d.ts +9370 -0
  56. package/dist/types/use/datePicker.d.ts +20210 -0
  57. package/dist/types/use/page.d.ts +20 -0
  58. package/dist/types/use/slots.d.ts +5 -0
  59. package/dist/types/use/timePicker.d.ts +10089 -0
  60. package/dist/types/utils/attribute.d.ts +49 -0
  61. package/dist/types/utils/cache.d.ts +10 -0
  62. package/dist/types/utils/calendar/event.d.ts +111 -0
  63. package/dist/types/utils/config/index.d.ts +5 -0
  64. package/dist/types/utils/date/helpers.d.ts +161 -0
  65. package/dist/types/utils/date/range.d.ts +74 -0
  66. package/dist/types/utils/date/repeat.d.ts +34 -0
  67. package/dist/types/utils/date/rules.d.ts +82 -0
  68. package/dist/types/utils/defaults/index.d.ts +30 -0
  69. package/dist/types/utils/defaults/locales.d.ts +9 -0
  70. package/dist/types/utils/glyph.d.ts +58 -0
  71. package/dist/types/utils/helpers.d.ts +49 -0
  72. package/dist/types/utils/locale.d.ts +48 -0
  73. package/dist/types/utils/page.d.ts +95 -0
  74. package/dist/types/utils/plugins/index.d.ts +4 -0
  75. package/dist/types/utils/popovers.d.ts +47 -0
  76. package/dist/types/utils/theme.d.ts +17 -0
  77. package/dist/types/utils/touch.d.ts +8 -0
  78. package/dist/types/utils/useDisplayMode.d.ts +12 -0
  79. package/dist/types/utils/watchers.d.ts +6 -0
  80. package/package.json +115 -0
  81. package/src/components/BaseIcon/BaseIcon.vue +29 -0
  82. package/src/components/BaseIcon/icons/IconChevronDown.vue +5 -0
  83. package/src/components/BaseIcon/icons/IconChevronLeft.vue +5 -0
  84. package/src/components/BaseIcon/icons/IconChevronRight.vue +5 -0
  85. package/src/components/BaseIcon/icons/IconClock.vue +11 -0
  86. package/src/components/BaseIcon/icons/index.ts +4 -0
  87. package/src/components/BaseSelect/BaseSelect.vue +136 -0
  88. package/src/components/Calendar/Calendar.vue +116 -0
  89. package/src/components/Calendar/CalendarDay.vue +422 -0
  90. package/src/components/Calendar/CalendarDayPopover.vue +44 -0
  91. package/src/components/Calendar/CalendarHeader.vue +203 -0
  92. package/src/components/Calendar/CalendarNav.vue +325 -0
  93. package/src/components/Calendar/CalendarNavPopover.vue +24 -0
  94. package/src/components/Calendar/CalendarPage.vue +140 -0
  95. package/src/components/Calendar/CalendarPageProvider.vue +15 -0
  96. package/src/components/Calendar/CalendarSlot.vue +36 -0
  97. package/src/components/Calendar/CalendarTitleSelect.vue +72 -0
  98. package/src/components/CalendarGrid/CalendarCell.vue +112 -0
  99. package/src/components/CalendarGrid/CalendarCellPopover.vue +93 -0
  100. package/src/components/CalendarGrid/CalendarDayCell.vue +57 -0
  101. package/src/components/CalendarGrid/CalendarEventDetails.vue +71 -0
  102. package/src/components/CalendarGrid/CalendarEventEdit.vue +195 -0
  103. package/src/components/CalendarGrid/CalendarGrid.vue +715 -0
  104. package/src/components/CalendarGrid/CalendarGridWeek.vue +60 -0
  105. package/src/components/CalendarGrid/CalendarViewSelect.vue +34 -0
  106. package/src/components/CalendarGrid/CalendarWeekCell.vue +52 -0
  107. package/src/components/CalendarGrid/Constraints.ts +40 -0
  108. package/src/components/DatePicker/DatePicker.vue +27 -0
  109. package/src/components/DatePicker/DatePickerBase.vue +53 -0
  110. package/src/components/DatePicker/DatePickerPopover.vue +37 -0
  111. package/src/components/DatePicker/TimePicker.vue +167 -0
  112. package/src/components/Popover/Popover.vue +572 -0
  113. package/src/components/Popover/PopoverRow.vue +94 -0
  114. package/src/components/index.ts +4 -0
  115. package/src/index.ts +24 -0
  116. package/src/styles/index.css +48 -0
  117. package/src/styles/theme.css +322 -0
  118. package/src/styles/transitions.css +77 -0
  119. package/src/use/base.ts +144 -0
  120. package/src/use/calendar.ts +796 -0
  121. package/src/use/calendarGrid.ts +954 -0
  122. package/src/use/datePicker.ts +909 -0
  123. package/src/use/page.ts +78 -0
  124. package/src/use/slots.ts +17 -0
  125. package/src/use/timePicker.ts +194 -0
  126. package/src/utils/attribute.ts +107 -0
  127. package/src/utils/cache.ts +30 -0
  128. package/src/utils/calendar/event.ts +335 -0
  129. package/src/utils/config/index.ts +11 -0
  130. package/src/utils/date/helpers.ts +1039 -0
  131. package/src/utils/date/range.ts +274 -0
  132. package/src/utils/date/repeat.ts +135 -0
  133. package/src/utils/date/rules.ts +290 -0
  134. package/src/utils/defaults/index.ts +77 -0
  135. package/src/utils/defaults/locales.ts +123 -0
  136. package/src/utils/defaults/masks.json +15 -0
  137. package/src/utils/defaults/touch.json +5 -0
  138. package/src/utils/glyph.ts +305 -0
  139. package/src/utils/helpers.ts +181 -0
  140. package/src/utils/locale.ts +275 -0
  141. package/src/utils/page.ts +608 -0
  142. package/src/utils/plugins/index.ts +24 -0
  143. package/src/utils/popovers.ts +216 -0
  144. package/src/utils/theme.ts +58 -0
  145. package/src/utils/touch.ts +72 -0
  146. package/src/utils/useDisplayMode.ts +198 -0
  147. package/src/utils/watchers.ts +26 -0
@@ -0,0 +1,1039 @@
1
+ import {
2
+ pad,
3
+ isNumber,
4
+ isString,
5
+ isDate,
6
+ isArray,
7
+ arrayHasItems,
8
+ isFunction,
9
+ isObject,
10
+ } from '../helpers';
11
+ import {
12
+ addDays,
13
+ addMonths,
14
+ addYears,
15
+ getISOWeek,
16
+ getWeek,
17
+ getWeeksInMonth,
18
+ } from 'date-fns';
19
+ import { fromZonedTime, toZonedTime } from 'date-fns-tz';
20
+ import { type LocaleConfig, default as Locale } from '../locale';
21
+
22
+ export { addDays, addMonths, addYears };
23
+ export { DateRepeat } from './repeat';
24
+
25
+ // #region Types
26
+
27
+ type DayNameLength = 'narrow' | 'short' | 'long';
28
+ type MonthNameLength = 'short' | 'long';
29
+
30
+ export type DayInMonth =
31
+ | 1
32
+ | 2
33
+ | 3
34
+ | 4
35
+ | 5
36
+ | 6
37
+ | 7
38
+ | 8
39
+ | 9
40
+ | 10
41
+ | 11
42
+ | 12
43
+ | 13
44
+ | 14
45
+ | 15
46
+ | 16
47
+ | 17
48
+ | 18
49
+ | 19
50
+ | 20
51
+ | 21
52
+ | 22
53
+ | 23
54
+ | 24
55
+ | 25
56
+ | 26
57
+ | 27
58
+ | 28
59
+ | 29
60
+ | 30
61
+ | 31
62
+ | -1
63
+ | -2
64
+ | -3
65
+ | -4
66
+ | -5
67
+ | -6
68
+ | -7
69
+ | -8
70
+ | -9
71
+ | -10
72
+ | -11
73
+ | -12
74
+ | -13
75
+ | -14
76
+ | -15
77
+ | -16
78
+ | -17
79
+ | -18
80
+ | -19
81
+ | -20
82
+ | -21
83
+ | -22
84
+ | -23
85
+ | -24
86
+ | -25
87
+ | -26
88
+ | -27
89
+ | -28
90
+ | -29
91
+ | -30
92
+ | -31;
93
+ export type DayOfWeek = 1 | 2 | 3 | 4 | 5 | 6 | 7;
94
+ export type WeekInMonth = 1 | 2 | 3 | 4 | 5 | 6;
95
+ export type WeekInMonthFromEnd = -6 | -5 | -4 | -3 | -2 | -1;
96
+ export type OrdinalWeekInMonth = -5 | -4 | -3 | -2 | -1 | 1 | 2 | 3 | 4 | 5;
97
+ export type MonthInYear = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
98
+ export type StartOfWeek = 1 | 2 | 3 | 4 | 5 | 6 | 7;
99
+
100
+ export type WeekStartsOn = 0 | 1 | 2 | 3 | 4 | 5 | 6;
101
+ export type DateSource = Date | string | number;
102
+ export type TimeNames = Partial<Record<Intl.RelativeTimeFormatUnit, string>>;
103
+
104
+ // #endregion Types
105
+
106
+ export function isDayInMonth(dayInMonth: unknown): dayInMonth is DayInMonth {
107
+ if (!isNumber(dayInMonth)) return false;
108
+ return dayInMonth >= 1 && dayInMonth <= 31;
109
+ }
110
+
111
+ export function isDayOfWeek(dayOfWeek: unknown): dayOfWeek is DayOfWeek {
112
+ if (!isNumber(dayOfWeek)) return false;
113
+ return dayOfWeek >= 1 && dayOfWeek <= 7;
114
+ }
115
+
116
+ export function isWeekInMonth(
117
+ weekInMonth: unknown,
118
+ ): weekInMonth is WeekInMonth {
119
+ if (!isNumber(weekInMonth)) return false;
120
+ return (
121
+ (weekInMonth >= -6 && weekInMonth <= -1) ||
122
+ (weekInMonth >= 1 && weekInMonth <= 6)
123
+ );
124
+ }
125
+
126
+ export function isMonthInYear(
127
+ monthInYear: unknown,
128
+ ): monthInYear is MonthInYear {
129
+ if (!isNumber(monthInYear)) return false;
130
+ return monthInYear >= 1 && monthInYear <= 12;
131
+ }
132
+
133
+ export function isOrdinalWeekInMonth(
134
+ weekInMonth: unknown,
135
+ ): weekInMonth is OrdinalWeekInMonth {
136
+ if (!isNumber(weekInMonth)) return false;
137
+ if (weekInMonth < -5 || weekInMonth > 5 || weekInMonth === 0) return false;
138
+ return true;
139
+ }
140
+
141
+ interface NumberRuleConfig {
142
+ min?: number;
143
+ max?: number;
144
+ interval?: number;
145
+ }
146
+
147
+ type DatePartsRuleFunction = (part: number, parts: TimeParts) => boolean;
148
+
149
+ type DatePartsRule =
150
+ number | Array<number> | NumberRuleConfig | DatePartsRuleFunction;
151
+
152
+ export interface DatePartsRules {
153
+ hours?: DatePartsRule;
154
+ minutes?: DatePartsRule;
155
+ seconds?: DatePartsRule;
156
+ milliseconds?: DatePartsRule;
157
+ }
158
+
159
+ export interface DatePartOption {
160
+ value: number;
161
+ label: string;
162
+ disabled?: boolean;
163
+ }
164
+
165
+ export interface FormatParseOptions {
166
+ locale?: Locale | LocaleConfig | string;
167
+ timezone?: string;
168
+ }
169
+
170
+ export interface DateOptions {
171
+ type: string;
172
+ fillDate: DateSource;
173
+ mask: string;
174
+ patch: DatePatch;
175
+ rules: DatePartsRules;
176
+ }
177
+
178
+ export interface PageAddress {
179
+ day?: number;
180
+ week?: number;
181
+ month: number;
182
+ year: number;
183
+ }
184
+
185
+ export interface TimeParts {
186
+ hours: number;
187
+ minutes: number;
188
+ seconds: number;
189
+ milliseconds: number;
190
+ }
191
+
192
+ export interface SimpleDateParts {
193
+ year: number;
194
+ month: number;
195
+ day: number;
196
+ hours: number;
197
+ minutes: number;
198
+ seconds: number;
199
+ milliseconds: number;
200
+ }
201
+
202
+ export interface DayParts {
203
+ dayIndex: number;
204
+ day: number;
205
+ dayFromEnd: number;
206
+ weekday: number;
207
+ weekdayOrdinal: number;
208
+ weekdayOrdinalFromEnd: number;
209
+ week: number;
210
+ weekFromEnd: number;
211
+ weeknumber: number;
212
+ month: number;
213
+ year: number;
214
+ date: Date;
215
+ }
216
+
217
+ export interface DateParts extends DayParts {
218
+ milliseconds: number;
219
+ seconds: number;
220
+ minutes: number;
221
+ hours: number;
222
+ time: number;
223
+ dateTime: number;
224
+ isValid: boolean;
225
+ timezoneOffset: number;
226
+ isPm?: boolean;
227
+ }
228
+
229
+ export interface MonthParts {
230
+ firstDayOfWeek: DayOfWeek;
231
+ firstDayOfMonth: Date;
232
+ inLeapYear: boolean;
233
+ firstWeekday: number;
234
+ numDays: number;
235
+ numWeeks: number;
236
+ month: number;
237
+ year: number;
238
+ weeknumbers: number[];
239
+ isoWeeknumbers: number[];
240
+ }
241
+
242
+ export type DatePatch = 'dateTime' | 'date' | 'time';
243
+
244
+ export const DatePatchKeys: Record<DatePatch, (keyof SimpleDateParts)[]> = {
245
+ dateTime: [
246
+ 'year',
247
+ 'month',
248
+ 'day',
249
+ 'hours',
250
+ 'minutes',
251
+ 'seconds',
252
+ 'milliseconds',
253
+ ],
254
+ date: ['year', 'month', 'day'],
255
+ time: ['hours', 'minutes', 'seconds', 'milliseconds'],
256
+ };
257
+
258
+ export const daysInWeek = 7;
259
+ export const weeksInMonth = 6;
260
+ export const MS_PER_SECOND = 1000;
261
+ export const MS_PER_MINUTE = MS_PER_SECOND * 60;
262
+ export const MS_PER_HOUR = MS_PER_MINUTE * 60;
263
+ export const MS_PER_DAY = MS_PER_HOUR * 24;
264
+
265
+ const daysInMonths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
266
+ const maskMacros = ['L', 'iso'];
267
+
268
+ type DatePartsRange = Readonly<[number, number, number]>;
269
+ type TimePartsKey = 'milliseconds' | 'seconds' | 'minutes' | 'hours';
270
+ const DATE_PART_RANGES: Record<TimePartsKey, DatePartsRange> = {
271
+ milliseconds: [0, 999, 3],
272
+ seconds: [0, 59, 2],
273
+ minutes: [0, 59, 2],
274
+ hours: [0, 23, 2],
275
+ } as const;
276
+
277
+ // #region Format constants
278
+
279
+ const token =
280
+ /d{1,2}|W{1,4}|M{1,4}|YY(?:YY)?|S{1,3}|Do|Z{1,4}|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g;
281
+ const literal = /\[([^]*?)\]/gm;
282
+ const formatFlags: any = {
283
+ D(d: DateParts) {
284
+ return d.day;
285
+ },
286
+ DD(d: DateParts) {
287
+ return pad(d.day, 2);
288
+ },
289
+ // Do(d: DateParts, l: Locale) {
290
+ // return l.DoFn(d.day);
291
+ // },
292
+ d(d: DateParts) {
293
+ return d.weekday - 1;
294
+ },
295
+ dd(d: DateParts) {
296
+ return pad(d.weekday - 1, 2);
297
+ },
298
+ W(d: DateParts, l: Locale) {
299
+ return l.dayNamesNarrow[d.weekday - 1];
300
+ },
301
+ WW(d: DateParts, l: Locale) {
302
+ return l.dayNamesShorter[d.weekday - 1];
303
+ },
304
+ WWW(d: DateParts, l: Locale) {
305
+ return l.dayNamesShort[d.weekday - 1];
306
+ },
307
+ WWWW(d: DateParts, l: Locale) {
308
+ return l.dayNames[d.weekday - 1];
309
+ },
310
+ M(d: DateParts) {
311
+ return d.month;
312
+ },
313
+ MM(d: DateParts) {
314
+ return pad(d.month, 2);
315
+ },
316
+ MMM(d: DateParts, l: Locale) {
317
+ return l.monthNamesShort[d.month - 1];
318
+ },
319
+ MMMM(d: DateParts, l: Locale) {
320
+ return l.monthNames[d.month - 1];
321
+ },
322
+ YY(d: DateParts) {
323
+ return String(d.year).substr(2);
324
+ },
325
+ YYYY(d: DateParts) {
326
+ return pad(d.year, 4);
327
+ },
328
+ h(d: DateParts) {
329
+ return d.hours % 12 || 12;
330
+ },
331
+ hh(d: DateParts) {
332
+ return pad(d.hours % 12 || 12, 2);
333
+ },
334
+ H(d: DateParts) {
335
+ return d.hours;
336
+ },
337
+ HH(d: DateParts) {
338
+ return pad(d.hours, 2);
339
+ },
340
+ m(d: DateParts) {
341
+ return d.minutes;
342
+ },
343
+ mm(d: DateParts) {
344
+ return pad(d.minutes, 2);
345
+ },
346
+ s(d: DateParts) {
347
+ return d.seconds;
348
+ },
349
+ ss(d: DateParts) {
350
+ return pad(d.seconds, 2);
351
+ },
352
+ S(d: DateParts) {
353
+ return Math.round(d.milliseconds / 100);
354
+ },
355
+ SS(d: DateParts) {
356
+ return pad(Math.round(d.milliseconds / 10), 2);
357
+ },
358
+ SSS(d: DateParts) {
359
+ return pad(d.milliseconds, 3);
360
+ },
361
+ a(d: DateParts, l: Locale) {
362
+ return d.hours < 12 ? l.amPm[0] : l.amPm[1];
363
+ },
364
+ A(d: DateParts, l: Locale) {
365
+ return d.hours < 12 ? l.amPm[0].toUpperCase() : l.amPm[1].toUpperCase();
366
+ },
367
+ Z() {
368
+ return 'Z';
369
+ },
370
+ ZZ(d: DateParts) {
371
+ const o = d.timezoneOffset;
372
+ return `${o > 0 ? '-' : '+'}${pad(Math.floor(Math.abs(o) / 60), 2)}`;
373
+ },
374
+ ZZZ(d: DateParts) {
375
+ const o = d.timezoneOffset;
376
+ return `${o > 0 ? '-' : '+'}${pad(
377
+ Math.floor(Math.abs(o) / 60) * 100 + (Math.abs(o) % 60),
378
+ 4,
379
+ )}`;
380
+ },
381
+ ZZZZ(d: DateParts) {
382
+ const o = d.timezoneOffset;
383
+ return `${o > 0 ? '-' : '+'}${pad(Math.floor(Math.abs(o) / 60), 2)}:${pad(
384
+ Math.abs(o) % 60,
385
+ 2,
386
+ )}`;
387
+ },
388
+ };
389
+
390
+ // #endregion Format constants
391
+
392
+ // #region Parse constants
393
+
394
+ const twoDigits = /\d\d?/;
395
+ const threeDigits = /\d{3}/;
396
+ const fourDigits = /\d{4}/;
397
+ const word =
398
+ /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
399
+ const noop = () => {};
400
+ const monthUpdate = (arrName: string) => (d: DateParts, v: string, l: any) => {
401
+ const index = l[arrName].indexOf(
402
+ v.charAt(0).toUpperCase() + v.substr(1).toLowerCase(),
403
+ );
404
+ if (~index) {
405
+ d.month = index;
406
+ }
407
+ };
408
+ const parseFlags: any = {
409
+ D: [
410
+ twoDigits,
411
+ (d: DateParts, v: number) => {
412
+ d.day = v;
413
+ },
414
+ ],
415
+ Do: [
416
+ new RegExp(twoDigits.source + word.source),
417
+ (d: DateParts, v: string) => {
418
+ d.day = parseInt(v, 10);
419
+ },
420
+ ],
421
+ d: [twoDigits, noop],
422
+ W: [word, noop],
423
+ M: [
424
+ twoDigits,
425
+ (d: DateParts, v: number) => {
426
+ d.month = v - 1;
427
+ },
428
+ ],
429
+ MMM: [word, monthUpdate('monthNamesShort')],
430
+ MMMM: [word, monthUpdate('monthNames')],
431
+ YY: [
432
+ twoDigits,
433
+ (d: DateParts, v: number) => {
434
+ const da = new Date();
435
+ const cent = +da.getFullYear().toString().substr(0, 2);
436
+ d.year = +`${v > 68 ? cent - 1 : cent}${v}`;
437
+ },
438
+ ],
439
+ YYYY: [
440
+ fourDigits,
441
+ (d: DateParts, v: number) => {
442
+ d.year = v;
443
+ },
444
+ ],
445
+ S: [
446
+ /\d/,
447
+ (d: DateParts, v: number) => {
448
+ d.milliseconds = v * 100;
449
+ },
450
+ ],
451
+ SS: [
452
+ /\d{2}/,
453
+ (d: DateParts, v: number) => {
454
+ d.milliseconds = v * 10;
455
+ },
456
+ ],
457
+ SSS: [
458
+ threeDigits,
459
+ (d: DateParts, v: number) => {
460
+ d.milliseconds = v;
461
+ },
462
+ ],
463
+ h: [
464
+ twoDigits,
465
+ (d: DateParts, v: number) => {
466
+ d.hours = v;
467
+ },
468
+ ],
469
+ m: [
470
+ twoDigits,
471
+ (d: DateParts, v: number) => {
472
+ d.minutes = v;
473
+ },
474
+ ],
475
+ s: [
476
+ twoDigits,
477
+ (d: DateParts, v: number) => {
478
+ d.seconds = v;
479
+ },
480
+ ],
481
+ a: [
482
+ word,
483
+ (d: DateParts, v: string, l: Locale) => {
484
+ const val = v.toLowerCase();
485
+ if (val === l.amPm[0]) {
486
+ d.isPm = false;
487
+ } else if (val === l.amPm[1]) {
488
+ d.isPm = true;
489
+ }
490
+ },
491
+ ],
492
+ Z: [
493
+ /[^\s]*?[+-]\d\d:?\d\d|[^\s]*?Z?/,
494
+ (d: DateParts, v: string) => {
495
+ if (v === 'Z') v = '+00:00';
496
+ const parts = `${v}`.match(/([+-]|\d\d)/gi);
497
+ if (parts) {
498
+ const minutes = +parts[1] * 60 + parseInt(parts[2], 10);
499
+ d.timezoneOffset = parts[0] === '+' ? minutes : -minutes;
500
+ }
501
+ },
502
+ ],
503
+ };
504
+ parseFlags.DD = parseFlags.D;
505
+ parseFlags.dd = parseFlags.d;
506
+ parseFlags.WWWW = parseFlags.WWW = parseFlags.WW = parseFlags.W;
507
+ parseFlags.MM = parseFlags.M;
508
+ parseFlags.mm = parseFlags.m;
509
+ parseFlags.hh = parseFlags.H = parseFlags.HH = parseFlags.h;
510
+ parseFlags.ss = parseFlags.s;
511
+ parseFlags.A = parseFlags.a;
512
+ parseFlags.ZZZZ = parseFlags.ZZZ = parseFlags.ZZ = parseFlags.Z;
513
+
514
+ // #endregion Parse constants
515
+
516
+ function normalizeMasks(masks: string | string[], locale: Locale): string[] {
517
+ return (
518
+ ((arrayHasItems(masks) && masks) || [
519
+ (isString(masks) && masks) || 'YYYY-MM-DD',
520
+ ]) as string[]
521
+ ).map(m =>
522
+ maskMacros.reduce(
523
+ (prev, curr) => prev.replace(curr, locale.masks[curr] || ''),
524
+ m,
525
+ ),
526
+ );
527
+ }
528
+
529
+ export function isDateParts(parts: unknown): parts is Partial<DateParts> {
530
+ return (
531
+ isObject(parts) && 'year' in parts && 'month' in parts && 'day' in parts
532
+ );
533
+ }
534
+
535
+ export function isDateSource(date: unknown): date is DateSource {
536
+ if (date == null) return false;
537
+ return isString(date) || isNumber(date) || isDate(date);
538
+ }
539
+
540
+ export function roundDate(dateMs: number, snapMs = 0) {
541
+ if (snapMs > 0) return new Date(Math.round(dateMs / snapMs) * snapMs);
542
+ return new Date(dateMs);
543
+ }
544
+
545
+ export function startOfWeek(date: Date, firstDayOfWeek: DayOfWeek = 1) {
546
+ const day = date.getDay() + 1;
547
+ const daysToAdd =
548
+ day >= firstDayOfWeek
549
+ ? firstDayOfWeek - day
550
+ : -(7 - (firstDayOfWeek - day));
551
+ return addDays(date, daysToAdd);
552
+ }
553
+
554
+ export function getStartOfWeek(date: Date, firstDayOfWeek: DayOfWeek = 1) {
555
+ const day = date.getDay() + 1;
556
+ const daysToAdd =
557
+ day >= firstDayOfWeek
558
+ ? firstDayOfWeek - day
559
+ : -(7 - (firstDayOfWeek - day));
560
+ return addDays(date, daysToAdd);
561
+ }
562
+
563
+ export function getDayIndex(year: number, month: number, day: number) {
564
+ const utcDate = Date.UTC(year, month - 1, day);
565
+ return diffInDays(new Date(0), new Date(utcDate));
566
+ }
567
+
568
+ export function diffInDays(d1: Date, d2: Date) {
569
+ return Math.round((d2.getTime() - d1.getTime()) / MS_PER_DAY);
570
+ }
571
+
572
+ export function diffInWeeks(d1: Date, d2: Date) {
573
+ return Math.ceil(diffInDays(startOfWeek(d1), startOfWeek(d2)) / 7);
574
+ }
575
+
576
+ export function diffInYears(d1: Date, d2: Date) {
577
+ return d2.getUTCFullYear() - d1.getUTCFullYear();
578
+ }
579
+
580
+ export function diffInMonths(d1: Date, d2: Date) {
581
+ return diffInYears(d1, d2) * 12 + (d2.getMonth() - d1.getMonth());
582
+ }
583
+
584
+ export function getDateFromParts(
585
+ parts: Partial<SimpleDateParts>,
586
+ timezone = '',
587
+ ) {
588
+ const d = new Date();
589
+ const {
590
+ year = d.getFullYear(),
591
+ month = d.getMonth() + 1,
592
+ day = d.getDate(),
593
+ hours: hrs = 0,
594
+ minutes: min = 0,
595
+ seconds: sec = 0,
596
+ milliseconds: ms = 0,
597
+ } = parts;
598
+
599
+ if (timezone) {
600
+ const localDate = new Date(year, month - 1, day, hrs, min, sec, ms);
601
+ return fromZonedTime(localDate, timezone);
602
+ }
603
+ return new Date(year, month - 1, day, hrs, min, sec, ms);
604
+ }
605
+
606
+ export function getTimezoneOffset(
607
+ parts: Partial<SimpleDateParts>,
608
+ timezone = '',
609
+ ) {
610
+ const {
611
+ year: y = 0,
612
+ month: m = 0,
613
+ day: d = 0,
614
+ hours: hrs = 0,
615
+ minutes: min = 0,
616
+ seconds: sec = 0,
617
+ milliseconds: ms = 0,
618
+ } = parts;
619
+ let date;
620
+ const utcDate = new Date(Date.UTC(y, m - 1, d, hrs, min, sec, ms));
621
+ if (timezone) {
622
+ date = fromZonedTime(new Date(y, m - 1, d, hrs, min, sec, ms), timezone);
623
+ } else {
624
+ date = new Date(y, m - 1, d, hrs, min, sec, ms);
625
+ }
626
+ return (date.getTime() - utcDate.getTime()) / 60000;
627
+ }
628
+
629
+ export function getDateParts(
630
+ date: Date,
631
+ locale: Locale,
632
+ timezone = locale.timezone,
633
+ ): DateParts {
634
+ const tzDate = timezone
635
+ ? toZonedTime(date, timezone)
636
+ : new Date(date.getTime());
637
+ const milliseconds = tzDate.getMilliseconds();
638
+ const seconds = tzDate.getSeconds();
639
+ const minutes = tzDate.getMinutes();
640
+ const hours = tzDate.getHours();
641
+ const time =
642
+ milliseconds +
643
+ seconds * MS_PER_SECOND +
644
+ minutes * MS_PER_MINUTE +
645
+ hours * MS_PER_HOUR;
646
+ const month = <MonthInYear>(tzDate.getMonth() + 1);
647
+ const year = tzDate.getFullYear();
648
+ const monthParts = locale.getMonthParts(month, year);
649
+ const day = <DayInMonth>tzDate.getDate();
650
+ const dayFromEnd = monthParts.numDays - day + 1;
651
+ const weekday = tzDate.getDay() + 1;
652
+ const weekdayOrdinal = Math.floor((day - 1) / 7 + 1);
653
+ const weekdayOrdinalFromEnd = Math.floor((monthParts.numDays - day) / 7 + 1);
654
+ const week = Math.ceil(
655
+ (day + Math.abs(monthParts.firstWeekday - monthParts.firstDayOfWeek)) / 7,
656
+ );
657
+ const weekFromEnd = monthParts.numWeeks - week + 1;
658
+ const weeknumber = monthParts.weeknumbers[week];
659
+ const dayIndex = getDayIndex(year, month, day);
660
+ const parts: DateParts = {
661
+ milliseconds,
662
+ seconds,
663
+ minutes,
664
+ hours,
665
+ time,
666
+ day,
667
+ dayFromEnd,
668
+ weekday,
669
+ weekdayOrdinal,
670
+ weekdayOrdinalFromEnd,
671
+ week,
672
+ weekFromEnd,
673
+ weeknumber,
674
+ month,
675
+ year,
676
+ date: tzDate,
677
+ dateTime: tzDate.getTime(),
678
+ dayIndex,
679
+ timezoneOffset: 0,
680
+ isValid: true,
681
+ };
682
+ return parts;
683
+ }
684
+
685
+ export function getMonthPartsKey(
686
+ month: number,
687
+ year: number,
688
+ firstDayOfWeek: DayOfWeek,
689
+ ) {
690
+ return `${year}-${month}-${firstDayOfWeek}`;
691
+ }
692
+
693
+ export function getMonthParts(
694
+ month: number,
695
+ year: number,
696
+ firstDayOfWeek: DayOfWeek,
697
+ ) {
698
+ const inLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
699
+ const firstDayOfMonth = new Date(year, month - 1, 1);
700
+ const firstWeekday = firstDayOfMonth.getDay() + 1;
701
+ const numDays = month === 2 && inLeapYear ? 29 : daysInMonths[month - 1];
702
+ const weekStartsOn: WeekStartsOn = (firstDayOfWeek - 1) as WeekStartsOn;
703
+ const numWeeks = getWeeksInMonth(firstDayOfMonth, {
704
+ weekStartsOn,
705
+ });
706
+ const weeknumbers = [];
707
+ const isoWeeknumbers = [];
708
+ for (let i = 0; i < numWeeks; i++) {
709
+ const date = addDays(firstDayOfMonth, i * 7);
710
+ weeknumbers.push(getWeek(date, { weekStartsOn }));
711
+ isoWeeknumbers.push(getISOWeek(date));
712
+ }
713
+ return {
714
+ firstDayOfWeek,
715
+ firstDayOfMonth,
716
+ inLeapYear,
717
+ firstWeekday,
718
+ numDays,
719
+ numWeeks,
720
+ month,
721
+ year,
722
+ weeknumbers,
723
+ isoWeeknumbers,
724
+ };
725
+ }
726
+
727
+ export function getWeekdayDates() {
728
+ const dates = [];
729
+ const year = 2020;
730
+ const month = 1;
731
+ const day = 5;
732
+ for (let i = 0; i < daysInWeek; i++) {
733
+ dates.push(
734
+ getDateFromParts({
735
+ year,
736
+ month,
737
+ day: day + i,
738
+ hours: 12,
739
+ }),
740
+ );
741
+ }
742
+ return dates;
743
+ }
744
+
745
+ export function getDayNames(
746
+ length: DayNameLength,
747
+ localeId: string | undefined = undefined,
748
+ ) {
749
+ const dtf = new Intl.DateTimeFormat(localeId, {
750
+ weekday: length,
751
+ });
752
+ return getWeekdayDates().map(d => dtf.format(d));
753
+ }
754
+
755
+ export function getHourDates() {
756
+ const dates = [];
757
+ for (let i = 0; i <= 24; i++) {
758
+ dates.push(new Date(2000, 0, 1, i));
759
+ }
760
+ return dates;
761
+ }
762
+
763
+ export function getRelativeTimeNames(localeId = undefined): TimeNames {
764
+ const units: Intl.RelativeTimeFormatUnit[] = [
765
+ 'second',
766
+ 'minute',
767
+ 'hour',
768
+ 'day',
769
+ 'week',
770
+ 'month',
771
+ 'quarter',
772
+ 'year',
773
+ ];
774
+ const rtf = new Intl.RelativeTimeFormat(localeId);
775
+ return units.reduce<TimeNames>((names, unit) => {
776
+ const parts = rtf.formatToParts(100, unit);
777
+ // @ts-ignore
778
+ names[unit] = parts[1].unit;
779
+ return names;
780
+ }, {});
781
+ }
782
+
783
+ export function getMonthDates() {
784
+ const dates = [];
785
+ for (let i = 0; i < 12; i++) {
786
+ dates.push(new Date(2000, i, 15));
787
+ }
788
+ return dates;
789
+ }
790
+
791
+ export function getMonthNames(length: MonthNameLength, localeId = undefined) {
792
+ const dtf = new Intl.DateTimeFormat(localeId, {
793
+ month: length,
794
+ timeZone: 'UTC',
795
+ });
796
+ return getMonthDates().map(d => dtf.format(d));
797
+ }
798
+
799
+ export function datePartIsValid(
800
+ part: number,
801
+ rule: DatePartsRule,
802
+ parts: DateParts,
803
+ ): boolean {
804
+ if (isNumber(rule)) return rule === part;
805
+ if (isArray(rule)) return (rule as number[]).includes(part);
806
+ if (isFunction(rule)) return rule(part, parts);
807
+ if (rule.min != null && rule.min > part) return false;
808
+ if (rule.max != null && rule.max < part) return false;
809
+ if (rule.interval != null && part % rule.interval !== 0) return false;
810
+ return true;
811
+ }
812
+
813
+ export function getDatePartOptions(
814
+ parts: DateParts,
815
+ range: DatePartsRange,
816
+ rule: DatePartsRule | undefined,
817
+ ) {
818
+ const options: DatePartOption[] = [];
819
+ const [min, max, padding] = range;
820
+ for (let i = min; i <= max; i++) {
821
+ if (rule == null || datePartIsValid(i, rule, parts)) {
822
+ options.push({
823
+ value: i,
824
+ label: pad(i, padding),
825
+ });
826
+ }
827
+ }
828
+ return options;
829
+ }
830
+
831
+ export function getDatePartsOptions(parts: DateParts, rules: DatePartsRules) {
832
+ return {
833
+ milliseconds: getDatePartOptions(
834
+ parts,
835
+ DATE_PART_RANGES.milliseconds,
836
+ rules.milliseconds,
837
+ ),
838
+ seconds: getDatePartOptions(parts, DATE_PART_RANGES.seconds, rules.seconds),
839
+ minutes: getDatePartOptions(parts, DATE_PART_RANGES.minutes, rules.minutes),
840
+ hours: getDatePartOptions(parts, DATE_PART_RANGES.hours, rules.hours),
841
+ };
842
+ }
843
+
844
+ export function getNearestDatePart(
845
+ parts: DateParts,
846
+ range: DatePartsRange,
847
+ value: number,
848
+ rule: DatePartsRule,
849
+ ) {
850
+ const options = getDatePartOptions(parts, range, rule);
851
+ const result = options.reduce((prev, opt) => {
852
+ if (opt.disabled) return prev;
853
+ if (isNaN(prev)) return opt.value;
854
+ const diffPrev = Math.abs(prev - value);
855
+ const diffCurr = Math.abs(opt.value - value);
856
+ return diffCurr < diffPrev ? opt.value : prev;
857
+ }, NaN);
858
+ return isNaN(result) ? value : result;
859
+ }
860
+
861
+ export function applyRulesForDateParts(
862
+ dateParts: DateParts,
863
+ rules: DatePartsRules,
864
+ ) {
865
+ const result = <DateParts>{ ...dateParts };
866
+ Object.entries(rules).forEach(([key, rule]) => {
867
+ const range = DATE_PART_RANGES[key as TimePartsKey];
868
+ const value = dateParts[key as TimePartsKey];
869
+ result[key as TimePartsKey] = getNearestDatePart(
870
+ dateParts,
871
+ range,
872
+ value,
873
+ rule,
874
+ );
875
+ });
876
+ return result;
877
+ }
878
+
879
+ export function parseDate(
880
+ dateString: string,
881
+ mask: string | string[],
882
+ locale: Locale,
883
+ ) {
884
+ const masks = normalizeMasks(mask, locale);
885
+ return (
886
+ masks
887
+ .map(m => {
888
+ if (typeof m !== 'string') {
889
+ throw new Error('Invalid mask');
890
+ }
891
+ // Reset string value
892
+ let str = dateString;
893
+ // Avoid regular expression denial of service, fail early for really long strings
894
+ // https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS
895
+ if (str.length > 1000) {
896
+ return false;
897
+ }
898
+
899
+ let isValid = true;
900
+ const dp: Partial<DateParts> = {};
901
+ m.replace(token, $0 => {
902
+ if (parseFlags[$0]) {
903
+ const info = parseFlags[$0];
904
+ const index = str.search(info[0]);
905
+ if (!~index) {
906
+ isValid = false;
907
+ } else {
908
+ str.replace(info[0], result => {
909
+ info[1](dp, result, locale);
910
+ str = str.substr(index + result.length);
911
+ return result;
912
+ });
913
+ }
914
+ }
915
+
916
+ return parseFlags[$0] ? '' : $0.slice(1, $0.length - 1);
917
+ });
918
+
919
+ if (!isValid) {
920
+ return false;
921
+ }
922
+
923
+ const today = new Date();
924
+ if (dp.hours != null) {
925
+ if (dp.isPm === true && +dp.hours !== 12) {
926
+ dp.hours = +dp.hours + 12;
927
+ } else if (dp.isPm === false && +dp.hours === 12) {
928
+ dp.hours = 0;
929
+ }
930
+ }
931
+
932
+ let date;
933
+ if (dp.timezoneOffset != null) {
934
+ dp.minutes = +(dp.minutes || 0) - +dp.timezoneOffset;
935
+ date = new Date(
936
+ Date.UTC(
937
+ dp.year || today.getFullYear(),
938
+ dp.month || 0,
939
+ dp.day || 1,
940
+ dp.hours || 0,
941
+ dp.minutes || 0,
942
+ dp.seconds || 0,
943
+ dp.milliseconds || 0,
944
+ ),
945
+ );
946
+ } else {
947
+ date = locale.getDateFromParts({
948
+ year: dp.year || today.getFullYear(),
949
+ month: (dp.month || 0) + 1,
950
+ day: dp.day || 1,
951
+ hours: dp.hours || 0,
952
+ minutes: dp.minutes || 0,
953
+ seconds: dp.seconds || 0,
954
+ milliseconds: dp.milliseconds || 0,
955
+ });
956
+ }
957
+ return date;
958
+ })
959
+ .find(d => d) || parseIsoDate(dateString, locale)
960
+ );
961
+ }
962
+
963
+ function parseIsoDate(dateString: string, locale: Locale): Date {
964
+ const match =
965
+ /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?(Z|[+-]\d{2}:?\d{2})?)?$/.exec(
966
+ dateString,
967
+ );
968
+ if (!match) return new Date(NaN);
969
+
970
+ const [, year, month, day, hours, minutes, seconds, fraction, zone] = match;
971
+ const parts = {
972
+ year: Number(year),
973
+ month: Number(month),
974
+ day: Number(day),
975
+ hours: Number(hours ?? 0),
976
+ minutes: Number(minutes ?? 0),
977
+ seconds: Number(seconds ?? 0),
978
+ milliseconds: Number((fraction ?? '').padEnd(3, '0')),
979
+ };
980
+
981
+ const validationDate = new Date(
982
+ Date.UTC(
983
+ parts.year,
984
+ parts.month - 1,
985
+ parts.day,
986
+ parts.hours,
987
+ parts.minutes,
988
+ parts.seconds,
989
+ parts.milliseconds,
990
+ ),
991
+ );
992
+ if (
993
+ validationDate.getUTCFullYear() !== parts.year ||
994
+ validationDate.getUTCMonth() !== parts.month - 1 ||
995
+ validationDate.getUTCDate() !== parts.day ||
996
+ validationDate.getUTCHours() !== parts.hours ||
997
+ validationDate.getUTCMinutes() !== parts.minutes ||
998
+ validationDate.getUTCSeconds() !== parts.seconds
999
+ ) {
1000
+ return new Date(NaN);
1001
+ }
1002
+
1003
+ if (!zone) return locale.getDateFromParts(parts);
1004
+
1005
+ let offsetMinutes = 0;
1006
+ if (zone !== 'Z') {
1007
+ const sign = zone.startsWith('-') ? -1 : 1;
1008
+ const normalized = zone.slice(1).replace(':', '');
1009
+ offsetMinutes =
1010
+ sign *
1011
+ (Number(normalized.slice(0, 2)) * 60 + Number(normalized.slice(2)));
1012
+ }
1013
+ return new Date(validationDate.getTime() - offsetMinutes * MS_PER_MINUTE);
1014
+ }
1015
+
1016
+ export function formatDate(
1017
+ date: Date,
1018
+ masks: string | string[],
1019
+ locale: Locale,
1020
+ ) {
1021
+ if (date == null) return '';
1022
+ let mask = normalizeMasks(masks, locale)[0];
1023
+ const timezone = /Z$/.test(mask) ? 'UTC' : locale.timezone;
1024
+ const literals: string[] = [];
1025
+ // Make literals inactive by replacing them with ??
1026
+ mask = mask.replace(literal, ($0, $1: string) => {
1027
+ literals.push($1);
1028
+ return '??';
1029
+ });
1030
+ const dateParts = getDateParts(date, locale, timezone);
1031
+ // Apply formatting rules
1032
+ mask = mask.replace(token, $0 =>
1033
+ $0 in formatFlags
1034
+ ? formatFlags[$0](dateParts, locale)
1035
+ : $0.slice(1, $0.length - 1),
1036
+ );
1037
+ // Inline literal values back into the formatted value
1038
+ return mask.replace(/\?\?/g, () => literals.shift()!);
1039
+ }