mirror of
https://github.com/hanzoai/calendar.git
synced 2026-10-30 14:46:43 +00:00
52d5261b51
* display UTM parameters * persist view (list | calendar) in localStorage * move to next page on clicking "next" of last item in the page improve navigation ^ Conflicts: ^ apps/web/modules/bookings/components/BookingCalendarContainer.tsx * fix navigation on calendar view * Delete apps/web/modules/bookings/NAVIGATION_IMPLEMENTATION.md * avoid page size being 0 * check feature flag on user level * use map to improve useBookingListData * address feedback * feat(bookings): add smart navigation for calendar view - Add sort option to tRPC bookings.get schema and handler - Create NAVIGATION_PROBE_WINDOW_MONTHS constant (3 months) - Create useNearestFutureBooking hook to find nearest future booking - Create useNearestPastBooking hook to find nearest past booking - Update useCalendarNavigationCapabilities to use probe results - Update BookingCalendarContainer to wire up probe hooks This enables the booking details sheet to: - Disable next/prev buttons when no bookings exist in that direction - Jump directly to the week containing the nearest booking Co-Authored-By: [email protected] <[email protected]> * fix type error * feat: add booking selection state when using slideover (#25637) * implement booking selection when using slideover * remove pixel shift * fix * auto scroll to selected event on calendar * make DateValues header sticky when scrolling --------- Co-authored-by: Eunjae Lee <[email protected]> * prefetch previous / next weeks on calendar view * clean up classes * handle "fetched & but no data" situation more correctly in useCalendarAutoSelector --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: sean-brydon <[email protected]>
106 lines
3.6 KiB
TypeScript
106 lines
3.6 KiB
TypeScript
import React from "react";
|
|
import { createStore, useStore } from "zustand";
|
|
import type { StoreApi } from "zustand";
|
|
|
|
import dayjs from "@calcom/dayjs";
|
|
import { CURRENT_TIMEZONE } from "@calcom/lib/timezoneConstants";
|
|
|
|
import type {
|
|
CalendarComponentProps,
|
|
CalendarPublicActions,
|
|
CalendarState,
|
|
CalendarStoreProps,
|
|
} from "../types/state";
|
|
import { mergeOverlappingDateRanges, weekdayDates } from "../utils";
|
|
|
|
const defaultState: CalendarComponentProps = {
|
|
view: "week",
|
|
startDate: weekdayDates(0, new Date()).startDate,
|
|
endDate: weekdayDates(0, new Date()).endDate,
|
|
events: [],
|
|
startHour: 0,
|
|
endHour: 23,
|
|
gridCellsPerHour: 4,
|
|
timezone: CURRENT_TIMEZONE,
|
|
showBackgroundPattern: true,
|
|
showBorder: true,
|
|
borderColor: "default",
|
|
showTimezone: false,
|
|
};
|
|
|
|
export function createCalendarStore(initial?: Partial<CalendarComponentProps>): StoreApi<CalendarStoreProps> {
|
|
return createStore<CalendarStoreProps>((set) => ({
|
|
...defaultState,
|
|
...initial,
|
|
setView: (view: CalendarComponentProps["view"]) => set({ view }),
|
|
setStartDate: (startDate: CalendarComponentProps["startDate"]) => set({ startDate }),
|
|
setEndDate: (endDate: CalendarComponentProps["endDate"]) => set({ endDate }),
|
|
setEvents: (events: CalendarComponentProps["events"]) => set({ events }),
|
|
// This looks a bit odd but init state only overrides the public props + actions as we don't want to override our internal state
|
|
initState: (state: CalendarState & CalendarPublicActions) => {
|
|
// Handle sorting of events if required
|
|
let events = state.events;
|
|
|
|
if (state.sortEvents) {
|
|
events = [...state.events].sort((a, b) => dayjs(a.start).valueOf() - dayjs(b.start).valueOf());
|
|
}
|
|
const blockingDates = mergeOverlappingDateRanges(state.blockingDates || []); // We merge overlapping dates so we don't get duplicate blocking "Cells" in the UI
|
|
|
|
set({
|
|
...state,
|
|
blockingDates,
|
|
events,
|
|
selectedBookingUid: state.selectedBookingUid,
|
|
});
|
|
},
|
|
setSelectedEvent: (event) => set({ selectedEvent: event }),
|
|
handleDateChange: (payload) =>
|
|
set((state) => {
|
|
const { startDate, endDate } = state;
|
|
if (payload === "INCREMENT") {
|
|
const newStartDate = dayjs(startDate).add(1, state.view).toDate();
|
|
const newEndDate = dayjs(endDate).add(1, state.view).toDate();
|
|
|
|
// Do nothing if
|
|
if (
|
|
(state.minDate && newStartDate < state.minDate) ||
|
|
(state.maxDate && newEndDate > state.maxDate)
|
|
) {
|
|
return {
|
|
startDate,
|
|
endDate,
|
|
};
|
|
}
|
|
|
|
// We call this callback if we have it -> Allows you to change your state outside of the component
|
|
state.onDateChange?.(newStartDate, newEndDate);
|
|
return {
|
|
startDate: newStartDate,
|
|
endDate: newEndDate,
|
|
};
|
|
}
|
|
const newStartDate = dayjs(startDate).subtract(1, state.view).toDate();
|
|
const newEndDate = dayjs(endDate).subtract(1, state.view).toDate();
|
|
state.onDateChange?.(newStartDate, newEndDate);
|
|
return {
|
|
startDate: newStartDate,
|
|
endDate: newEndDate,
|
|
};
|
|
}),
|
|
}));
|
|
}
|
|
|
|
export const CalendarStoreContext = React.createContext<StoreApi<CalendarStoreProps> | null>(null);
|
|
|
|
export function useCalendarStore<T>(
|
|
selector: (state: CalendarStoreProps) => T,
|
|
equalityFn?: (a: T, b: T) => boolean
|
|
): T {
|
|
const store = React.useContext(CalendarStoreContext);
|
|
if (!store) {
|
|
throw new Error("useCalendarStore must be used within a CalendarStoreProvider");
|
|
}
|
|
|
|
return useStore(store, selector, equalityFn);
|
|
}
|