@gobrand/calendar-core 0.0.17 → 0.0.19

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/dist/index.cjs CHANGED
@@ -82,16 +82,6 @@ function goToToday() {
82
82
  const today = import_polyfill.Temporal.Now.plainDateISO();
83
83
  return { year: today.year, month: today.month };
84
84
  }
85
- function getWeekdays(weekStartsOn = 1, locale = "en-US", format = "short") {
86
- const referenceDate = import_polyfill.Temporal.PlainDate.from("2023-01-02");
87
- const days = [];
88
- for (let i = 0; i < 7; i++) {
89
- const offset = (weekStartsOn - 1 + i + 7) % 7;
90
- const date = referenceDate.add({ days: offset });
91
- days.push(date.toLocaleString(locale, { weekday: format }));
92
- }
93
- return days;
94
- }
95
85
  function getMonthName(month, locale = "en-US") {
96
86
  return month.toPlainDate({ day: 1 }).toLocaleString(locale, { month: "long" });
97
87
  }
@@ -187,6 +177,7 @@ function buildMonth(year, month, options) {
187
177
  }
188
178
  const dateKey = currentDate.toString();
189
179
  weeks[weeks.length - 1].push({
180
+ id: `${yearMonth.toString()}-${dateKey}`,
190
181
  date: currentDate,
191
182
  isCurrentMonth: currentDate.month === month,
192
183
  isToday: import_polyfill2.Temporal.PlainDate.compare(currentDate, today) === 0,
@@ -214,7 +205,7 @@ var import_polyfill3 = require("@js-temporal/polyfill");
214
205
  function buildDay(date, options) {
215
206
  const startHour = options?.startHour ?? 0;
216
207
  const endHour = options?.endHour ?? 24;
217
- const slotDuration = options?.slotDuration ?? 30;
208
+ const slotDuration = options?.slotDuration ?? 60;
218
209
  const today = options?.today ?? import_polyfill3.Temporal.Now.plainDateISO();
219
210
  const data = options?.data ?? [];
220
211
  const accessor = options?.accessor;
@@ -228,6 +219,7 @@ function buildDay(date, options) {
228
219
  }
229
220
  }
230
221
  const timeSlots = [];
222
+ const dateStr = date.toString();
231
223
  let currentHour = startHour;
232
224
  let currentMinute = 0;
233
225
  while (currentHour < endHour) {
@@ -254,6 +246,7 @@ function buildDay(date, options) {
254
246
  }
255
247
  }
256
248
  timeSlots.push({
249
+ id: `${dateStr}-${String(currentHour).padStart(2, "0")}:${String(currentMinute).padStart(2, "0")}`,
257
250
  hour: currentHour,
258
251
  minute: currentMinute,
259
252
  time: slotStart,
@@ -266,6 +259,7 @@ function buildDay(date, options) {
266
259
  }
267
260
  }
268
261
  return {
262
+ id: dateStr,
269
263
  date,
270
264
  isToday: import_polyfill3.Temporal.PlainDate.compare(date, today) === 0,
271
265
  timeSlots,
@@ -312,6 +306,7 @@ function buildWeek(date, options) {
312
306
  timeSlots = dayView.timeSlots;
313
307
  }
314
308
  days.push({
309
+ id: `${weekStart.toString()}-${dateKey}`,
315
310
  date: currentDate,
316
311
  isToday: import_polyfill4.Temporal.PlainDate.compare(currentDate, today) === 0,
317
312
  items: itemsByDate.get(dateKey) ?? [],
@@ -384,8 +379,20 @@ function getDayDateRange(date, timeZone) {
384
379
  return { start: startZoned, end: endZoned };
385
380
  }
386
381
 
387
- // src/core/calendar.ts
382
+ // src/utils/getWeekdays.ts
388
383
  var import_polyfill6 = require("@js-temporal/polyfill");
384
+ function getWeekdays(weekStartsOn = 1, locale = "en-US", format = "short") {
385
+ const sunday = import_polyfill6.Temporal.PlainDate.from("2023-01-01");
386
+ const days = [];
387
+ for (let i = 0; i < 7; i++) {
388
+ const date = sunday.add({ days: (weekStartsOn + i) % 7 });
389
+ days.push(date.toLocaleString(locale, { weekday: format }));
390
+ }
391
+ return days;
392
+ }
393
+
394
+ // src/core/calendar.ts
395
+ var import_polyfill7 = require("@js-temporal/polyfill");
389
396
  var import_store = require("@tanstack/store");
390
397
  function computeDateRange(view, referenceDate, timeZone, weekStartsOn = 1) {
391
398
  let start;
@@ -410,22 +417,22 @@ function computeDateRange(view, referenceDate, timeZone, weekStartsOn = 1) {
410
417
  }
411
418
  const startZoned = start.toZonedDateTime({
412
419
  timeZone,
413
- plainTime: import_polyfill6.Temporal.PlainTime.from("00:00:00")
420
+ plainTime: import_polyfill7.Temporal.PlainTime.from("00:00:00")
414
421
  });
415
422
  const endZoned = end.toZonedDateTime({
416
423
  timeZone,
417
- plainTime: import_polyfill6.Temporal.PlainTime.from("23:59:59.999")
424
+ plainTime: import_polyfill7.Temporal.PlainTime.from("23:59:59.999")
418
425
  });
419
426
  return { start: startZoned, end: endZoned };
420
427
  }
421
428
  function createCalendar(options) {
422
429
  const configuredViews = Object.keys(options.views);
423
430
  const defaultView = configuredViews[0];
424
- const timeZone = options.timeZone || import_polyfill6.Temporal.Now.timeZoneId();
431
+ const timeZone = options.timeZone || import_polyfill7.Temporal.Now.timeZoneId();
425
432
  const monthView = options.views.month;
426
433
  const weekView = options.views.week;
427
434
  const weekStartsOn = monthView?.weekStartsOn ?? weekView?.weekStartsOn ?? 1;
428
- const initialReferenceDate = options.state?.referenceDate || import_polyfill6.Temporal.Now.plainDateISO();
435
+ const initialReferenceDate = options.state?.referenceDate || import_polyfill7.Temporal.Now.plainDateISO();
429
436
  const initialView = options.state?.currentView || defaultView;
430
437
  const initialDateRange = computeDateRange(
431
438
  initialView,
@@ -450,18 +457,18 @@ function createCalendar(options) {
450
457
  dateRange: initialDateRange,
451
458
  ...resolvedOptions.state
452
459
  });
453
- const getMonthImpl = () => {
460
+ const getMonthImpl = (data = []) => {
454
461
  const state = store.state;
455
462
  const { year, month } = state.referenceDate;
456
463
  const monthView2 = _options.views.month;
457
464
  if (!monthView2) throw new Error("Month view not configured");
458
465
  return buildMonth(year, month, {
459
466
  weekStartsOn: monthView2.weekStartsOn,
460
- data: _options.data,
467
+ data,
461
468
  accessor: monthView2.accessor
462
469
  });
463
470
  };
464
- const getWeekImpl = () => {
471
+ const getWeekImpl = (data = []) => {
465
472
  const state = store.state;
466
473
  const weekView2 = _options.views.week;
467
474
  if (!weekView2) throw new Error("Week view not configured");
@@ -470,11 +477,11 @@ function createCalendar(options) {
470
477
  startHour: weekView2.startHour,
471
478
  endHour: weekView2.endHour,
472
479
  slotDuration: weekView2.slotDuration,
473
- data: _options.data,
480
+ data,
474
481
  accessor: weekView2.accessor
475
482
  });
476
483
  };
477
- const getDayImpl = () => {
484
+ const getDayImpl = (data = []) => {
478
485
  const state = store.state;
479
486
  const dayView = _options.views.day;
480
487
  if (!dayView) throw new Error("Day view not configured");
@@ -482,7 +489,7 @@ function createCalendar(options) {
482
489
  startHour: dayView.startHour,
483
490
  endHour: dayView.endHour,
484
491
  slotDuration: dayView.slotDuration,
485
- data: _options.data,
492
+ data,
486
493
  accessor: dayView.accessor
487
494
  });
488
495
  };
@@ -510,7 +517,7 @@ function createCalendar(options) {
510
517
  };
511
518
  const nextMonthImpl = () => {
512
519
  setStateImpl((old) => {
513
- const current = import_polyfill6.Temporal.PlainYearMonth.from({
520
+ const current = import_polyfill7.Temporal.PlainYearMonth.from({
514
521
  year: old.referenceDate.year,
515
522
  month: old.referenceDate.month
516
523
  });
@@ -522,7 +529,7 @@ function createCalendar(options) {
522
529
  };
523
530
  const previousMonthImpl = () => {
524
531
  setStateImpl((old) => {
525
- const current = import_polyfill6.Temporal.PlainYearMonth.from({
532
+ const current = import_polyfill7.Temporal.PlainYearMonth.from({
526
533
  year: old.referenceDate.year,
527
534
  month: old.referenceDate.month
528
535
  });
@@ -607,7 +614,7 @@ function createCalendar(options) {
607
614
  previousMonth: previousMonthImpl,
608
615
  goToMonth(year, month) {
609
616
  setStateImpl(() => ({
610
- referenceDate: import_polyfill6.Temporal.PlainDate.from({ year, month, day: 1 })
617
+ referenceDate: import_polyfill7.Temporal.PlainDate.from({ year, month, day: 1 })
611
618
  }));
612
619
  },
613
620
  nextWeek: nextWeekImpl,
@@ -616,7 +623,7 @@ function createCalendar(options) {
616
623
  previousDay: previousDayImpl,
617
624
  goToToday() {
618
625
  setStateImpl(() => ({
619
- referenceDate: import_polyfill6.Temporal.Now.plainDateISO()
626
+ referenceDate: import_polyfill7.Temporal.Now.plainDateISO()
620
627
  }));
621
628
  },
622
629
  goToDate(date) {
@@ -667,6 +674,15 @@ function createCalendar(options) {
667
674
  get dateRange() {
668
675
  return store.state.dateRange;
669
676
  },
677
+ getDateRange(view) {
678
+ const effectiveView = view ?? store.state.currentView ?? defaultView;
679
+ return computeDateRange(
680
+ effectiveView,
681
+ store.state.referenceDate,
682
+ timeZone,
683
+ weekStartsOn
684
+ );
685
+ },
670
686
  get options() {
671
687
  return _options;
672
688
  },
package/dist/index.d.cts CHANGED
@@ -7,6 +7,8 @@ type CalendarAccessor<TItem> = {
7
7
  getEnd?: (item: TItem) => Temporal.ZonedDateTime;
8
8
  };
9
9
  type CalendarDay<TItem = unknown> = {
10
+ /** Unique identifier for this day within the view context. Use as React/Vue key. */
11
+ id: string;
10
12
  date: Temporal.PlainDate;
11
13
  isCurrentMonth: boolean;
12
14
  isToday: boolean;
@@ -18,6 +20,8 @@ type CalendarMonth<TItem = unknown> = {
18
20
  month: Temporal.PlainYearMonth;
19
21
  };
20
22
  type WeekDay<TItem = unknown> = {
23
+ /** Unique identifier for this day within the view context. Use as React/Vue key. */
24
+ id: string;
21
25
  date: Temporal.PlainDate;
22
26
  isToday: boolean;
23
27
  items: TItem[];
@@ -29,12 +33,16 @@ type CalendarWeekView<TItem = unknown> = {
29
33
  weekEnd: Temporal.PlainDate;
30
34
  };
31
35
  type TimeSlot<TItem = unknown> = {
36
+ /** Unique identifier for this time slot. Use as React/Vue key. */
37
+ id: string;
32
38
  hour: number;
33
39
  minute: number;
34
40
  time: Temporal.PlainTime;
35
41
  items: TItem[];
36
42
  };
37
43
  type CalendarDayView<TItem = unknown> = {
44
+ /** Unique identifier for this day view. Use as React/Vue key. */
45
+ id: string;
38
46
  date: Temporal.PlainDate;
39
47
  isToday: boolean;
40
48
  timeSlots: TimeSlot<TItem>[];
@@ -73,17 +81,16 @@ type CalendarViewOptions<TItem> = {
73
81
  day?: DayViewOptions<TItem>;
74
82
  };
75
83
  type CalendarOptions<TItem> = {
76
- data: TItem[];
77
84
  views: CalendarViewOptions<TItem>;
78
85
  timeZone?: 'UTC' | string;
79
86
  state?: Partial<CalendarState>;
80
- onStateChange?: (updater: Updater<CalendarState>) => void;
87
+ onStateChange?: (state: CalendarState) => void;
81
88
  };
82
89
  type HasView<Options, View extends 'month' | 'week' | 'day'> = Options extends {
83
90
  views: infer V;
84
91
  } ? V extends Record<View, unknown> ? V[View] extends undefined ? false : true : false : false;
85
92
  type MonthMethods<TItem, TOptions> = HasView<TOptions, 'month'> extends true ? {
86
- getMonth(): CalendarMonth<TItem>;
93
+ getMonth(data?: TItem[]): CalendarMonth<TItem>;
87
94
  nextMonth(): void;
88
95
  previousMonth(): void;
89
96
  goToMonth(year: number, month: number): void;
@@ -94,7 +101,7 @@ type MonthMethods<TItem, TOptions> = HasView<TOptions, 'month'> extends true ? {
94
101
  goToMonth?: never;
95
102
  };
96
103
  type WeekMethods<TItem, TOptions> = HasView<TOptions, 'week'> extends true ? {
97
- getWeek(): CalendarWeekView<TItem>;
104
+ getWeek(data?: TItem[]): CalendarWeekView<TItem>;
98
105
  nextWeek(): void;
99
106
  previousWeek(): void;
100
107
  } : {
@@ -103,7 +110,7 @@ type WeekMethods<TItem, TOptions> = HasView<TOptions, 'week'> extends true ? {
103
110
  previousWeek?: never;
104
111
  };
105
112
  type DayMethods<TItem, TOptions> = HasView<TOptions, 'day'> extends true ? {
106
- getDay(): CalendarDayView<TItem>;
113
+ getDay(data?: TItem[]): CalendarDayView<TItem>;
107
114
  nextDay(): void;
108
115
  previousDay(): void;
109
116
  } : {
@@ -126,6 +133,7 @@ type BaseCalendarMethods<Options> = {
126
133
  currentView: ValidViews<Options>;
127
134
  setCurrentView(view: ValidViews<Options>): void;
128
135
  dateRange: DateRange;
136
+ getDateRange(view?: ValidViews<Options>): DateRange;
129
137
  options: Options;
130
138
  setOptions(updater: (old: Options) => Options): void;
131
139
  store: _tanstack_store.Store<CalendarState>;
@@ -133,71 +141,145 @@ type BaseCalendarMethods<Options> = {
133
141
  hasWeekView(): boolean;
134
142
  hasDayView(): boolean;
135
143
  };
136
- type ViewsToOptions<TItem, TViews extends CalendarViewOptions<TItem>> = {
137
- data: TItem[];
138
- views: TViews;
144
+ /**
145
+ * Unified calendar type with conditional methods based on configured views.
146
+ *
147
+ * @example
148
+ * // Calendar with specific options - methods are conditional
149
+ * const calendar = createCalendar<Post, typeof options>(options);
150
+ * calendar.getMonth([]); // Only available if month view configured
151
+ *
152
+ * @example
153
+ * // Generic calendar type - all methods available
154
+ * type AnyPostCalendar = Calendar<Post>;
155
+ */
156
+ type Calendar<TItem, TOptions extends CalendarOptions<TItem> = CalendarOptions<TItem>> = BaseCalendarMethods<TOptions> & MonthMethods<TItem, TOptions> & WeekMethods<TItem, TOptions> & DayMethods<TItem, TOptions>;
157
+ /**
158
+ * Calendar instance with all view methods available and typed items.
159
+ * Use this for contexts where the specific view configuration is unknown at compile time,
160
+ * but you still want type-safe item data.
161
+ *
162
+ * @example
163
+ * // In React context - store with unknown, consume with specific type
164
+ * const context = createContext<CalendarInstance<unknown> | null>(null);
165
+ *
166
+ * // Consumer gets typed data
167
+ * function useCalendar<TItem>(): CalendarInstance<TItem> {
168
+ * return useContext(context) as CalendarInstance<TItem>;
169
+ * }
170
+ */
171
+ type CalendarInstance<TItem = unknown> = {
172
+ getMonth(data?: TItem[]): CalendarMonth<TItem>;
173
+ getWeek(data?: TItem[]): CalendarWeekView<TItem>;
174
+ getDay(data?: TItem[]): CalendarDayView<TItem>;
175
+ getTitle(view?: ViewType, locales?: Temporal.LocalesArgument, options?: globalThis.Intl.DateTimeFormatOptions): string;
176
+ getState(): CalendarState;
177
+ setState(updater: CalendarState | ((old: CalendarState) => Partial<CalendarState>)): void;
178
+ goToToday(): void;
179
+ goToDate(date: Temporal.PlainDate): void;
180
+ next(view?: ViewType): void;
181
+ previous(view?: ViewType): void;
182
+ views: ReadonlyArray<string>;
183
+ currentView: string;
184
+ setCurrentView(view: string): void;
185
+ dateRange: DateRange;
186
+ getDateRange(view?: ViewType): DateRange;
187
+ options: CalendarOptions<TItem>;
188
+ setOptions(updater: (old: CalendarOptions<TItem>) => CalendarOptions<TItem>): void;
189
+ store: _tanstack_store.Store<CalendarState>;
190
+ nextMonth(): void;
191
+ previousMonth(): void;
192
+ goToMonth(year: number, month: number): void;
193
+ nextWeek(): void;
194
+ previousWeek(): void;
195
+ nextDay(): void;
196
+ previousDay(): void;
197
+ hasMonthView(): boolean;
198
+ hasWeekView(): boolean;
199
+ hasDayView(): boolean;
139
200
  };
140
- type Calendar<TItem, TOptionsOrViews = CalendarOptions<TItem>> = TOptionsOrViews extends CalendarOptions<TItem> ? BaseCalendarMethods<TOptionsOrViews> & MonthMethods<TItem, TOptionsOrViews> & WeekMethods<TItem, TOptionsOrViews> & DayMethods<TItem, TOptionsOrViews> : TOptionsOrViews extends CalendarViewOptions<TItem> ? BaseCalendarMethods<ViewsToOptions<TItem, TOptionsOrViews>> & MonthMethods<TItem, ViewsToOptions<TItem, TOptionsOrViews>> & WeekMethods<TItem, ViewsToOptions<TItem, TOptionsOrViews>> & DayMethods<TItem, ViewsToOptions<TItem, TOptionsOrViews>> : BaseCalendarMethods<CalendarOptions<TItem>> & MonthMethods<TItem, CalendarOptions<TItem>> & WeekMethods<TItem, CalendarOptions<TItem>> & DayMethods<TItem, CalendarOptions<TItem>>;
141
- type InferViewNames<V extends CalendarViewOptions<unknown>> = keyof V & string;
142
- type InferViewConfig<V extends CalendarViewOptions<unknown>, Name extends keyof V> = V[Name] extends infer Config ? {
143
- name: Name;
144
- config: Config;
145
- } : never;
146
201
  /**
147
- * Extract the item type from a Calendar type
202
+ * Extract the item type from a Calendar's options
148
203
  * @example type Item = CalendarItemType<typeof calendar>; // Post
149
204
  */
150
- type CalendarItemType<C extends Calendar<any, any>> = C extends Calendar<infer TItem, any> ? TItem : never;
205
+ type CalendarItemType<C extends CalendarInstance> = C['options'] extends CalendarOptions<infer TItem> ? TItem : never;
151
206
  /**
152
207
  * Extract the views configuration from a Calendar type
153
208
  * @example type Views = CalendarViewsConfig<typeof calendar>;
154
209
  */
155
- type CalendarViewsConfig<C extends Calendar<any, any>> = C extends Calendar<any, infer TOptions> ? TOptions extends {
210
+ type CalendarViewsConfig<C extends CalendarInstance> = C['options'] extends {
156
211
  views: infer V;
157
- } ? V : TOptions extends CalendarViewOptions<any> ? TOptions : never : never;
212
+ } ? V : never;
158
213
  /**
159
214
  * Check if a Calendar has a specific view configured
160
215
  * @example type HasMonth = HasViewType<typeof calendar, 'month'>; // true | false
161
216
  */
162
- type HasViewType<C extends Calendar<any, any>, V extends 'month' | 'week' | 'day'> = C extends Calendar<any, infer TOptions> ? HasView<TOptions, V> : false;
217
+ type HasViewType<C extends CalendarInstance, V extends 'month' | 'week' | 'day'> = C['options'] extends CalendarOptions<infer TItem> ? HasView<C['options'], V> : false;
163
218
  /**
164
219
  * Extract valid view names as a union from a Calendar
165
220
  * @example type Views = ValidViewNames<typeof calendar>; // 'month' | 'week'
166
221
  */
167
- type ValidViewNames<C extends Calendar<any, any>> = C extends Calendar<any, infer TOptions> ? ValidViews<TOptions> : never;
168
- /**
169
- * Extract the item type from CalendarOptions before creating a calendar
170
- * @example type Item = ItemTypeFromOptions<typeof options>; // Post
171
- */
172
- type ItemTypeFromOptions<O extends CalendarOptions<any>> = O extends CalendarOptions<infer TItem> ? TItem : never;
222
+ type ValidViewNames<C extends CalendarInstance> = C['options'] extends CalendarOptions<infer TItem> ? ValidViews<C['options']> : never;
173
223
  /**
174
224
  * Base props for components that receive a Calendar
175
225
  * @example
176
- * function MyComponent<C extends Calendar<any, any>>(
226
+ * function MyComponent<C extends CalendarInstance>(
177
227
  * props: CalendarComponentProps<C>
178
228
  * ) { ... }
179
229
  */
180
- type CalendarComponentProps<C extends Calendar<any, any>> = {
230
+ type CalendarComponentProps<C extends CalendarInstance> = {
181
231
  calendar: C;
182
232
  };
183
233
  /**
184
234
  * Require that a Calendar has month view configured
185
235
  * @example
186
- * function MonthView<C extends Calendar<any, any>>(
236
+ * function MonthView<C extends CalendarInstance>(
187
237
  * props: { calendar: RequireMonthView<C> }
188
238
  * ) {
189
239
  * // calendar.getMonth() is guaranteed to exist
190
240
  * }
191
241
  */
192
- type RequireMonthView<C extends Calendar<any, any>> = HasViewType<C, 'month'> extends true ? C : never;
242
+ type RequireMonthView<C extends CalendarInstance> = HasViewType<C, 'month'> extends true ? C : never;
193
243
  /**
194
244
  * Require that a Calendar has week view configured
195
245
  */
196
- type RequireWeekView<C extends Calendar<any, any>> = HasViewType<C, 'week'> extends true ? C : never;
246
+ type RequireWeekView<C extends CalendarInstance> = HasViewType<C, 'week'> extends true ? C : never;
197
247
  /**
198
248
  * Require that a Calendar has day view configured
199
249
  */
200
- type RequireDayView<C extends Calendar<any, any>> = HasViewType<C, 'day'> extends true ? C : never;
250
+ type RequireDayView<C extends CalendarInstance> = HasViewType<C, 'day'> extends true ? C : never;
251
+ /**
252
+ * Built-in view types
253
+ */
254
+ type ViewType = 'month' | 'week' | 'day';
255
+ /**
256
+ * Discriminated union of all view results
257
+ */
258
+ type ViewResult<TItem> = {
259
+ type: 'month';
260
+ data: CalendarMonth<TItem>;
261
+ } | {
262
+ type: 'week';
263
+ data: CalendarWeekView<TItem>;
264
+ } | {
265
+ type: 'day';
266
+ data: CalendarDayView<TItem>;
267
+ };
268
+ /**
269
+ * Conditional type that narrows ViewResult based on view name
270
+ * - If V is a specific view type, returns that specific result
271
+ * - If V is undefined, returns the full discriminated union
272
+ */
273
+ type ViewResultFor<TItem, V extends ViewType | undefined> = V extends 'month' ? {
274
+ type: 'month';
275
+ data: CalendarMonth<TItem>;
276
+ } : V extends 'week' ? {
277
+ type: 'week';
278
+ data: CalendarWeekView<TItem>;
279
+ } : V extends 'day' ? {
280
+ type: 'day';
281
+ data: CalendarDayView<TItem>;
282
+ } : ViewResult<TItem>;
201
283
 
202
284
  declare function functionalUpdate<T>(updater: T | ((old: T) => T), input: T): T;
203
285
  declare function createCalendarAccessor<T, A extends CalendarAccessor<T> = CalendarAccessor<T>>(accessor: A): A;
@@ -211,7 +293,6 @@ declare function goToToday(): {
211
293
  year: number;
212
294
  month: number;
213
295
  };
214
- declare function getWeekdays(weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6, locale?: string | string[], format?: 'short' | 'long' | 'narrow'): string[];
215
296
  declare function getMonthName(month: Temporal.PlainYearMonth, locale?: string): string;
216
297
  declare function formatTime(time: Temporal.PlainTime, locale?: string): string;
217
298
  declare function getTimeSlotHeight(slotDuration: number, hourHeight: number): number;
@@ -292,6 +373,16 @@ declare function getWeekDateRange(date: Temporal.PlainDate, timeZone: string, op
292
373
  */
293
374
  declare function getDayDateRange(date: Temporal.PlainDate, timeZone: string): DateRange;
294
375
 
376
+ /**
377
+ * Returns an array of weekday names starting from the specified day.
378
+ *
379
+ * @param weekStartsOn - Day the week starts on (0=Sunday, 1=Monday, ..., 6=Saturday). Defaults to 1 (Monday).
380
+ * @param locale - Locale for formatting weekday names. Defaults to 'en-US'.
381
+ * @param format - Format for weekday names: 'short' (Mon), 'long' (Monday), or 'narrow' (M). Defaults to 'short'.
382
+ * @returns Array of 7 weekday names starting from weekStartsOn.
383
+ */
384
+ declare function getWeekdays(weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6, locale?: string | string[], format?: 'short' | 'long' | 'narrow'): string[];
385
+
295
386
  declare function createCalendar<TItem, TOptions extends CalendarOptions<TItem>>(options: TOptions): Calendar<TItem, TOptions>;
296
387
 
297
388
  declare function createCalendarViews<TItem>(): <const TViews extends {
@@ -300,4 +391,4 @@ declare function createCalendarViews<TItem>(): <const TViews extends {
300
391
  day?: DayViewOptions<TItem>;
301
392
  }>(views: TViews) => TViews;
302
393
 
303
- export { type Calendar, type CalendarAccessor, type CalendarComponentProps, type CalendarDay, type CalendarDayView, type CalendarItemType, type CalendarMonth, type CalendarOptions, type CalendarState, type CalendarViewOptions, type CalendarViewsConfig, type CalendarWeek, type CalendarWeekView, type DateRange, type DateRangeBounds, type DayViewOptions, type HasViewType, type InferViewConfig, type InferViewNames, type ItemTypeFromOptions, type MonthViewOptions, type RequireDayView, type RequireMonthView, type RequireWeekView, type TimeSlot, type Updater, type ValidViewNames, type WeekDay, type WeekViewOptions, buildDay, buildMonth, buildWeek, convertToTimezone, createCalendar, createCalendarAccessor, createCalendarViews, createZonedDateTime, formatTime, functionalUpdate, getCurrentTimeZone, getDayDateRange, getDayRange, getEventPosition, getMonthDateRange, getMonthName, getMonthRange, getTimeSlotHeight, getTimezoneOffset, getWeekDateRange, getWeekRange, getWeekdays, goToToday, nextDay, nextMonth, nextWeek, previousDay, previousMonth, previousWeek };
394
+ export { type Calendar, type CalendarAccessor, type CalendarComponentProps, type CalendarDay, type CalendarDayView, type CalendarInstance, type CalendarItemType, type CalendarMonth, type CalendarOptions, type CalendarState, type CalendarViewOptions, type CalendarViewsConfig, type CalendarWeek, type CalendarWeekView, type DateRange, type DateRangeBounds, type DayViewOptions, type HasViewType, type MonthViewOptions, type RequireDayView, type RequireMonthView, type RequireWeekView, type TimeSlot, type Updater, type ValidViewNames, type ViewResult, type ViewResultFor, type ViewType, type WeekDay, type WeekViewOptions, buildDay, buildMonth, buildWeek, convertToTimezone, createCalendar, createCalendarAccessor, createCalendarViews, createZonedDateTime, formatTime, functionalUpdate, getCurrentTimeZone, getDayDateRange, getDayRange, getEventPosition, getMonthDateRange, getMonthName, getMonthRange, getTimeSlotHeight, getTimezoneOffset, getWeekDateRange, getWeekRange, getWeekdays, goToToday, nextDay, nextMonth, nextWeek, previousDay, previousMonth, previousWeek };
package/dist/index.d.ts CHANGED
@@ -7,6 +7,8 @@ type CalendarAccessor<TItem> = {
7
7
  getEnd?: (item: TItem) => Temporal.ZonedDateTime;
8
8
  };
9
9
  type CalendarDay<TItem = unknown> = {
10
+ /** Unique identifier for this day within the view context. Use as React/Vue key. */
11
+ id: string;
10
12
  date: Temporal.PlainDate;
11
13
  isCurrentMonth: boolean;
12
14
  isToday: boolean;
@@ -18,6 +20,8 @@ type CalendarMonth<TItem = unknown> = {
18
20
  month: Temporal.PlainYearMonth;
19
21
  };
20
22
  type WeekDay<TItem = unknown> = {
23
+ /** Unique identifier for this day within the view context. Use as React/Vue key. */
24
+ id: string;
21
25
  date: Temporal.PlainDate;
22
26
  isToday: boolean;
23
27
  items: TItem[];
@@ -29,12 +33,16 @@ type CalendarWeekView<TItem = unknown> = {
29
33
  weekEnd: Temporal.PlainDate;
30
34
  };
31
35
  type TimeSlot<TItem = unknown> = {
36
+ /** Unique identifier for this time slot. Use as React/Vue key. */
37
+ id: string;
32
38
  hour: number;
33
39
  minute: number;
34
40
  time: Temporal.PlainTime;
35
41
  items: TItem[];
36
42
  };
37
43
  type CalendarDayView<TItem = unknown> = {
44
+ /** Unique identifier for this day view. Use as React/Vue key. */
45
+ id: string;
38
46
  date: Temporal.PlainDate;
39
47
  isToday: boolean;
40
48
  timeSlots: TimeSlot<TItem>[];
@@ -73,17 +81,16 @@ type CalendarViewOptions<TItem> = {
73
81
  day?: DayViewOptions<TItem>;
74
82
  };
75
83
  type CalendarOptions<TItem> = {
76
- data: TItem[];
77
84
  views: CalendarViewOptions<TItem>;
78
85
  timeZone?: 'UTC' | string;
79
86
  state?: Partial<CalendarState>;
80
- onStateChange?: (updater: Updater<CalendarState>) => void;
87
+ onStateChange?: (state: CalendarState) => void;
81
88
  };
82
89
  type HasView<Options, View extends 'month' | 'week' | 'day'> = Options extends {
83
90
  views: infer V;
84
91
  } ? V extends Record<View, unknown> ? V[View] extends undefined ? false : true : false : false;
85
92
  type MonthMethods<TItem, TOptions> = HasView<TOptions, 'month'> extends true ? {
86
- getMonth(): CalendarMonth<TItem>;
93
+ getMonth(data?: TItem[]): CalendarMonth<TItem>;
87
94
  nextMonth(): void;
88
95
  previousMonth(): void;
89
96
  goToMonth(year: number, month: number): void;
@@ -94,7 +101,7 @@ type MonthMethods<TItem, TOptions> = HasView<TOptions, 'month'> extends true ? {
94
101
  goToMonth?: never;
95
102
  };
96
103
  type WeekMethods<TItem, TOptions> = HasView<TOptions, 'week'> extends true ? {
97
- getWeek(): CalendarWeekView<TItem>;
104
+ getWeek(data?: TItem[]): CalendarWeekView<TItem>;
98
105
  nextWeek(): void;
99
106
  previousWeek(): void;
100
107
  } : {
@@ -103,7 +110,7 @@ type WeekMethods<TItem, TOptions> = HasView<TOptions, 'week'> extends true ? {
103
110
  previousWeek?: never;
104
111
  };
105
112
  type DayMethods<TItem, TOptions> = HasView<TOptions, 'day'> extends true ? {
106
- getDay(): CalendarDayView<TItem>;
113
+ getDay(data?: TItem[]): CalendarDayView<TItem>;
107
114
  nextDay(): void;
108
115
  previousDay(): void;
109
116
  } : {
@@ -126,6 +133,7 @@ type BaseCalendarMethods<Options> = {
126
133
  currentView: ValidViews<Options>;
127
134
  setCurrentView(view: ValidViews<Options>): void;
128
135
  dateRange: DateRange;
136
+ getDateRange(view?: ValidViews<Options>): DateRange;
129
137
  options: Options;
130
138
  setOptions(updater: (old: Options) => Options): void;
131
139
  store: _tanstack_store.Store<CalendarState>;
@@ -133,71 +141,145 @@ type BaseCalendarMethods<Options> = {
133
141
  hasWeekView(): boolean;
134
142
  hasDayView(): boolean;
135
143
  };
136
- type ViewsToOptions<TItem, TViews extends CalendarViewOptions<TItem>> = {
137
- data: TItem[];
138
- views: TViews;
144
+ /**
145
+ * Unified calendar type with conditional methods based on configured views.
146
+ *
147
+ * @example
148
+ * // Calendar with specific options - methods are conditional
149
+ * const calendar = createCalendar<Post, typeof options>(options);
150
+ * calendar.getMonth([]); // Only available if month view configured
151
+ *
152
+ * @example
153
+ * // Generic calendar type - all methods available
154
+ * type AnyPostCalendar = Calendar<Post>;
155
+ */
156
+ type Calendar<TItem, TOptions extends CalendarOptions<TItem> = CalendarOptions<TItem>> = BaseCalendarMethods<TOptions> & MonthMethods<TItem, TOptions> & WeekMethods<TItem, TOptions> & DayMethods<TItem, TOptions>;
157
+ /**
158
+ * Calendar instance with all view methods available and typed items.
159
+ * Use this for contexts where the specific view configuration is unknown at compile time,
160
+ * but you still want type-safe item data.
161
+ *
162
+ * @example
163
+ * // In React context - store with unknown, consume with specific type
164
+ * const context = createContext<CalendarInstance<unknown> | null>(null);
165
+ *
166
+ * // Consumer gets typed data
167
+ * function useCalendar<TItem>(): CalendarInstance<TItem> {
168
+ * return useContext(context) as CalendarInstance<TItem>;
169
+ * }
170
+ */
171
+ type CalendarInstance<TItem = unknown> = {
172
+ getMonth(data?: TItem[]): CalendarMonth<TItem>;
173
+ getWeek(data?: TItem[]): CalendarWeekView<TItem>;
174
+ getDay(data?: TItem[]): CalendarDayView<TItem>;
175
+ getTitle(view?: ViewType, locales?: Temporal.LocalesArgument, options?: globalThis.Intl.DateTimeFormatOptions): string;
176
+ getState(): CalendarState;
177
+ setState(updater: CalendarState | ((old: CalendarState) => Partial<CalendarState>)): void;
178
+ goToToday(): void;
179
+ goToDate(date: Temporal.PlainDate): void;
180
+ next(view?: ViewType): void;
181
+ previous(view?: ViewType): void;
182
+ views: ReadonlyArray<string>;
183
+ currentView: string;
184
+ setCurrentView(view: string): void;
185
+ dateRange: DateRange;
186
+ getDateRange(view?: ViewType): DateRange;
187
+ options: CalendarOptions<TItem>;
188
+ setOptions(updater: (old: CalendarOptions<TItem>) => CalendarOptions<TItem>): void;
189
+ store: _tanstack_store.Store<CalendarState>;
190
+ nextMonth(): void;
191
+ previousMonth(): void;
192
+ goToMonth(year: number, month: number): void;
193
+ nextWeek(): void;
194
+ previousWeek(): void;
195
+ nextDay(): void;
196
+ previousDay(): void;
197
+ hasMonthView(): boolean;
198
+ hasWeekView(): boolean;
199
+ hasDayView(): boolean;
139
200
  };
140
- type Calendar<TItem, TOptionsOrViews = CalendarOptions<TItem>> = TOptionsOrViews extends CalendarOptions<TItem> ? BaseCalendarMethods<TOptionsOrViews> & MonthMethods<TItem, TOptionsOrViews> & WeekMethods<TItem, TOptionsOrViews> & DayMethods<TItem, TOptionsOrViews> : TOptionsOrViews extends CalendarViewOptions<TItem> ? BaseCalendarMethods<ViewsToOptions<TItem, TOptionsOrViews>> & MonthMethods<TItem, ViewsToOptions<TItem, TOptionsOrViews>> & WeekMethods<TItem, ViewsToOptions<TItem, TOptionsOrViews>> & DayMethods<TItem, ViewsToOptions<TItem, TOptionsOrViews>> : BaseCalendarMethods<CalendarOptions<TItem>> & MonthMethods<TItem, CalendarOptions<TItem>> & WeekMethods<TItem, CalendarOptions<TItem>> & DayMethods<TItem, CalendarOptions<TItem>>;
141
- type InferViewNames<V extends CalendarViewOptions<unknown>> = keyof V & string;
142
- type InferViewConfig<V extends CalendarViewOptions<unknown>, Name extends keyof V> = V[Name] extends infer Config ? {
143
- name: Name;
144
- config: Config;
145
- } : never;
146
201
  /**
147
- * Extract the item type from a Calendar type
202
+ * Extract the item type from a Calendar's options
148
203
  * @example type Item = CalendarItemType<typeof calendar>; // Post
149
204
  */
150
- type CalendarItemType<C extends Calendar<any, any>> = C extends Calendar<infer TItem, any> ? TItem : never;
205
+ type CalendarItemType<C extends CalendarInstance> = C['options'] extends CalendarOptions<infer TItem> ? TItem : never;
151
206
  /**
152
207
  * Extract the views configuration from a Calendar type
153
208
  * @example type Views = CalendarViewsConfig<typeof calendar>;
154
209
  */
155
- type CalendarViewsConfig<C extends Calendar<any, any>> = C extends Calendar<any, infer TOptions> ? TOptions extends {
210
+ type CalendarViewsConfig<C extends CalendarInstance> = C['options'] extends {
156
211
  views: infer V;
157
- } ? V : TOptions extends CalendarViewOptions<any> ? TOptions : never : never;
212
+ } ? V : never;
158
213
  /**
159
214
  * Check if a Calendar has a specific view configured
160
215
  * @example type HasMonth = HasViewType<typeof calendar, 'month'>; // true | false
161
216
  */
162
- type HasViewType<C extends Calendar<any, any>, V extends 'month' | 'week' | 'day'> = C extends Calendar<any, infer TOptions> ? HasView<TOptions, V> : false;
217
+ type HasViewType<C extends CalendarInstance, V extends 'month' | 'week' | 'day'> = C['options'] extends CalendarOptions<infer TItem> ? HasView<C['options'], V> : false;
163
218
  /**
164
219
  * Extract valid view names as a union from a Calendar
165
220
  * @example type Views = ValidViewNames<typeof calendar>; // 'month' | 'week'
166
221
  */
167
- type ValidViewNames<C extends Calendar<any, any>> = C extends Calendar<any, infer TOptions> ? ValidViews<TOptions> : never;
168
- /**
169
- * Extract the item type from CalendarOptions before creating a calendar
170
- * @example type Item = ItemTypeFromOptions<typeof options>; // Post
171
- */
172
- type ItemTypeFromOptions<O extends CalendarOptions<any>> = O extends CalendarOptions<infer TItem> ? TItem : never;
222
+ type ValidViewNames<C extends CalendarInstance> = C['options'] extends CalendarOptions<infer TItem> ? ValidViews<C['options']> : never;
173
223
  /**
174
224
  * Base props for components that receive a Calendar
175
225
  * @example
176
- * function MyComponent<C extends Calendar<any, any>>(
226
+ * function MyComponent<C extends CalendarInstance>(
177
227
  * props: CalendarComponentProps<C>
178
228
  * ) { ... }
179
229
  */
180
- type CalendarComponentProps<C extends Calendar<any, any>> = {
230
+ type CalendarComponentProps<C extends CalendarInstance> = {
181
231
  calendar: C;
182
232
  };
183
233
  /**
184
234
  * Require that a Calendar has month view configured
185
235
  * @example
186
- * function MonthView<C extends Calendar<any, any>>(
236
+ * function MonthView<C extends CalendarInstance>(
187
237
  * props: { calendar: RequireMonthView<C> }
188
238
  * ) {
189
239
  * // calendar.getMonth() is guaranteed to exist
190
240
  * }
191
241
  */
192
- type RequireMonthView<C extends Calendar<any, any>> = HasViewType<C, 'month'> extends true ? C : never;
242
+ type RequireMonthView<C extends CalendarInstance> = HasViewType<C, 'month'> extends true ? C : never;
193
243
  /**
194
244
  * Require that a Calendar has week view configured
195
245
  */
196
- type RequireWeekView<C extends Calendar<any, any>> = HasViewType<C, 'week'> extends true ? C : never;
246
+ type RequireWeekView<C extends CalendarInstance> = HasViewType<C, 'week'> extends true ? C : never;
197
247
  /**
198
248
  * Require that a Calendar has day view configured
199
249
  */
200
- type RequireDayView<C extends Calendar<any, any>> = HasViewType<C, 'day'> extends true ? C : never;
250
+ type RequireDayView<C extends CalendarInstance> = HasViewType<C, 'day'> extends true ? C : never;
251
+ /**
252
+ * Built-in view types
253
+ */
254
+ type ViewType = 'month' | 'week' | 'day';
255
+ /**
256
+ * Discriminated union of all view results
257
+ */
258
+ type ViewResult<TItem> = {
259
+ type: 'month';
260
+ data: CalendarMonth<TItem>;
261
+ } | {
262
+ type: 'week';
263
+ data: CalendarWeekView<TItem>;
264
+ } | {
265
+ type: 'day';
266
+ data: CalendarDayView<TItem>;
267
+ };
268
+ /**
269
+ * Conditional type that narrows ViewResult based on view name
270
+ * - If V is a specific view type, returns that specific result
271
+ * - If V is undefined, returns the full discriminated union
272
+ */
273
+ type ViewResultFor<TItem, V extends ViewType | undefined> = V extends 'month' ? {
274
+ type: 'month';
275
+ data: CalendarMonth<TItem>;
276
+ } : V extends 'week' ? {
277
+ type: 'week';
278
+ data: CalendarWeekView<TItem>;
279
+ } : V extends 'day' ? {
280
+ type: 'day';
281
+ data: CalendarDayView<TItem>;
282
+ } : ViewResult<TItem>;
201
283
 
202
284
  declare function functionalUpdate<T>(updater: T | ((old: T) => T), input: T): T;
203
285
  declare function createCalendarAccessor<T, A extends CalendarAccessor<T> = CalendarAccessor<T>>(accessor: A): A;
@@ -211,7 +293,6 @@ declare function goToToday(): {
211
293
  year: number;
212
294
  month: number;
213
295
  };
214
- declare function getWeekdays(weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6, locale?: string | string[], format?: 'short' | 'long' | 'narrow'): string[];
215
296
  declare function getMonthName(month: Temporal.PlainYearMonth, locale?: string): string;
216
297
  declare function formatTime(time: Temporal.PlainTime, locale?: string): string;
217
298
  declare function getTimeSlotHeight(slotDuration: number, hourHeight: number): number;
@@ -292,6 +373,16 @@ declare function getWeekDateRange(date: Temporal.PlainDate, timeZone: string, op
292
373
  */
293
374
  declare function getDayDateRange(date: Temporal.PlainDate, timeZone: string): DateRange;
294
375
 
376
+ /**
377
+ * Returns an array of weekday names starting from the specified day.
378
+ *
379
+ * @param weekStartsOn - Day the week starts on (0=Sunday, 1=Monday, ..., 6=Saturday). Defaults to 1 (Monday).
380
+ * @param locale - Locale for formatting weekday names. Defaults to 'en-US'.
381
+ * @param format - Format for weekday names: 'short' (Mon), 'long' (Monday), or 'narrow' (M). Defaults to 'short'.
382
+ * @returns Array of 7 weekday names starting from weekStartsOn.
383
+ */
384
+ declare function getWeekdays(weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6, locale?: string | string[], format?: 'short' | 'long' | 'narrow'): string[];
385
+
295
386
  declare function createCalendar<TItem, TOptions extends CalendarOptions<TItem>>(options: TOptions): Calendar<TItem, TOptions>;
296
387
 
297
388
  declare function createCalendarViews<TItem>(): <const TViews extends {
@@ -300,4 +391,4 @@ declare function createCalendarViews<TItem>(): <const TViews extends {
300
391
  day?: DayViewOptions<TItem>;
301
392
  }>(views: TViews) => TViews;
302
393
 
303
- export { type Calendar, type CalendarAccessor, type CalendarComponentProps, type CalendarDay, type CalendarDayView, type CalendarItemType, type CalendarMonth, type CalendarOptions, type CalendarState, type CalendarViewOptions, type CalendarViewsConfig, type CalendarWeek, type CalendarWeekView, type DateRange, type DateRangeBounds, type DayViewOptions, type HasViewType, type InferViewConfig, type InferViewNames, type ItemTypeFromOptions, type MonthViewOptions, type RequireDayView, type RequireMonthView, type RequireWeekView, type TimeSlot, type Updater, type ValidViewNames, type WeekDay, type WeekViewOptions, buildDay, buildMonth, buildWeek, convertToTimezone, createCalendar, createCalendarAccessor, createCalendarViews, createZonedDateTime, formatTime, functionalUpdate, getCurrentTimeZone, getDayDateRange, getDayRange, getEventPosition, getMonthDateRange, getMonthName, getMonthRange, getTimeSlotHeight, getTimezoneOffset, getWeekDateRange, getWeekRange, getWeekdays, goToToday, nextDay, nextMonth, nextWeek, previousDay, previousMonth, previousWeek };
394
+ export { type Calendar, type CalendarAccessor, type CalendarComponentProps, type CalendarDay, type CalendarDayView, type CalendarInstance, type CalendarItemType, type CalendarMonth, type CalendarOptions, type CalendarState, type CalendarViewOptions, type CalendarViewsConfig, type CalendarWeek, type CalendarWeekView, type DateRange, type DateRangeBounds, type DayViewOptions, type HasViewType, type MonthViewOptions, type RequireDayView, type RequireMonthView, type RequireWeekView, type TimeSlot, type Updater, type ValidViewNames, type ViewResult, type ViewResultFor, type ViewType, type WeekDay, type WeekViewOptions, buildDay, buildMonth, buildWeek, convertToTimezone, createCalendar, createCalendarAccessor, createCalendarViews, createZonedDateTime, formatTime, functionalUpdate, getCurrentTimeZone, getDayDateRange, getDayRange, getEventPosition, getMonthDateRange, getMonthName, getMonthRange, getTimeSlotHeight, getTimezoneOffset, getWeekDateRange, getWeekRange, getWeekdays, goToToday, nextDay, nextMonth, nextWeek, previousDay, previousMonth, previousWeek };
package/dist/index.js CHANGED
@@ -28,16 +28,6 @@ function goToToday() {
28
28
  const today = Temporal.Now.plainDateISO();
29
29
  return { year: today.year, month: today.month };
30
30
  }
31
- function getWeekdays(weekStartsOn = 1, locale = "en-US", format = "short") {
32
- const referenceDate = Temporal.PlainDate.from("2023-01-02");
33
- const days = [];
34
- for (let i = 0; i < 7; i++) {
35
- const offset = (weekStartsOn - 1 + i + 7) % 7;
36
- const date = referenceDate.add({ days: offset });
37
- days.push(date.toLocaleString(locale, { weekday: format }));
38
- }
39
- return days;
40
- }
41
31
  function getMonthName(month, locale = "en-US") {
42
32
  return month.toPlainDate({ day: 1 }).toLocaleString(locale, { month: "long" });
43
33
  }
@@ -133,6 +123,7 @@ function buildMonth(year, month, options) {
133
123
  }
134
124
  const dateKey = currentDate.toString();
135
125
  weeks[weeks.length - 1].push({
126
+ id: `${yearMonth.toString()}-${dateKey}`,
136
127
  date: currentDate,
137
128
  isCurrentMonth: currentDate.month === month,
138
129
  isToday: Temporal2.PlainDate.compare(currentDate, today) === 0,
@@ -160,7 +151,7 @@ import { Temporal as Temporal3 } from "@js-temporal/polyfill";
160
151
  function buildDay(date, options) {
161
152
  const startHour = options?.startHour ?? 0;
162
153
  const endHour = options?.endHour ?? 24;
163
- const slotDuration = options?.slotDuration ?? 30;
154
+ const slotDuration = options?.slotDuration ?? 60;
164
155
  const today = options?.today ?? Temporal3.Now.plainDateISO();
165
156
  const data = options?.data ?? [];
166
157
  const accessor = options?.accessor;
@@ -174,6 +165,7 @@ function buildDay(date, options) {
174
165
  }
175
166
  }
176
167
  const timeSlots = [];
168
+ const dateStr = date.toString();
177
169
  let currentHour = startHour;
178
170
  let currentMinute = 0;
179
171
  while (currentHour < endHour) {
@@ -200,6 +192,7 @@ function buildDay(date, options) {
200
192
  }
201
193
  }
202
194
  timeSlots.push({
195
+ id: `${dateStr}-${String(currentHour).padStart(2, "0")}:${String(currentMinute).padStart(2, "0")}`,
203
196
  hour: currentHour,
204
197
  minute: currentMinute,
205
198
  time: slotStart,
@@ -212,6 +205,7 @@ function buildDay(date, options) {
212
205
  }
213
206
  }
214
207
  return {
208
+ id: dateStr,
215
209
  date,
216
210
  isToday: Temporal3.PlainDate.compare(date, today) === 0,
217
211
  timeSlots,
@@ -258,6 +252,7 @@ function buildWeek(date, options) {
258
252
  timeSlots = dayView.timeSlots;
259
253
  }
260
254
  days.push({
255
+ id: `${weekStart.toString()}-${dateKey}`,
261
256
  date: currentDate,
262
257
  isToday: Temporal4.PlainDate.compare(currentDate, today) === 0,
263
258
  items: itemsByDate.get(dateKey) ?? [],
@@ -330,8 +325,20 @@ function getDayDateRange(date, timeZone) {
330
325
  return { start: startZoned, end: endZoned };
331
326
  }
332
327
 
333
- // src/core/calendar.ts
328
+ // src/utils/getWeekdays.ts
334
329
  import { Temporal as Temporal6 } from "@js-temporal/polyfill";
330
+ function getWeekdays(weekStartsOn = 1, locale = "en-US", format = "short") {
331
+ const sunday = Temporal6.PlainDate.from("2023-01-01");
332
+ const days = [];
333
+ for (let i = 0; i < 7; i++) {
334
+ const date = sunday.add({ days: (weekStartsOn + i) % 7 });
335
+ days.push(date.toLocaleString(locale, { weekday: format }));
336
+ }
337
+ return days;
338
+ }
339
+
340
+ // src/core/calendar.ts
341
+ import { Temporal as Temporal7 } from "@js-temporal/polyfill";
335
342
  import { Store } from "@tanstack/store";
336
343
  function computeDateRange(view, referenceDate, timeZone, weekStartsOn = 1) {
337
344
  let start;
@@ -356,22 +363,22 @@ function computeDateRange(view, referenceDate, timeZone, weekStartsOn = 1) {
356
363
  }
357
364
  const startZoned = start.toZonedDateTime({
358
365
  timeZone,
359
- plainTime: Temporal6.PlainTime.from("00:00:00")
366
+ plainTime: Temporal7.PlainTime.from("00:00:00")
360
367
  });
361
368
  const endZoned = end.toZonedDateTime({
362
369
  timeZone,
363
- plainTime: Temporal6.PlainTime.from("23:59:59.999")
370
+ plainTime: Temporal7.PlainTime.from("23:59:59.999")
364
371
  });
365
372
  return { start: startZoned, end: endZoned };
366
373
  }
367
374
  function createCalendar(options) {
368
375
  const configuredViews = Object.keys(options.views);
369
376
  const defaultView = configuredViews[0];
370
- const timeZone = options.timeZone || Temporal6.Now.timeZoneId();
377
+ const timeZone = options.timeZone || Temporal7.Now.timeZoneId();
371
378
  const monthView = options.views.month;
372
379
  const weekView = options.views.week;
373
380
  const weekStartsOn = monthView?.weekStartsOn ?? weekView?.weekStartsOn ?? 1;
374
- const initialReferenceDate = options.state?.referenceDate || Temporal6.Now.plainDateISO();
381
+ const initialReferenceDate = options.state?.referenceDate || Temporal7.Now.plainDateISO();
375
382
  const initialView = options.state?.currentView || defaultView;
376
383
  const initialDateRange = computeDateRange(
377
384
  initialView,
@@ -396,18 +403,18 @@ function createCalendar(options) {
396
403
  dateRange: initialDateRange,
397
404
  ...resolvedOptions.state
398
405
  });
399
- const getMonthImpl = () => {
406
+ const getMonthImpl = (data = []) => {
400
407
  const state = store.state;
401
408
  const { year, month } = state.referenceDate;
402
409
  const monthView2 = _options.views.month;
403
410
  if (!monthView2) throw new Error("Month view not configured");
404
411
  return buildMonth(year, month, {
405
412
  weekStartsOn: monthView2.weekStartsOn,
406
- data: _options.data,
413
+ data,
407
414
  accessor: monthView2.accessor
408
415
  });
409
416
  };
410
- const getWeekImpl = () => {
417
+ const getWeekImpl = (data = []) => {
411
418
  const state = store.state;
412
419
  const weekView2 = _options.views.week;
413
420
  if (!weekView2) throw new Error("Week view not configured");
@@ -416,11 +423,11 @@ function createCalendar(options) {
416
423
  startHour: weekView2.startHour,
417
424
  endHour: weekView2.endHour,
418
425
  slotDuration: weekView2.slotDuration,
419
- data: _options.data,
426
+ data,
420
427
  accessor: weekView2.accessor
421
428
  });
422
429
  };
423
- const getDayImpl = () => {
430
+ const getDayImpl = (data = []) => {
424
431
  const state = store.state;
425
432
  const dayView = _options.views.day;
426
433
  if (!dayView) throw new Error("Day view not configured");
@@ -428,7 +435,7 @@ function createCalendar(options) {
428
435
  startHour: dayView.startHour,
429
436
  endHour: dayView.endHour,
430
437
  slotDuration: dayView.slotDuration,
431
- data: _options.data,
438
+ data,
432
439
  accessor: dayView.accessor
433
440
  });
434
441
  };
@@ -456,7 +463,7 @@ function createCalendar(options) {
456
463
  };
457
464
  const nextMonthImpl = () => {
458
465
  setStateImpl((old) => {
459
- const current = Temporal6.PlainYearMonth.from({
466
+ const current = Temporal7.PlainYearMonth.from({
460
467
  year: old.referenceDate.year,
461
468
  month: old.referenceDate.month
462
469
  });
@@ -468,7 +475,7 @@ function createCalendar(options) {
468
475
  };
469
476
  const previousMonthImpl = () => {
470
477
  setStateImpl((old) => {
471
- const current = Temporal6.PlainYearMonth.from({
478
+ const current = Temporal7.PlainYearMonth.from({
472
479
  year: old.referenceDate.year,
473
480
  month: old.referenceDate.month
474
481
  });
@@ -553,7 +560,7 @@ function createCalendar(options) {
553
560
  previousMonth: previousMonthImpl,
554
561
  goToMonth(year, month) {
555
562
  setStateImpl(() => ({
556
- referenceDate: Temporal6.PlainDate.from({ year, month, day: 1 })
563
+ referenceDate: Temporal7.PlainDate.from({ year, month, day: 1 })
557
564
  }));
558
565
  },
559
566
  nextWeek: nextWeekImpl,
@@ -562,7 +569,7 @@ function createCalendar(options) {
562
569
  previousDay: previousDayImpl,
563
570
  goToToday() {
564
571
  setStateImpl(() => ({
565
- referenceDate: Temporal6.Now.plainDateISO()
572
+ referenceDate: Temporal7.Now.plainDateISO()
566
573
  }));
567
574
  },
568
575
  goToDate(date) {
@@ -613,6 +620,15 @@ function createCalendar(options) {
613
620
  get dateRange() {
614
621
  return store.state.dateRange;
615
622
  },
623
+ getDateRange(view) {
624
+ const effectiveView = view ?? store.state.currentView ?? defaultView;
625
+ return computeDateRange(
626
+ effectiveView,
627
+ store.state.referenceDate,
628
+ timeZone,
629
+ weekStartsOn
630
+ );
631
+ },
616
632
  get options() {
617
633
  return _options;
618
634
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobrand/calendar-core",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "Lightweight utility library for building calendars using the Temporal API",
5
5
  "private": false,
6
6
  "publishConfig": {