svelte-range-calendar 0.2.0 → 0.5.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.
@@ -1,21 +1,10 @@
1
- import {
2
- getCenturyEnd,
3
- getCenturyStart,
4
- getDayEnd,
5
- getDayStart,
6
- getDecadeEnd,
7
- getDecadeStart,
8
- getMonthEnd,
9
- getMonthStart,
10
- getYearEnd,
11
- getYearStart,
12
- } from 'svelte-date-utils';
1
+ import { createDateUtils } from 'svelte-date-utils';
2
+ import type { DateAdapter } from 'svelte-date-utils';
13
3
 
14
4
  import type { CalendarType, DateType, LooseValue, LooseValuePiece, Range, View } from './types.js';
15
5
 
16
6
  export const views = ['century', 'decade', 'year', 'month'] as const satisfies readonly View[];
17
7
 
18
- /** Type of date represented by a single tile in a given view. */
19
8
  export const tileTypes: Record<View, DateType> = {
20
9
  century: 'decade',
21
10
  decade: 'year',
@@ -23,36 +12,25 @@ export const tileTypes: Record<View, DateType> = {
23
12
  month: 'day',
24
13
  };
25
14
 
26
- const dayInMs = 24 * 60 * 60 * 1000;
27
-
28
- const gregoryRegions = new Set(
29
- 'AG AS AU BD BR BS BT BW BZ CA CO DM DO ET GT GU HK HN ID IN JM JP KE KH KR LA MH MM MO MT MX MZ NI NP PA PE PH PK PR PT PY SA SG SV TH TT TW UM US VE VI WS YE ZA ZW'.split(
30
- ' ',
31
- ),
32
- );
33
- const islamicRegions = new Set('AE AF BH DJ DZ EG IQ IR JO KW LY OM QA SD SY'.split(' '));
34
-
35
15
  export function getCalendarType(locale: string): CalendarType {
36
16
  let region: string | undefined;
37
-
38
17
  try {
39
18
  region = new Intl.Locale(locale).maximize().region;
40
19
  } catch {
41
20
  region = locale.split('-')[1]?.toUpperCase();
42
21
  }
43
-
44
- if (region === 'IL') {
45
- return 'hebrew';
46
- }
47
-
48
- if (region && islamicRegions.has(region)) {
22
+ if (region === 'IL') return 'hebrew';
23
+ if (region && 'AE AF BH DJ DZ EG IQ IR JO KW LY OM QA SD SY'.split(' ').includes(region)) {
49
24
  return 'islamic';
50
25
  }
51
-
52
- if (region && gregoryRegions.has(region)) {
26
+ if (
27
+ region &&
28
+ 'AG AS AU BD BR BS BT BW BZ CA CO DM DO ET GT GU HK HN ID IN JM JP KE KH KR LA MH MM MO MT MX MZ NI NP PA PE PH PK PR PT PY SA SG SV TH TT TW UM US VE VI WS YE ZA ZW'
29
+ .split(' ')
30
+ .includes(region)
31
+ ) {
53
32
  return 'gregory';
54
33
  }
55
-
56
34
  return 'iso8601';
57
35
  }
58
36
 
@@ -63,128 +41,145 @@ const weekStartDays: Record<CalendarType, number> = {
63
41
  islamic: 6,
64
42
  };
65
43
 
66
- export function isWeekend(date: Date, calendarType: CalendarType): boolean {
67
- const day = date.getDay();
68
-
44
+ export function isWeekend<T>(
45
+ adapter: DateAdapter<T>,
46
+ date: T,
47
+ calendarType: CalendarType,
48
+ ): boolean {
49
+ const day = adapter.toJsDate(date).getDay();
69
50
  return calendarType === 'islamic' || calendarType === 'hebrew'
70
51
  ? day === 5 || day === 6
71
52
  : day === 0 || day === 6;
72
53
  }
73
54
 
74
- export function addDays(date: Date, days: number): Date {
75
- return new Date(date.getFullYear(), date.getMonth(), date.getDate() + days);
55
+ export function addDays<T>(adapter: DateAdapter<T>, date: T, days: number): T {
56
+ return adapter.startOfDay(adapter.addDays(date, days));
76
57
  }
77
58
 
78
- export function getBeginOfWeek(date: Date, calendarType: CalendarType): Date {
79
- const offset = (date.getDay() - weekStartDays[calendarType] + 7) % 7;
80
-
81
- return addDays(date, -offset);
59
+ export function getBeginOfWeek<T>(adapter: DateAdapter<T>, date: T, calendarType: CalendarType): T {
60
+ const day = adapter.toJsDate(date).getDay();
61
+ const offset = (day - weekStartDays[calendarType] + 7) % 7;
62
+ return addDays(adapter, date, -offset);
82
63
  }
83
64
 
84
- export function getDaysBetween(from: Date, to: Date): number {
85
- const fromDay = Date.UTC(from.getFullYear(), from.getMonth(), from.getDate());
86
- const toDay = Date.UTC(to.getFullYear(), to.getMonth(), to.getDate());
87
-
88
- return Math.round((toDay - fromDay) / dayInMs);
65
+ export function getDaysBetween<T>(adapter: DateAdapter<T>, from: T, to: T): number {
66
+ const a = adapter.toJsDate(from);
67
+ const b = adapter.toJsDate(to);
68
+ const fromDay = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
69
+ const toDay = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
70
+ return Math.round((toDay - fromDay) / 86_400_000);
89
71
  }
90
72
 
91
- export function getWeekNumber(date: Date, calendarType: CalendarType): number {
73
+ export function getWeekNumber<T>(
74
+ adapter: DateAdapter<T>,
75
+ date: T,
76
+ calendarType: CalendarType,
77
+ ): number {
78
+ const jsDate = adapter.toJsDate(date);
92
79
  if (calendarType === 'iso8601') {
93
- const thursday = addDays(date, 4 - (date.getDay() || 7));
94
- const yearStart = new Date(thursday.getFullYear(), 0, 1);
95
-
96
- return Math.ceil((getDaysBetween(yearStart, thursday) + 1) / 7);
80
+ const day = jsDate.getDay() || 7;
81
+ const thursday = addDays(adapter, date, 4 - day);
82
+ const year = adapter.getYear(thursday);
83
+ const yearStart = adapter.setDate(
84
+ adapter.setMonth(adapter.setYear(adapter.date(), year), 0),
85
+ 1,
86
+ );
87
+ return Math.ceil((getDaysBetween(adapter, yearStart, thursday) + 1) / 7);
97
88
  }
98
-
99
- const weekStart = getBeginOfWeek(date, calendarType);
100
-
101
- // The week containing January 1st is week 1
102
- if (addDays(weekStart, 6).getFullYear() > weekStart.getFullYear()) {
103
- return 1;
104
- }
105
-
106
- const firstWeekStart = getBeginOfWeek(new Date(weekStart.getFullYear(), 0, 1), calendarType);
107
-
108
- return Math.round(getDaysBetween(firstWeekStart, weekStart) / 7) + 1;
89
+ const weekStart = getBeginOfWeek(adapter, date, calendarType);
90
+ if (adapter.getYear(addDays(adapter, weekStart, 6)) > adapter.getYear(weekStart)) return 1;
91
+ const year = adapter.getYear(weekStart);
92
+ const firstDate = adapter.setDate(adapter.setMonth(adapter.setYear(adapter.date(), year), 0), 1);
93
+ const firstWeekStart = getBeginOfWeek(adapter, firstDate, calendarType);
94
+ return Math.round(getDaysBetween(adapter, firstWeekStart, weekStart) / 7) + 1;
109
95
  }
110
96
 
111
- export function getBegin(type: DateType, date: Date): Date {
97
+ export function getBegin<T>(adapter: DateAdapter<T>, type: DateType, date: T): T {
98
+ const utils = createDateUtils(adapter);
112
99
  switch (type) {
113
100
  case 'century':
114
- return getCenturyStart(date);
101
+ return utils.getCenturyStart(date);
115
102
  case 'decade':
116
- return getDecadeStart(date);
103
+ return utils.getDecadeStart(date);
117
104
  case 'year':
118
- return getYearStart(date);
105
+ return utils.getYearStart(date);
119
106
  case 'month':
120
- return getMonthStart(date);
107
+ return utils.getMonthStart(date);
121
108
  case 'day':
122
- return getDayStart(date);
109
+ return utils.getDayStart(date);
123
110
  }
124
111
  }
125
112
 
126
- export function getEnd(type: DateType, date: Date): Date {
113
+ export function getEnd<T>(adapter: DateAdapter<T>, type: DateType, date: T): T {
114
+ const utils = createDateUtils(adapter);
127
115
  switch (type) {
128
116
  case 'century':
129
- return getCenturyEnd(date);
117
+ return utils.getCenturyEnd(date);
130
118
  case 'decade':
131
- return getDecadeEnd(date);
119
+ return utils.getDecadeEnd(date);
132
120
  case 'year':
133
- return getYearEnd(date);
121
+ return utils.getYearEnd(date);
134
122
  case 'month':
135
- return getMonthEnd(date);
123
+ return utils.getMonthEnd(date);
136
124
  case 'day':
137
- return getDayEnd(date);
125
+ return utils.getDayEnd(date);
138
126
  }
139
127
  }
140
128
 
141
- export function getRange(type: DateType, date: Date): Range<Date> {
142
- return [getBegin(type, date), getEnd(type, date)];
129
+ export function getRange<T>(adapter: DateAdapter<T>, type: DateType, date: T): Range<T> {
130
+ return [getBegin(adapter, type, date), getEnd(adapter, type, date)];
143
131
  }
144
132
 
145
133
  const stepInYears: Record<View, number> = { century: 100, decade: 10, month: 0, year: 1 };
146
134
  const bigStepInYears: Record<View, number> = { century: 0, decade: 100, month: 1, year: 10 };
147
135
 
148
- /**
149
- * Moves a date by one unit of the given view (`double` = false), or by one unit of the next
150
- * bigger view (`double` = true), and returns the beginning of the resulting period.
151
- */
152
- export function shiftDate(view: View, date: Date, amount: number, double = false): Date {
153
- const year = date.getFullYear();
154
- const month = date.getMonth();
136
+ function makeDate<T>(adapter: DateAdapter<T>, year: number, month: number, day = 1): T {
137
+ const date = adapter.setYear(adapter.date(), year);
138
+ return adapter.setDate(adapter.setMonth(date, month), day);
139
+ }
155
140
 
141
+ export function shiftDate<T>(
142
+ adapter: DateAdapter<T>,
143
+ view: View,
144
+ date: T,
145
+ amount: number,
146
+ double = false,
147
+ ): T {
148
+ const year = adapter.getYear(date);
149
+ const month = adapter.getMonth(date);
156
150
  if (!double) {
157
151
  return view === 'month'
158
- ? new Date(year, month + amount, 1)
159
- : getBegin(view, new Date(year + amount * stepInYears[view], 0, 1));
152
+ ? makeDate(adapter, year, month + amount)
153
+ : getBegin(adapter, view, makeDate(adapter, year + amount * stepInYears[view], 0));
160
154
  }
161
-
162
155
  return getBegin(
156
+ adapter,
163
157
  view,
164
- new Date(year + amount * bigStepInYears[view], view === 'month' ? month : 0, 1),
158
+ makeDate(adapter, year + amount * bigStepInYears[view], view === 'month' ? month : 0),
165
159
  );
166
160
  }
167
161
 
168
- export function isRangeWithinRange(greater: Range<Date>, smaller: Range<Date>): boolean {
169
- return (
170
- greater[0].getTime() <= smaller[0].getTime() && greater[1].getTime() >= smaller[1].getTime()
171
- );
162
+ export function isRangeWithinRange<T>(
163
+ adapter: DateAdapter<T>,
164
+ greater: Range<T>,
165
+ smaller: Range<T>,
166
+ ): boolean {
167
+ return !adapter.isAfter(smaller[0], greater[0]) && !adapter.isBefore(smaller[1], greater[1]);
172
168
  }
173
169
 
174
- export function toDate(value: LooseValuePiece | undefined): Date | null {
175
- if (value === null || value === undefined) {
176
- return null;
177
- }
178
-
179
- const date = value instanceof Date ? value : new Date(value);
180
-
181
- return Number.isNaN(date.getTime()) ? null : date;
170
+ export function toDate<T>(
171
+ adapter: DateAdapter<T>,
172
+ value: LooseValuePiece<T> | undefined,
173
+ ): T | null {
174
+ if (value === null || value === undefined) return null;
175
+ const date = typeof value === 'string' ? adapter.date(value) : value;
176
+ return adapter.isValid(date) ? date : null;
182
177
  }
183
178
 
184
- export function normalizeValue(value: LooseValue | undefined): Range<Date | null> {
185
- if (Array.isArray(value)) {
186
- return [toDate(value[0]), toDate(value[1])];
187
- }
188
-
189
- return [toDate(value), null];
179
+ export function normalizeValue<T>(
180
+ adapter: DateAdapter<T>,
181
+ value: LooseValue<T> | undefined,
182
+ ): Range<T | null> {
183
+ if (Array.isArray(value)) return [toDate(adapter, value[0]), toDate(adapter, value[1])];
184
+ return [toDate(adapter, value), null];
190
185
  }
@@ -12,7 +12,7 @@ function getFormatter(locale: string | undefined, options: Intl.DateTimeFormatOp
12
12
  return formatter;
13
13
  }
14
14
 
15
- export type Formatter = (locale: string | undefined, date: Date) => string;
15
+ export type Formatter<T = Date> = (locale: string | undefined, date: T) => string;
16
16
 
17
17
  export const formatDay: Formatter = (locale, date) =>
18
18
  getFormatter(locale, { day: 'numeric' }).format(date);
@@ -8,13 +8,13 @@ export type Detail = View;
8
8
 
9
9
  export type DateType = View | 'day';
10
10
 
11
- export type LooseValuePiece = string | Date | null;
11
+ export type LooseValuePiece<T = Date> = string | T | null;
12
12
 
13
- export type LooseValue = LooseValuePiece | Range<LooseValuePiece>;
13
+ export type LooseValue<T = Date> = LooseValuePiece<T> | Range<LooseValuePiece<T>>;
14
14
 
15
- export type ValuePiece = Date | null;
15
+ export type ValuePiece<T = Date> = T | null;
16
16
 
17
- export type Value = ValuePiece | Range<ValuePiece>;
17
+ export type Value<T = Date> = ValuePiece<T> | Range<ValuePiece<T>>;
18
18
 
19
19
  export type ReturnValue = 'start' | 'end' | 'range';
20
20
 
@@ -27,21 +27,21 @@ export type NavigationAction =
27
27
  | 'drillUp'
28
28
  | 'drillDown';
29
29
 
30
- export type ViewCallbackProperties = {
30
+ export type ViewCallbackProperties<T = Date> = {
31
31
  action: NavigationAction;
32
- activeStartDate: Date | null;
33
- value: LooseValue | undefined;
32
+ activeStartDate: T | null;
33
+ value: LooseValue<T> | undefined;
34
34
  view: View;
35
35
  };
36
36
 
37
- export type TileArgs = {
38
- activeStartDate: Date;
39
- date: Date;
37
+ export type TileArgs<T = Date> = {
38
+ activeStartDate: T;
39
+ date: T;
40
40
  view: View;
41
41
  };
42
42
 
43
- export type NavigationLabelArgs = {
44
- date: Date;
43
+ export type NavigationLabelArgs<T = Date> = {
44
+ date: T;
45
45
  label: string;
46
46
  locale: string | undefined;
47
47
  view: View;