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.
- package/LICENSE +9 -0
- package/README.md +142 -0
- package/dist/cjs/index.css +1133 -0
- package/dist/cjs/index.js +10699 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/es/index.js +10678 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/style.css +1134 -0
- package/dist/iife/index.js +2 -0
- package/dist/iife/index.js.map +1 -0
- package/dist/mjs/index.mjs +10678 -0
- package/dist/mjs/index.mjs.map +1 -0
- package/dist/mjs/style.css +1134 -0
- package/dist/style.css +1134 -0
- package/dist/types/components/BaseIcon/BaseIcon.vue.d.ts +41 -0
- package/dist/types/components/BaseIcon/icons/IconChevronDown.vue.d.ts +3 -0
- package/dist/types/components/BaseIcon/icons/IconChevronLeft.vue.d.ts +3 -0
- package/dist/types/components/BaseIcon/icons/IconChevronRight.vue.d.ts +3 -0
- package/dist/types/components/BaseIcon/icons/IconClock.vue.d.ts +3 -0
- package/dist/types/components/BaseIcon/icons/index.d.ts +4 -0
- package/dist/types/components/BaseSelect/BaseSelect.vue.d.ts +26 -0
- package/dist/types/components/Calendar/Calendar.vue.d.ts +2526 -0
- package/dist/types/components/Calendar/CalendarDay.vue.d.ts +83 -0
- package/dist/types/components/Calendar/CalendarDayPopover.vue.d.ts +3 -0
- package/dist/types/components/Calendar/CalendarHeader.vue.d.ts +13 -0
- package/dist/types/components/Calendar/CalendarNav.vue.d.ts +4 -0
- package/dist/types/components/Calendar/CalendarNavPopover.vue.d.ts +3 -0
- package/dist/types/components/Calendar/CalendarPage.vue.d.ts +3 -0
- package/dist/types/components/Calendar/CalendarPageProvider.vue.d.ts +17 -0
- package/dist/types/components/Calendar/CalendarSlot.vue.d.ts +15 -0
- package/dist/types/components/Calendar/CalendarTitleSelect.vue.d.ts +15 -0
- package/dist/types/components/CalendarGrid/CalendarCell.vue.d.ts +9 -0
- package/dist/types/components/CalendarGrid/CalendarCellPopover.vue.d.ts +209 -0
- package/dist/types/components/CalendarGrid/CalendarDayCell.vue.d.ts +8 -0
- package/dist/types/components/CalendarGrid/CalendarEventDetails.vue.d.ts +15 -0
- package/dist/types/components/CalendarGrid/CalendarEventEdit.vue.d.ts +26 -0
- package/dist/types/components/CalendarGrid/CalendarGrid.vue.d.ts +134 -0
- package/dist/types/components/CalendarGrid/CalendarGridWeek.vue.d.ts +8 -0
- package/dist/types/components/CalendarGrid/CalendarViewSelect.vue.d.ts +3 -0
- package/dist/types/components/CalendarGrid/CalendarWeekCell.vue.d.ts +10 -0
- package/dist/types/components/CalendarGrid/Constraints.d.ts +16 -0
- package/dist/types/components/DatePicker/DatePicker.vue.d.ts +15251 -0
- package/dist/types/components/DatePicker/DatePickerBase.vue.d.ts +3 -0
- package/dist/types/components/DatePicker/DatePickerPopover.vue.d.ts +3 -0
- package/dist/types/components/DatePicker/TimePicker.vue.d.ts +10089 -0
- package/dist/types/components/Popover/Popover.vue.d.ts +72 -0
- package/dist/types/components/Popover/PopoverRow.vue.d.ts +17 -0
- package/dist/types/components/index.d.ts +4 -0
- package/dist/types/index.d.cts +40981 -0
- package/dist/types/index.d.mts +40981 -0
- package/dist/types/index.d.ts +40981 -0
- package/dist/types/use/base.d.ts +67 -0
- package/dist/types/use/calendar.d.ts +2422 -0
- package/dist/types/use/calendarGrid.d.ts +9370 -0
- package/dist/types/use/datePicker.d.ts +20210 -0
- package/dist/types/use/page.d.ts +20 -0
- package/dist/types/use/slots.d.ts +5 -0
- package/dist/types/use/timePicker.d.ts +10089 -0
- package/dist/types/utils/attribute.d.ts +49 -0
- package/dist/types/utils/cache.d.ts +10 -0
- package/dist/types/utils/calendar/event.d.ts +111 -0
- package/dist/types/utils/config/index.d.ts +5 -0
- package/dist/types/utils/date/helpers.d.ts +161 -0
- package/dist/types/utils/date/range.d.ts +74 -0
- package/dist/types/utils/date/repeat.d.ts +34 -0
- package/dist/types/utils/date/rules.d.ts +82 -0
- package/dist/types/utils/defaults/index.d.ts +30 -0
- package/dist/types/utils/defaults/locales.d.ts +9 -0
- package/dist/types/utils/glyph.d.ts +58 -0
- package/dist/types/utils/helpers.d.ts +49 -0
- package/dist/types/utils/locale.d.ts +48 -0
- package/dist/types/utils/page.d.ts +95 -0
- package/dist/types/utils/plugins/index.d.ts +4 -0
- package/dist/types/utils/popovers.d.ts +47 -0
- package/dist/types/utils/theme.d.ts +17 -0
- package/dist/types/utils/touch.d.ts +8 -0
- package/dist/types/utils/useDisplayMode.d.ts +12 -0
- package/dist/types/utils/watchers.d.ts +6 -0
- package/package.json +115 -0
- package/src/components/BaseIcon/BaseIcon.vue +29 -0
- package/src/components/BaseIcon/icons/IconChevronDown.vue +5 -0
- package/src/components/BaseIcon/icons/IconChevronLeft.vue +5 -0
- package/src/components/BaseIcon/icons/IconChevronRight.vue +5 -0
- package/src/components/BaseIcon/icons/IconClock.vue +11 -0
- package/src/components/BaseIcon/icons/index.ts +4 -0
- package/src/components/BaseSelect/BaseSelect.vue +136 -0
- package/src/components/Calendar/Calendar.vue +116 -0
- package/src/components/Calendar/CalendarDay.vue +422 -0
- package/src/components/Calendar/CalendarDayPopover.vue +44 -0
- package/src/components/Calendar/CalendarHeader.vue +203 -0
- package/src/components/Calendar/CalendarNav.vue +325 -0
- package/src/components/Calendar/CalendarNavPopover.vue +24 -0
- package/src/components/Calendar/CalendarPage.vue +140 -0
- package/src/components/Calendar/CalendarPageProvider.vue +15 -0
- package/src/components/Calendar/CalendarSlot.vue +36 -0
- package/src/components/Calendar/CalendarTitleSelect.vue +72 -0
- package/src/components/CalendarGrid/CalendarCell.vue +112 -0
- package/src/components/CalendarGrid/CalendarCellPopover.vue +93 -0
- package/src/components/CalendarGrid/CalendarDayCell.vue +57 -0
- package/src/components/CalendarGrid/CalendarEventDetails.vue +71 -0
- package/src/components/CalendarGrid/CalendarEventEdit.vue +195 -0
- package/src/components/CalendarGrid/CalendarGrid.vue +715 -0
- package/src/components/CalendarGrid/CalendarGridWeek.vue +60 -0
- package/src/components/CalendarGrid/CalendarViewSelect.vue +34 -0
- package/src/components/CalendarGrid/CalendarWeekCell.vue +52 -0
- package/src/components/CalendarGrid/Constraints.ts +40 -0
- package/src/components/DatePicker/DatePicker.vue +27 -0
- package/src/components/DatePicker/DatePickerBase.vue +53 -0
- package/src/components/DatePicker/DatePickerPopover.vue +37 -0
- package/src/components/DatePicker/TimePicker.vue +167 -0
- package/src/components/Popover/Popover.vue +572 -0
- package/src/components/Popover/PopoverRow.vue +94 -0
- package/src/components/index.ts +4 -0
- package/src/index.ts +24 -0
- package/src/styles/index.css +48 -0
- package/src/styles/theme.css +322 -0
- package/src/styles/transitions.css +77 -0
- package/src/use/base.ts +144 -0
- package/src/use/calendar.ts +796 -0
- package/src/use/calendarGrid.ts +954 -0
- package/src/use/datePicker.ts +909 -0
- package/src/use/page.ts +78 -0
- package/src/use/slots.ts +17 -0
- package/src/use/timePicker.ts +194 -0
- package/src/utils/attribute.ts +107 -0
- package/src/utils/cache.ts +30 -0
- package/src/utils/calendar/event.ts +335 -0
- package/src/utils/config/index.ts +11 -0
- package/src/utils/date/helpers.ts +1039 -0
- package/src/utils/date/range.ts +274 -0
- package/src/utils/date/repeat.ts +135 -0
- package/src/utils/date/rules.ts +290 -0
- package/src/utils/defaults/index.ts +77 -0
- package/src/utils/defaults/locales.ts +123 -0
- package/src/utils/defaults/masks.json +15 -0
- package/src/utils/defaults/touch.json +5 -0
- package/src/utils/glyph.ts +305 -0
- package/src/utils/helpers.ts +181 -0
- package/src/utils/locale.ts +275 -0
- package/src/utils/page.ts +608 -0
- package/src/utils/plugins/index.ts +24 -0
- package/src/utils/popovers.ts +216 -0
- package/src/utils/theme.ts +58 -0
- package/src/utils/touch.ts +72 -0
- package/src/utils/useDisplayMode.ts +198 -0
- package/src/utils/watchers.ts +26 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { isArray, isDate, isObject } from '../helpers';
|
|
2
|
+
import Locale from '../locale';
|
|
3
|
+
import type { CalendarDay } from '../page';
|
|
4
|
+
import { type DateParts, type DayParts, MS_PER_DAY, addDays } from './helpers';
|
|
5
|
+
import { DateRepeat, type DateRepeatConfig } from './repeat';
|
|
6
|
+
|
|
7
|
+
type DateRangeDate = Date | string | number | null;
|
|
8
|
+
|
|
9
|
+
interface DateRangeConfig {
|
|
10
|
+
start: DateRangeDate;
|
|
11
|
+
end: DateRangeDate;
|
|
12
|
+
span: number;
|
|
13
|
+
order: number;
|
|
14
|
+
repeat: Partial<DateRepeatConfig>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type DateRangeSource =
|
|
18
|
+
| DateRange
|
|
19
|
+
| DateRangeDate
|
|
20
|
+
| [DateRangeDate, DateRangeDate]
|
|
21
|
+
| Partial<DateRangeConfig>;
|
|
22
|
+
|
|
23
|
+
export interface SimpleDateRange {
|
|
24
|
+
start: Date;
|
|
25
|
+
end: Date;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class DateRange {
|
|
29
|
+
order: number;
|
|
30
|
+
locale: Locale;
|
|
31
|
+
start: DateParts | null = null;
|
|
32
|
+
end: DateParts | null = null;
|
|
33
|
+
repeat: DateRepeat | null = null;
|
|
34
|
+
|
|
35
|
+
static fromMany(ranges: DateRangeSource | DateRangeSource[], locale: Locale) {
|
|
36
|
+
// Assign dates
|
|
37
|
+
return (isArray(ranges) ? ranges : [ranges])
|
|
38
|
+
.filter(d => d)
|
|
39
|
+
.map(d => DateRange.from(d, locale));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static from(source: DateRangeSource, locale: Locale) {
|
|
43
|
+
if (source instanceof DateRange) return source;
|
|
44
|
+
const config: Partial<DateRangeConfig> = {
|
|
45
|
+
start: null,
|
|
46
|
+
end: null,
|
|
47
|
+
};
|
|
48
|
+
if (source != null) {
|
|
49
|
+
if (isArray(source)) {
|
|
50
|
+
config.start = source[0] ?? null;
|
|
51
|
+
config.end = source[1] ?? null;
|
|
52
|
+
} else if (isObject(source)) {
|
|
53
|
+
Object.assign(config, source);
|
|
54
|
+
} else {
|
|
55
|
+
config.start = source;
|
|
56
|
+
config.end = source;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (config.start != null) {
|
|
60
|
+
config.start = locale.toDateOrNull(config.start);
|
|
61
|
+
}
|
|
62
|
+
if (config.end != null) {
|
|
63
|
+
config.end = locale.toDateOrNull(config.end);
|
|
64
|
+
}
|
|
65
|
+
return new DateRange(config, locale);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private constructor(config: Partial<DateRangeConfig>, locale = new Locale()) {
|
|
69
|
+
this.locale = locale;
|
|
70
|
+
const { start, end, span, order, repeat } = config;
|
|
71
|
+
|
|
72
|
+
if (isDate(start)) {
|
|
73
|
+
this.start = locale.getDateParts(start);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (isDate(end)) {
|
|
77
|
+
this.end = locale.getDateParts(end);
|
|
78
|
+
} else if (this.start != null && span) {
|
|
79
|
+
this.end = locale.getDateParts(addDays(this.start.date, span - 1));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.order = order ?? 0;
|
|
83
|
+
|
|
84
|
+
if (repeat) {
|
|
85
|
+
this.repeat = new DateRepeat(
|
|
86
|
+
{
|
|
87
|
+
from: this.start?.date,
|
|
88
|
+
...repeat,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
locale: this.locale,
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get opts() {
|
|
98
|
+
const { order, locale } = this;
|
|
99
|
+
return { order, locale };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get hasRepeat() {
|
|
103
|
+
return !!this.repeat;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get isSingleDay() {
|
|
107
|
+
const { start, end } = this;
|
|
108
|
+
return (
|
|
109
|
+
start &&
|
|
110
|
+
end &&
|
|
111
|
+
start.year === end.year &&
|
|
112
|
+
start.month === end.month &&
|
|
113
|
+
start.day === end.day
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get isMultiDay() {
|
|
118
|
+
return !this.isSingleDay;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get daySpan() {
|
|
122
|
+
if (this.start == null || this.end == null) {
|
|
123
|
+
if (this.hasRepeat) return 1;
|
|
124
|
+
return Infinity;
|
|
125
|
+
}
|
|
126
|
+
return this.end.dayIndex - this.start.dayIndex;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
startsOnDay(dayParts: DayParts) {
|
|
130
|
+
return (
|
|
131
|
+
this.start?.dayIndex === dayParts.dayIndex ||
|
|
132
|
+
!!this.repeat?.passes(dayParts)
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
intersectsDay(dayIndex: number) {
|
|
137
|
+
return this.intersectsDayRange(dayIndex, dayIndex);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
intersectsRange(range: DateRange) {
|
|
141
|
+
return this.intersectsDayRange(
|
|
142
|
+
range.start?.dayIndex ?? -Infinity,
|
|
143
|
+
range.end?.dayIndex ?? Infinity,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
intersectsDayRange(startDayIndex: number, endDayIndex: number) {
|
|
148
|
+
if (this.start && this.start.dayIndex > endDayIndex) return false;
|
|
149
|
+
if (this.end && this.end.dayIndex < startDayIndex) return false;
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
interface DataRange {
|
|
155
|
+
startDay: number;
|
|
156
|
+
startTime: number;
|
|
157
|
+
endDay: number;
|
|
158
|
+
endTime: number;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface RangeData {
|
|
162
|
+
key: string | number;
|
|
163
|
+
order?: number;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
interface DataRanges {
|
|
167
|
+
ranges: DataRange[];
|
|
168
|
+
data: RangeData;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface DateRangeCell<T extends RangeData> extends DataRange {
|
|
172
|
+
data: T;
|
|
173
|
+
onStart: boolean;
|
|
174
|
+
onEnd: boolean;
|
|
175
|
+
startTime: number;
|
|
176
|
+
startDate: Date;
|
|
177
|
+
endTime: number;
|
|
178
|
+
endDate: Date;
|
|
179
|
+
allDay: boolean;
|
|
180
|
+
order: number;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export class DateRangeContext {
|
|
184
|
+
private records: Record<string, DataRanges> = {};
|
|
185
|
+
|
|
186
|
+
render(data: RangeData, range: DateRange, days: DayParts[]) {
|
|
187
|
+
let result = null;
|
|
188
|
+
const startDayIndex = days[0]?.dayIndex ?? 0;
|
|
189
|
+
const endDayIndex = days[days.length - 1]?.dayIndex ?? 0;
|
|
190
|
+
if (range.hasRepeat) {
|
|
191
|
+
days.forEach(day => {
|
|
192
|
+
if (range.startsOnDay(day)) {
|
|
193
|
+
const span = range.daySpan < Infinity ? range.daySpan : 1;
|
|
194
|
+
result = {
|
|
195
|
+
startDay: day.dayIndex,
|
|
196
|
+
startTime: range.start?.time ?? 0,
|
|
197
|
+
endDay: day.dayIndex + span - 1,
|
|
198
|
+
endTime: range.end?.time ?? MS_PER_DAY,
|
|
199
|
+
};
|
|
200
|
+
this.getRangeRecords(data).push(result);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
} else if (range.intersectsDayRange(startDayIndex, endDayIndex)) {
|
|
204
|
+
result = {
|
|
205
|
+
startDay: range.start?.dayIndex ?? -Infinity,
|
|
206
|
+
startTime: range.start?.time ?? -Infinity,
|
|
207
|
+
endDay: range.end?.dayIndex ?? Infinity,
|
|
208
|
+
endTime: range.end?.time ?? Infinity,
|
|
209
|
+
};
|
|
210
|
+
this.getRangeRecords(data).push(result);
|
|
211
|
+
}
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private getRangeRecords(data: RangeData) {
|
|
216
|
+
let record = this.records[data.key];
|
|
217
|
+
if (!record) {
|
|
218
|
+
record = {
|
|
219
|
+
ranges: [],
|
|
220
|
+
data,
|
|
221
|
+
};
|
|
222
|
+
this.records[data.key] = record;
|
|
223
|
+
}
|
|
224
|
+
return record.ranges;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
getCell(key: string | number, day: CalendarDay) {
|
|
228
|
+
const cells = this.getCells(day);
|
|
229
|
+
const result = cells.find(cell => cell.data.key === key);
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
cellExists(key: string | number, dayIndex: number) {
|
|
234
|
+
const records = this.records[key];
|
|
235
|
+
if (records == null) return false;
|
|
236
|
+
return records.ranges.some(
|
|
237
|
+
r => r.startDay <= dayIndex && r.endDay >= dayIndex,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
getCells(day: CalendarDay) {
|
|
242
|
+
const records = Object.values(this.records);
|
|
243
|
+
const result: DateRangeCell<any>[] = [];
|
|
244
|
+
const { dayIndex } = day;
|
|
245
|
+
records.forEach(({ data, ranges }) => {
|
|
246
|
+
ranges
|
|
247
|
+
.filter(r => r.startDay <= dayIndex && r.endDay >= dayIndex)
|
|
248
|
+
.forEach(range => {
|
|
249
|
+
const onStart = dayIndex === range.startDay;
|
|
250
|
+
const onEnd = dayIndex === range.endDay;
|
|
251
|
+
const startTime = onStart ? range.startTime : 0;
|
|
252
|
+
const startDate = new Date(day.startDate.getTime() + startTime);
|
|
253
|
+
const endTime = onEnd ? range.endTime : MS_PER_DAY;
|
|
254
|
+
const endDate = new Date(day.endDate.getTime() + endTime);
|
|
255
|
+
const allDay = startTime === 0 && endTime === MS_PER_DAY;
|
|
256
|
+
const order = data.order || 0;
|
|
257
|
+
result.push({
|
|
258
|
+
...range,
|
|
259
|
+
data,
|
|
260
|
+
onStart,
|
|
261
|
+
onEnd,
|
|
262
|
+
startTime,
|
|
263
|
+
startDate,
|
|
264
|
+
endTime,
|
|
265
|
+
endDate,
|
|
266
|
+
allDay,
|
|
267
|
+
order,
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
result.sort((a, b) => a.order - b.order);
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { isArray, isFunction, isObject, isString } from '../helpers';
|
|
2
|
+
import Locale from '../locale';
|
|
3
|
+
import type {
|
|
4
|
+
DateParts,
|
|
5
|
+
DayInMonth,
|
|
6
|
+
DayOfWeek,
|
|
7
|
+
DayParts,
|
|
8
|
+
MonthInYear,
|
|
9
|
+
WeekInMonth,
|
|
10
|
+
} from './helpers';
|
|
11
|
+
import {
|
|
12
|
+
ComponentRule,
|
|
13
|
+
ComponentRuleType,
|
|
14
|
+
FunctionRule,
|
|
15
|
+
GroupRuleType,
|
|
16
|
+
IntervalRule,
|
|
17
|
+
IntervalRuleType,
|
|
18
|
+
OrdinalComponentRule,
|
|
19
|
+
OrdinalComponentRuleType,
|
|
20
|
+
type Rule,
|
|
21
|
+
type RuleType,
|
|
22
|
+
type SingleOrArray,
|
|
23
|
+
} from './rules';
|
|
24
|
+
|
|
25
|
+
export type RepeatIntervalShort = 'day' | 'week' | 'month' | 'year';
|
|
26
|
+
|
|
27
|
+
export type RepeatInterval = 'days' | 'weeks' | 'months' | 'years';
|
|
28
|
+
export interface DateRepeatConfig {
|
|
29
|
+
every: RepeatIntervalShort | [number, RepeatInterval];
|
|
30
|
+
from: Date;
|
|
31
|
+
until: Date;
|
|
32
|
+
weekdays: SingleOrArray<DayOfWeek>;
|
|
33
|
+
days: SingleOrArray<DayInMonth>;
|
|
34
|
+
weeks: SingleOrArray<WeekInMonth>;
|
|
35
|
+
months: SingleOrArray<MonthInYear>;
|
|
36
|
+
years: SingleOrArray<number>;
|
|
37
|
+
ordinalWeekdays: SingleOrArray<number[]>;
|
|
38
|
+
on: SingleOrArray<DateRepeatFn | Partial<DateRepeatConfig>>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type DateRepeatFn = (dayParts: DayParts) => boolean;
|
|
42
|
+
|
|
43
|
+
export interface DateRepeatOptions {
|
|
44
|
+
locale: Locale;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class DateRepeat implements Rule<GroupRuleType> {
|
|
48
|
+
validated = true;
|
|
49
|
+
|
|
50
|
+
config: Partial<DateRepeatConfig> | DateRepeatFn;
|
|
51
|
+
type = GroupRuleType.Any;
|
|
52
|
+
from: DateParts | undefined;
|
|
53
|
+
until: DateParts | undefined;
|
|
54
|
+
rules: Rule<RuleType>[] = [];
|
|
55
|
+
locale = new Locale();
|
|
56
|
+
|
|
57
|
+
constructor(
|
|
58
|
+
config: Partial<DateRepeatConfig> | DateRepeatFn,
|
|
59
|
+
options: Partial<DateRepeatOptions> = {},
|
|
60
|
+
private parent?: DateRepeat,
|
|
61
|
+
) {
|
|
62
|
+
if (options.locale) this.locale = options.locale;
|
|
63
|
+
|
|
64
|
+
this.config = config;
|
|
65
|
+
if (isFunction(config)) {
|
|
66
|
+
this.type = GroupRuleType.All;
|
|
67
|
+
this.rules = [new FunctionRule(config)];
|
|
68
|
+
} else if (isArray(config)) {
|
|
69
|
+
this.type = GroupRuleType.Any;
|
|
70
|
+
this.rules = config.map(c => new DateRepeat(c, options, this));
|
|
71
|
+
} else if (isObject(config)) {
|
|
72
|
+
this.type = GroupRuleType.All;
|
|
73
|
+
// Assign bounding dates
|
|
74
|
+
this.from = config.from
|
|
75
|
+
? this.locale.getDateParts(config.from)
|
|
76
|
+
: parent?.from;
|
|
77
|
+
this.until = config.until
|
|
78
|
+
? this.locale.getDateParts(config.until)
|
|
79
|
+
: parent?.until;
|
|
80
|
+
this.rules = this.getObjectRules(config);
|
|
81
|
+
} else {
|
|
82
|
+
console.error('Rule group configuration must be an object or an array.');
|
|
83
|
+
this.validated = false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getObjectRules(config: any) {
|
|
88
|
+
const rules: Rule<RuleType>[] = [];
|
|
89
|
+
|
|
90
|
+
// Add interval rule
|
|
91
|
+
if (config.every) {
|
|
92
|
+
if (isString(config.every)) {
|
|
93
|
+
config.every = [1, `${config.every}s`];
|
|
94
|
+
}
|
|
95
|
+
if (isArray(config.every)) {
|
|
96
|
+
const [interval = 1, type = IntervalRuleType.Days] = config.every;
|
|
97
|
+
rules.push(new IntervalRule(type, interval, this.from!));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Add component rules
|
|
102
|
+
Object.values(ComponentRuleType).forEach(type => {
|
|
103
|
+
if (!(type in config)) return;
|
|
104
|
+
rules.push(ComponentRule.create(type, config[type]));
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Add ordinal component rules
|
|
108
|
+
Object.values(OrdinalComponentRuleType).forEach(type => {
|
|
109
|
+
if (!(type in config)) return;
|
|
110
|
+
rules.push(new OrdinalComponentRule(type, config[type]));
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Add group rules
|
|
114
|
+
if (config.on != null) {
|
|
115
|
+
if (!isArray(config.on)) config.on = [config.on];
|
|
116
|
+
rules.push(
|
|
117
|
+
new DateRepeat(config.on, { locale: this.locale }, this.parent),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return rules;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
passes(dayParts: DayParts) {
|
|
125
|
+
if (!this.validated) return true;
|
|
126
|
+
|
|
127
|
+
if (this.from && dayParts.dayIndex <= this.from.dayIndex) return false;
|
|
128
|
+
if (this.until && dayParts.dayIndex >= this.until.dayIndex) return false;
|
|
129
|
+
|
|
130
|
+
if (this.type === GroupRuleType.Any) {
|
|
131
|
+
return this.rules.some(r => r.passes(dayParts));
|
|
132
|
+
}
|
|
133
|
+
return this.rules.every(r => r.passes(dayParts));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { isArray, isFunction, isNumber } from '../helpers';
|
|
2
|
+
import {
|
|
3
|
+
type DayOfWeek,
|
|
4
|
+
type DayParts,
|
|
5
|
+
type OrdinalWeekInMonth,
|
|
6
|
+
diffInDays,
|
|
7
|
+
diffInMonths,
|
|
8
|
+
diffInWeeks,
|
|
9
|
+
diffInYears,
|
|
10
|
+
isDayInMonth,
|
|
11
|
+
isDayOfWeek,
|
|
12
|
+
isMonthInYear,
|
|
13
|
+
isOrdinalWeekInMonth,
|
|
14
|
+
isWeekInMonth,
|
|
15
|
+
} from './helpers';
|
|
16
|
+
|
|
17
|
+
export type SingleOrArray<T> = T | T[];
|
|
18
|
+
|
|
19
|
+
export enum GroupRuleType {
|
|
20
|
+
Any = 'any',
|
|
21
|
+
All = 'all',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export enum IntervalRuleType {
|
|
25
|
+
Days = 'days',
|
|
26
|
+
Weeks = 'weeks',
|
|
27
|
+
Months = 'months',
|
|
28
|
+
Years = 'years',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export enum ComponentRuleType {
|
|
32
|
+
Days = 'days',
|
|
33
|
+
Weekdays = 'weekdays',
|
|
34
|
+
Weeks = 'weeks',
|
|
35
|
+
Months = 'months',
|
|
36
|
+
Years = 'years',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export enum OrdinalComponentRuleType {
|
|
40
|
+
OrdinalWeekdays = 'ordinalWeekdays',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export enum FunctionRuleType {
|
|
44
|
+
Function = 'function',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type RuleType =
|
|
48
|
+
| GroupRuleType
|
|
49
|
+
| IntervalRuleType
|
|
50
|
+
| ComponentRuleType
|
|
51
|
+
| OrdinalComponentRuleType
|
|
52
|
+
| FunctionRuleType;
|
|
53
|
+
|
|
54
|
+
export type OrdinalArrayConfig = SingleOrArray<
|
|
55
|
+
[OrdinalWeekInMonth, ...DayOfWeek[]]
|
|
56
|
+
>;
|
|
57
|
+
|
|
58
|
+
export interface Rule<T> {
|
|
59
|
+
type: T;
|
|
60
|
+
passes(dayParts: DayParts): boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class IntervalRule implements Rule<IntervalRuleType> {
|
|
64
|
+
private validated = true;
|
|
65
|
+
|
|
66
|
+
constructor(
|
|
67
|
+
public type: IntervalRuleType,
|
|
68
|
+
public interval: number,
|
|
69
|
+
public from: DayParts,
|
|
70
|
+
) {
|
|
71
|
+
// Start date needed for interval rules
|
|
72
|
+
if (!this.from) {
|
|
73
|
+
console.error(
|
|
74
|
+
`A valid "from" date is required for date interval rule. This rule will be skipped.`,
|
|
75
|
+
);
|
|
76
|
+
this.validated = false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
passes(dateParts: DayParts) {
|
|
81
|
+
if (!this.validated) return true;
|
|
82
|
+
|
|
83
|
+
const { date } = dateParts;
|
|
84
|
+
switch (this.type) {
|
|
85
|
+
case 'days': {
|
|
86
|
+
return diffInDays(this.from.date, date) % this.interval === 0;
|
|
87
|
+
}
|
|
88
|
+
case 'weeks': {
|
|
89
|
+
return diffInWeeks(this.from.date, date) % this.interval === 0;
|
|
90
|
+
}
|
|
91
|
+
case 'months': {
|
|
92
|
+
return diffInMonths(this.from.date, date) % this.interval === 0;
|
|
93
|
+
}
|
|
94
|
+
case 'years': {
|
|
95
|
+
return diffInYears(this.from.date, date) % this.interval === 0;
|
|
96
|
+
}
|
|
97
|
+
default: {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export class ComponentRule implements Rule<ComponentRuleType> {
|
|
105
|
+
components: number[] = [];
|
|
106
|
+
|
|
107
|
+
static create(type: ComponentRuleType, config: unknown) {
|
|
108
|
+
switch (type) {
|
|
109
|
+
case ComponentRuleType.Days:
|
|
110
|
+
return new DaysRule(config);
|
|
111
|
+
case ComponentRuleType.Weekdays:
|
|
112
|
+
return new WeekdaysRule(config);
|
|
113
|
+
case ComponentRuleType.Weeks:
|
|
114
|
+
return new WeeksRule(config);
|
|
115
|
+
case ComponentRuleType.Months:
|
|
116
|
+
return new MonthsRule(config);
|
|
117
|
+
case ComponentRuleType.Years:
|
|
118
|
+
return new YearsRule(config);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
constructor(
|
|
123
|
+
public type: ComponentRuleType,
|
|
124
|
+
components: unknown,
|
|
125
|
+
public validator: (component: unknown) => component is number,
|
|
126
|
+
public getter: (dayParts: DayParts) => number[],
|
|
127
|
+
) {
|
|
128
|
+
this.components = this.normalizeComponents(components);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
normalizeComponents(components: unknown) {
|
|
132
|
+
if (this.validator(components)) return [components];
|
|
133
|
+
if (!isArray(components)) return [];
|
|
134
|
+
const result: number[] = [];
|
|
135
|
+
components.forEach(component => {
|
|
136
|
+
if (!this.validator(component)) {
|
|
137
|
+
console.error(
|
|
138
|
+
`Component value ${component} in invalid for "${this.type}" rule. This rule will be skipped.`,
|
|
139
|
+
);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
result.push(component);
|
|
143
|
+
});
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
passes(dayParts: DayParts) {
|
|
148
|
+
const comps = this.getter(dayParts);
|
|
149
|
+
const result = comps.some(comp => this.components.includes(comp));
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export class DaysRule extends ComponentRule {
|
|
155
|
+
constructor(components: unknown) {
|
|
156
|
+
super(
|
|
157
|
+
ComponentRuleType.Days,
|
|
158
|
+
components,
|
|
159
|
+
isDayInMonth,
|
|
160
|
+
({ day, dayFromEnd }) => [day, -dayFromEnd],
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export class WeekdaysRule extends ComponentRule {
|
|
166
|
+
constructor(components: unknown) {
|
|
167
|
+
super(
|
|
168
|
+
ComponentRuleType.Weekdays,
|
|
169
|
+
components,
|
|
170
|
+
isDayOfWeek,
|
|
171
|
+
({ weekday }) => [weekday],
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export class WeeksRule extends ComponentRule {
|
|
177
|
+
constructor(components: unknown) {
|
|
178
|
+
super(
|
|
179
|
+
ComponentRuleType.Weeks,
|
|
180
|
+
components,
|
|
181
|
+
isWeekInMonth,
|
|
182
|
+
({ week, weekFromEnd }) => [week, -weekFromEnd],
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export class MonthsRule extends ComponentRule {
|
|
188
|
+
constructor(components: unknown) {
|
|
189
|
+
super(ComponentRuleType.Months, components, isMonthInYear, ({ month }) => [
|
|
190
|
+
month,
|
|
191
|
+
]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export class YearsRule extends ComponentRule {
|
|
196
|
+
constructor(components: unknown) {
|
|
197
|
+
super(ComponentRuleType.Years, components, isNumber, ({ year }) => [year]);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export class OrdinalComponentRule implements Rule<OrdinalComponentRuleType> {
|
|
202
|
+
components: [OrdinalWeekInMonth, DayOfWeek][];
|
|
203
|
+
|
|
204
|
+
constructor(
|
|
205
|
+
public type: OrdinalComponentRuleType,
|
|
206
|
+
components: OrdinalArrayConfig,
|
|
207
|
+
) {
|
|
208
|
+
this.components = this.normalizeComponents(components);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
normalizeArrayConfig(config: OrdinalArrayConfig) {
|
|
212
|
+
const result: [OrdinalWeekInMonth, DayOfWeek][] = [];
|
|
213
|
+
config.forEach((numOrArray, i) => {
|
|
214
|
+
if (isNumber(numOrArray)) {
|
|
215
|
+
if (i === 0) return;
|
|
216
|
+
if (!isOrdinalWeekInMonth(config[0])) {
|
|
217
|
+
console.error(
|
|
218
|
+
`Ordinal range for "${this.type}" rule is from -5 to -1 or 1 to 5. This rule will be skipped.`,
|
|
219
|
+
);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (!isDayOfWeek(numOrArray)) {
|
|
223
|
+
console.error(
|
|
224
|
+
`Acceptable range for "${this.type}" rule is from 1 to 5. This rule will be skipped`,
|
|
225
|
+
);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
result.push([config[0], numOrArray]);
|
|
229
|
+
} else if (isArray(numOrArray)) {
|
|
230
|
+
result.push(...this.normalizeArrayConfig(numOrArray));
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
normalizeComponents(config: OrdinalArrayConfig) {
|
|
237
|
+
const result: [OrdinalWeekInMonth, DayOfWeek][] = [];
|
|
238
|
+
config.forEach((numOrArray, i) => {
|
|
239
|
+
if (isNumber(numOrArray)) {
|
|
240
|
+
if (i === 0) return;
|
|
241
|
+
if (!isOrdinalWeekInMonth(config[0])) {
|
|
242
|
+
console.error(
|
|
243
|
+
`Ordinal range for "${this.type}" rule is from -5 to -1 or 1 to 5. This rule will be skipped.`,
|
|
244
|
+
);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (!isDayOfWeek(numOrArray)) {
|
|
248
|
+
console.error(
|
|
249
|
+
`Acceptable range for "${this.type}" rule is from 1 to 5. This rule will be skipped`,
|
|
250
|
+
);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
result.push([config[0], numOrArray]);
|
|
254
|
+
} else if (isArray(numOrArray)) {
|
|
255
|
+
result.push(...this.normalizeArrayConfig(numOrArray));
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
passes(dayParts: DayParts) {
|
|
262
|
+
const { weekday, weekdayOrdinal, weekdayOrdinalFromEnd } = dayParts;
|
|
263
|
+
return this.components.some(
|
|
264
|
+
([ordinalWeek, ordinalWeekday]) =>
|
|
265
|
+
(ordinalWeek === weekdayOrdinal ||
|
|
266
|
+
ordinalWeek === -weekdayOrdinalFromEnd) &&
|
|
267
|
+
weekday === ordinalWeekday,
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export class FunctionRule implements Rule<FunctionRuleType> {
|
|
273
|
+
type = FunctionRuleType.Function;
|
|
274
|
+
private validated = true;
|
|
275
|
+
|
|
276
|
+
constructor(public fn: Function) {
|
|
277
|
+
if (!isFunction(fn)) {
|
|
278
|
+
console.error(
|
|
279
|
+
`The function rule requires a valid function. This rule will be skipped.`,
|
|
280
|
+
);
|
|
281
|
+
this.validated = false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
passes(dayParts: DayParts) {
|
|
286
|
+
if (!this.validated) return true;
|
|
287
|
+
|
|
288
|
+
return this.fn(dayParts);
|
|
289
|
+
}
|
|
290
|
+
}
|