@schedule-x/current-time
Advanced tools
Comparing version 1.43.0 to 1.44.0
@@ -1,51 +0,51 @@ | ||
import { Signal } from '@preact/signals' | ||
import { JSXInternal } from 'preact/src/jsx' | ||
import { Signal } from "@preact/signals"; | ||
import { JSXInternal } from "preact/src/jsx"; | ||
declare enum WeekDay { | ||
SUNDAY = 0, | ||
MONDAY = 1, | ||
TUESDAY = 2, | ||
WEDNESDAY = 3, | ||
THURSDAY = 4, | ||
FRIDAY = 5, | ||
SATURDAY = 6, | ||
SUNDAY = 0, | ||
MONDAY = 1, | ||
TUESDAY = 2, | ||
WEDNESDAY = 3, | ||
THURSDAY = 4, | ||
FRIDAY = 5, | ||
SATURDAY = 6 | ||
} | ||
type WeekWithDates = Date[] | ||
type MonthWithDates = Date[][] | ||
type WeekWithDates = Date[]; | ||
type MonthWithDates = Date[][]; | ||
declare enum Month { | ||
JANUARY = 0, | ||
FEBRUARY = 1, | ||
MARCH = 2, | ||
APRIL = 3, | ||
MAY = 4, | ||
JUNE = 5, | ||
JULY = 6, | ||
AUGUST = 7, | ||
SEPTEMBER = 8, | ||
OCTOBER = 9, | ||
NOVEMBER = 10, | ||
DECEMBER = 11, | ||
JANUARY = 0, | ||
FEBRUARY = 1, | ||
MARCH = 2, | ||
APRIL = 3, | ||
MAY = 4, | ||
JUNE = 5, | ||
JULY = 6, | ||
AUGUST = 7, | ||
SEPTEMBER = 8, | ||
OCTOBER = 9, | ||
NOVEMBER = 10, | ||
DECEMBER = 11 | ||
} | ||
interface TimeUnits { | ||
firstDayOfWeek: WeekDay | ||
getMonthWithTrailingAndLeadingDays(year: number, month: Month): MonthWithDates | ||
getWeekFor(date: Date): WeekWithDates | ||
getMonthsFor(year: number): Date[] | ||
firstDayOfWeek: WeekDay; | ||
getMonthWithTrailingAndLeadingDays(year: number, month: Month): MonthWithDates; | ||
getWeekFor(date: Date): WeekWithDates; | ||
getMonthsFor(year: number): Date[]; | ||
} | ||
declare enum DatePickerView { | ||
MONTH_DAYS = 'month-days', | ||
YEARS = 'years', | ||
MONTH_DAYS = "month-days", | ||
YEARS = "years" | ||
} | ||
interface DatePickerState { | ||
isOpen: Signal<boolean> | ||
selectedDate: Signal<string> | ||
inputDisplayedValue: Signal<string> | ||
datePickerDate: Signal<string> | ||
datePickerView: Signal<DatePickerView> | ||
inputWrapperElement: Signal<HTMLDivElement | undefined> | ||
open(): void | ||
close(): void | ||
toggle(): void | ||
setView(view: DatePickerView): void | ||
isOpen: Signal<boolean>; | ||
selectedDate: Signal<string>; | ||
inputDisplayedValue: Signal<string>; | ||
datePickerDate: Signal<string>; | ||
datePickerView: Signal<DatePickerView>; | ||
inputWrapperElement: Signal<HTMLDivElement | undefined>; | ||
open(): void; | ||
close(): void; | ||
toggle(): void; | ||
setView(view: DatePickerView): void; | ||
} | ||
type TranslateFn = (key: string) => string | ||
type TranslateFn = (key: string) => string; | ||
/** | ||
@@ -55,5 +55,5 @@ * This interface serves as a bridge between the AppSingleton for the date picker and calendar | ||
interface AppSingleton { | ||
timeUnitsImpl: TimeUnits | ||
datePickerState: DatePickerState | ||
translate: TranslateFn | ||
timeUnitsImpl: TimeUnits; | ||
datePickerState: DatePickerState; | ||
translate: TranslateFn; | ||
} | ||
@@ -64,321 +64,298 @@ /** | ||
interface Config { | ||
locale: string | ||
firstDayOfWeek: WeekDay | ||
locale: string; | ||
firstDayOfWeek: WeekDay; | ||
} | ||
declare enum Placement { | ||
TOP_START = 'top-start', | ||
TOP_END = 'top-end', | ||
BOTTOM_START = 'bottom-start', | ||
BOTTOM_END = 'bottom-end', | ||
TOP_START = "top-start", | ||
TOP_END = "top-end", | ||
BOTTOM_START = "bottom-start", | ||
BOTTOM_END = "bottom-end" | ||
} | ||
type DatePickerListeners = { | ||
onChange?: (date: string) => void | ||
} | ||
onChange?: (date: string) => void; | ||
}; | ||
type DatePickerStyle = { | ||
dark?: boolean | ||
fullWidth?: boolean | ||
} | ||
dark?: boolean; | ||
fullWidth?: boolean; | ||
}; | ||
interface DatePickerConfigInternal extends Config { | ||
min: string | ||
max: string | ||
placement: Placement | ||
listeners: DatePickerListeners | ||
style: DatePickerStyle | ||
teleportTo?: HTMLElement | ||
label?: string | ||
min: string; | ||
max: string; | ||
placement: Placement; | ||
listeners: DatePickerListeners; | ||
style: DatePickerStyle; | ||
teleportTo?: HTMLElement; | ||
label?: string; | ||
} | ||
// This enum is used to represent names of all internally built views of the calendar | ||
declare enum InternalViewName { | ||
Day = 'day', | ||
Week = 'week', | ||
MonthGrid = 'month-grid', | ||
MonthAgenda = 'month-agenda', | ||
Day = "day", | ||
Week = "week", | ||
MonthGrid = "month-grid", | ||
MonthAgenda = "month-agenda" | ||
} | ||
// Since implementers can use custom views, we need to have a type that combines the internal views with these custom views | ||
type ViewName = InternalViewName | string | ||
type ViewName = InternalViewName | string; | ||
type DateRange = { | ||
start: string | ||
end: string | ||
} | ||
start: string; | ||
end: string; | ||
}; | ||
interface RangeSetterConfig { | ||
date: string | ||
timeUnitsImpl: TimeUnits | ||
calendarConfig: CalendarConfigInternal | ||
range: Signal<DateRange | null> | ||
date: string; | ||
timeUnitsImpl: TimeUnits; | ||
calendarConfig: CalendarConfigInternal; | ||
range: Signal<DateRange | null>; | ||
} | ||
type PreactViewComponent = (props: { | ||
$app: CalendarAppSingleton | ||
id: string | ||
}) => JSXInternal.Element | ||
declare const addMonths: (to: string, nMonths: number) => string | ||
declare const addDays: (to: string, nDays: number) => string | ||
$app: CalendarAppSingleton; | ||
id: string; | ||
}) => JSXInternal.Element; | ||
declare const addMonths: (to: string, nMonths: number) => string; | ||
declare const addDays: (to: string, nDays: number) => string; | ||
type ViewConfig<FrameworkComponent = PreactViewComponent> = { | ||
/** | ||
* a unique identifier for the view | ||
* */ | ||
name: ViewName | ||
/** | ||
* text that will be displayed in the view dropdown | ||
* */ | ||
label: string | ||
/** | ||
* function that is called when a new date is selected | ||
* */ | ||
setDateRange: (config: RangeSetterConfig) => DateRange | ||
/** | ||
* should the view be displayed on small screens (< 700px calendar width) | ||
* */ | ||
hasSmallScreenCompat: boolean | ||
/** | ||
* should the view be displayed on wide screens (> 700px calendar width) | ||
* */ | ||
hasWideScreenCompat: boolean | ||
/** | ||
* The component you want to render | ||
* */ | ||
Component: FrameworkComponent | ||
/** | ||
* function that is called when the user clicks the backward/forward button | ||
* */ | ||
backwardForwardFn: typeof addDays | typeof addMonths | ||
/** | ||
* number of units to add into the backwardForwardFn function. Result behind the scenes for example: | ||
* backwardForwardFn = addDays | ||
* backwardForwardUnits = 1 | ||
* result (behind the scenes) = addDays(date, 1) | ||
* */ | ||
backwardForwardUnits: number | ||
} | ||
type View<FrameworkComponent = PreactViewComponent> = | ||
ViewConfig<FrameworkComponent> & { | ||
render(onElement: HTMLElement, $app: CalendarAppSingleton): void | ||
destroy(): void | ||
} | ||
type EventId = number | string | ||
type startDate = string | ||
type nDays = number | ||
type EventFragments = Record<startDate, nDays> | ||
/** | ||
* a unique identifier for the view | ||
* */ | ||
name: ViewName; | ||
/** | ||
* text that will be displayed in the view dropdown | ||
* */ | ||
label: string; | ||
/** | ||
* function that is called when a new date is selected | ||
* */ | ||
setDateRange: (config: RangeSetterConfig) => DateRange; | ||
/** | ||
* should the view be displayed on small screens (< 700px calendar width) | ||
* */ | ||
hasSmallScreenCompat: boolean; | ||
/** | ||
* should the view be displayed on wide screens (> 700px calendar width) | ||
* */ | ||
hasWideScreenCompat: boolean; | ||
/** | ||
* The component you want to render | ||
* */ | ||
Component: FrameworkComponent; | ||
/** | ||
* function that is called when the user clicks the backward/forward button | ||
* */ | ||
backwardForwardFn: typeof addDays | typeof addMonths; | ||
/** | ||
* number of units to add into the backwardForwardFn function. Result behind the scenes for example: | ||
* backwardForwardFn = addDays | ||
* backwardForwardUnits = 1 | ||
* result (behind the scenes) = addDays(date, 1) | ||
* */ | ||
backwardForwardUnits: number; | ||
}; | ||
type View<FrameworkComponent = PreactViewComponent> = ViewConfig<FrameworkComponent> & { | ||
render(onElement: HTMLElement, $app: CalendarAppSingleton): void; | ||
destroy(): void; | ||
}; | ||
type EventId = number | string; | ||
type startDate = string; | ||
type nDays = number; | ||
type EventFragments = Record<startDate, nDays>; | ||
type CalendarEventOptions = { | ||
disableDND?: boolean; | ||
disableResize?: boolean; | ||
additionalClasses?: string[]; | ||
}; | ||
interface CalendarEventExternal { | ||
id: EventId | ||
start: string | ||
end: string | ||
title?: string | ||
people?: string[] | ||
location?: string | ||
description?: string | ||
calendarId?: string | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
[key: string]: any | ||
id: EventId; | ||
start: string; | ||
end: string; | ||
title?: string; | ||
people?: string[]; | ||
location?: string; | ||
description?: string; | ||
calendarId?: string; | ||
_options?: CalendarEventOptions; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
[key: string]: any; | ||
} | ||
interface CalendarEventInternal extends CalendarEventExternal { | ||
// event duration | ||
_isSingleDayTimed: boolean | ||
_isSingleDayFullDay: boolean | ||
_isSingleHybridDayTimed: boolean | ||
_isMultiDayTimed: boolean | ||
_isMultiDayFullDay: boolean | ||
// week time grid | ||
_previousConcurrentEvents: number | undefined | ||
_totalConcurrentEvents: number | undefined | ||
// week date grid | ||
_nDaysInGrid: number | undefined | ||
// month grid | ||
_eventFragments: EventFragments | ||
_color: string | ||
_getForeignProperties(): Record<string, unknown> | ||
_getExternalEvent(): CalendarEventExternal | ||
// event duration | ||
_isSingleDayTimed: boolean; | ||
_isSingleDayFullDay: boolean; | ||
_isSingleHybridDayTimed: boolean; | ||
_isMultiDayTimed: boolean; | ||
_isMultiDayFullDay: boolean; | ||
// week time grid | ||
_previousConcurrentEvents: number | undefined; | ||
_totalConcurrentEvents: number | undefined; | ||
// week date grid | ||
_nDaysInGrid: number | undefined; | ||
// month grid | ||
_eventFragments: EventFragments; | ||
_color: string; | ||
_getForeignProperties(): Record<string, unknown>; | ||
_getExternalEvent(): CalendarEventExternal; | ||
} | ||
type DayBoundariesInternal = { | ||
start: number | ||
end: number | ||
start: number; | ||
end: number; | ||
}; | ||
interface TimeGridDragHandler { | ||
} | ||
interface TimeGridDragHandler {} | ||
type DayBoundariesDateTime = { | ||
start: string | ||
end: string | ||
start: string; | ||
end: string; | ||
}; | ||
interface DateGridDragHandler { | ||
} | ||
interface DateGridDragHandler {} | ||
interface EventCoordinates { | ||
clientX: number | ||
clientY: number | ||
clientX: number; | ||
clientY: number; | ||
} | ||
interface DragHandlerDependencies { | ||
$app: CalendarAppSingleton | ||
eventCoordinates: EventCoordinates | ||
eventCopy: CalendarEventInternal | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void | ||
$app: CalendarAppSingleton; | ||
eventCoordinates: EventCoordinates; | ||
eventCopy: CalendarEventInternal; | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void; | ||
} | ||
interface MonthGridDragHandler {} | ||
interface MonthGridDragHandler { | ||
} | ||
interface DragAndDropPlugin extends PluginBase { | ||
createTimeGridDragHandler( | ||
dependencies: DragHandlerDependencies, | ||
dayBoundariesDateTime: DayBoundariesDateTime | ||
): TimeGridDragHandler | ||
createDateGridDragHandler( | ||
dependencies: DragHandlerDependencies | ||
): DateGridDragHandler | ||
createMonthGridDragHandler( | ||
calendarEvent: CalendarEventInternal, | ||
$app: CalendarAppSingleton | ||
): MonthGridDragHandler | ||
createTimeGridDragHandler(dependencies: DragHandlerDependencies, dayBoundariesDateTime: DayBoundariesDateTime): TimeGridDragHandler; | ||
createDateGridDragHandler(dependencies: DragHandlerDependencies): DateGridDragHandler; | ||
createMonthGridDragHandler(calendarEvent: CalendarEventInternal, $app: CalendarAppSingleton): MonthGridDragHandler; | ||
} | ||
type EventModalProps = { | ||
$app: CalendarAppSingleton | ||
} | ||
$app: CalendarAppSingleton; | ||
}; | ||
interface EventModalPlugin extends PluginBase { | ||
calendarEvent: Signal<CalendarEventInternal | null> | ||
calendarEventDOMRect: Signal<DOMRect | null> | ||
calendarEventElement: Signal<HTMLElement | null> | ||
setCalendarEvent( | ||
event: CalendarEventInternal | null, | ||
eventTargetDOMRect: DOMRect | null | ||
): void | ||
ComponentFn(props: EventModalProps): JSXInternal.Element | ||
calendarEvent: Signal<CalendarEventInternal | null>; | ||
calendarEventDOMRect: Signal<DOMRect | null>; | ||
calendarEventElement: Signal<HTMLElement | null>; | ||
setCalendarEvent(event: CalendarEventInternal | null, eventTargetDOMRect: DOMRect | null): void; | ||
ComponentFn(props: EventModalProps): JSXInternal.Element; | ||
} | ||
interface CalendarCallbacks { | ||
onEventUpdate?: (event: CalendarEventExternal) => void | ||
onEventClick?: (event: CalendarEventExternal) => void | ||
onRangeUpdate?: (range: DateRange) => void | ||
onSelectedDateUpdate?: (date: string) => void | ||
onClickDate?: (date: string) => void | ||
onClickDateTime?: (dateTime: string) => void | ||
onClickPlusEvents?: (date: string) => void | ||
onEventUpdate?: (event: CalendarEventExternal) => void; | ||
onEventClick?: (event: CalendarEventExternal) => void; | ||
onRangeUpdate?: (range: DateRange) => void; | ||
onSelectedDateUpdate?: (date: string) => void; | ||
onClickDate?: (date: string) => void; | ||
onClickDateTime?: (dateTime: string) => void; | ||
onClickPlusEvents?: (date: string) => void; | ||
} | ||
type CustomComponentFns = { | ||
timeGridEvent?: CustomComponentFn | ||
dateGridEvent?: CustomComponentFn | ||
monthGridEvent?: CustomComponentFn | ||
monthAgendaEvent?: CustomComponentFn | ||
eventModal?: CustomComponentFn | ||
} | ||
timeGridEvent?: CustomComponentFn; | ||
dateGridEvent?: CustomComponentFn; | ||
monthGridEvent?: CustomComponentFn; | ||
monthAgendaEvent?: CustomComponentFn; | ||
eventModal?: CustomComponentFn; | ||
}; | ||
interface EventsFacade { | ||
get(id: EventId): CalendarEventExternal | undefined | ||
getAll(): CalendarEventExternal[] | ||
add(event: CalendarEventExternal): void | ||
update(event: CalendarEventExternal): void | ||
remove(id: EventId): void | ||
set(events: CalendarEventExternal[]): void | ||
get(id: EventId): CalendarEventExternal | undefined; | ||
getAll(): CalendarEventExternal[]; | ||
add(event: CalendarEventExternal): void; | ||
update(event: CalendarEventExternal): void; | ||
remove(id: EventId): void; | ||
set(events: CalendarEventExternal[]): void; | ||
} | ||
interface EventRecurrencePlugin extends PluginBase { | ||
updateRecurrenceDND( | ||
eventId: EventId, | ||
oldEventStart: string, | ||
newEventStart: string | ||
): void | ||
updateRecurrenceOnResize( | ||
eventId: EventId, | ||
oldEventEnd: string, | ||
newEventEnd: string | ||
): void | ||
eventsFacade: EventsFacade | ||
updateRecurrenceDND(eventId: EventId, oldEventStart: string, newEventStart: string): void; | ||
updateRecurrenceOnResize(eventId: EventId, oldEventEnd: string, newEventEnd: string): void; | ||
eventsFacade: EventsFacade; | ||
} | ||
interface ResizePlugin extends PluginBase { | ||
createTimeGridEventResizer( | ||
calendarEvent: CalendarEventInternal, | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void, | ||
mouseDownEvent: MouseEvent, | ||
dayBoundariesDateTime: { | ||
start: string | ||
end: string | ||
} | ||
): void | ||
createDateGridEventResizer( | ||
calendarEvent: CalendarEventInternal, | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void, | ||
mouseDownEvent: MouseEvent | ||
): void | ||
createTimeGridEventResizer(calendarEvent: CalendarEventInternal, updateCopy: (newCopy: CalendarEventInternal | undefined) => void, mouseDownEvent: MouseEvent, dayBoundariesDateTime: { | ||
start: string; | ||
end: string; | ||
}): void; | ||
createDateGridEventResizer(calendarEvent: CalendarEventInternal, updateCopy: (newCopy: CalendarEventInternal | undefined) => void, mouseDownEvent: MouseEvent): void; | ||
} | ||
type WeekOptions = { | ||
gridHeight: number | ||
} | ||
gridHeight: number; | ||
}; | ||
type MonthGridOptions = { | ||
nEventsPerDay: number | ||
} | ||
nEventsPerDay: number; | ||
}; | ||
type ColorDefinition = { | ||
main: string | ||
container: string | ||
onContainer: string | ||
} | ||
main: string; | ||
container: string; | ||
onContainer: string; | ||
}; | ||
type CalendarType = { | ||
colorName: string | ||
label?: string | ||
lightColors?: ColorDefinition | ||
darkColors?: ColorDefinition | ||
} | ||
colorName: string; | ||
label?: string; | ||
lightColors?: ColorDefinition; | ||
darkColors?: ColorDefinition; | ||
}; | ||
type Plugins = { | ||
dragAndDrop?: DragAndDropPlugin | ||
eventModal?: EventModalPlugin | ||
scrollController?: PluginBase | ||
eventRecurrence?: EventRecurrencePlugin | ||
resize?: ResizePlugin | ||
[key: string]: PluginBase | undefined | ||
} | ||
type CustomComponentFn = ( | ||
wrapperElement: HTMLElement, | ||
props: Record<string, unknown> | ||
) => void | ||
dragAndDrop?: DragAndDropPlugin; | ||
eventModal?: EventModalPlugin; | ||
scrollController?: PluginBase; | ||
eventRecurrence?: EventRecurrencePlugin; | ||
resize?: ResizePlugin; | ||
[key: string]: PluginBase | undefined; | ||
}; | ||
type CustomComponentFn = (wrapperElement: HTMLElement, props: Record<string, unknown>) => void; | ||
interface CalendarConfigInternal extends Config { | ||
defaultView: ViewName | ||
views: View[] | ||
dayBoundaries: DayBoundariesInternal | ||
weekOptions: WeekOptions | ||
monthGridOptions: MonthGridOptions | ||
calendars: Signal<Record<string, CalendarType>> | ||
plugins: Plugins | ||
isDark: boolean | ||
callbacks: CalendarCallbacks | ||
_customComponentFns: CustomComponentFns | ||
minDate?: string | ||
maxDate?: string | ||
// Getters | ||
isHybridDay: boolean | ||
timePointsPerDay: number | ||
defaultView: ViewName; | ||
views: View[]; | ||
dayBoundaries: DayBoundariesInternal; | ||
weekOptions: WeekOptions; | ||
monthGridOptions: MonthGridOptions; | ||
calendars: Signal<Record<string, CalendarType>>; | ||
plugins: Plugins; | ||
isDark: boolean; | ||
callbacks: CalendarCallbacks; | ||
_customComponentFns: CustomComponentFns; | ||
minDate?: string; | ||
maxDate?: string; | ||
// Getters | ||
isHybridDay: boolean; | ||
timePointsPerDay: number; | ||
} | ||
interface CalendarState { | ||
isCalendarSmall: Signal<boolean | undefined> | ||
view: Signal<ViewName> | ||
range: Signal<DateRange | null> | ||
isDark: Signal<boolean> | ||
setRange: (date: string) => void | ||
isCalendarSmall: Signal<boolean | undefined>; | ||
view: Signal<ViewName>; | ||
range: Signal<DateRange | null>; | ||
isDark: Signal<boolean>; | ||
setRange: (date: string) => void; | ||
} | ||
type EventsFilterPredicate = | ||
| ((event: CalendarEventInternal) => boolean) | ||
| undefined | ||
type EventsFilterPredicate = ((event: CalendarEventInternal) => boolean) | undefined; | ||
interface CalendarEvents { | ||
list: Signal<CalendarEventInternal[]> | ||
filterPredicate: Signal<EventsFilterPredicate> | ||
list: Signal<CalendarEventInternal[]>; | ||
filterPredicate: Signal<EventsFilterPredicate>; | ||
} | ||
interface CalendarElements { | ||
calendarWrapper: HTMLDivElement | undefined | ||
calendarWrapper: HTMLDivElement | undefined; | ||
} | ||
interface CalendarAppSingleton extends AppSingleton { | ||
config: CalendarConfigInternal | ||
datePickerConfig: DatePickerConfigInternal | ||
calendarState: CalendarState | ||
calendarEvents: CalendarEvents | ||
elements: CalendarElements | ||
config: CalendarConfigInternal; | ||
datePickerConfig: DatePickerConfigInternal; | ||
calendarState: CalendarState; | ||
calendarEvents: CalendarEvents; | ||
elements: CalendarElements; | ||
} | ||
interface PluginBase { | ||
name: string | ||
init?($app: CalendarAppSingleton): void | ||
destroy?(): void | ||
name: string; | ||
// TODO v2: change to `beforeRender` | ||
beforeInit?($app: CalendarAppSingleton): void; | ||
// TODO v2: change to `onRender` and remove $app parameter | ||
init?($app: CalendarAppSingleton): void; | ||
destroy?(): void; | ||
} | ||
interface CurrentTimePlugin extends PluginBase {} | ||
interface CurrentTimePlugin extends PluginBase { | ||
} | ||
type CurrentTimePluginConfig = { | ||
fullWeekWidth?: boolean | ||
} | ||
fullWeekWidth?: boolean; | ||
}; | ||
declare class CurrentTimePluginImpl implements CurrentTimePlugin { | ||
private config | ||
name: string | ||
$app: CalendarAppSingleton | ||
observer: MutationObserver | null | ||
constructor(config?: CurrentTimePluginConfig) | ||
init($app: CalendarAppSingleton): void | ||
private setIndicator | ||
private createFullWidthIndicator | ||
destroy(): void | ||
private config; | ||
name: string; | ||
$app: CalendarAppSingleton; | ||
observer: MutationObserver | null; | ||
constructor(config?: CurrentTimePluginConfig); | ||
init($app: CalendarAppSingleton): void; | ||
private setIndicator; | ||
private createFullWidthIndicator; | ||
destroy(): void; | ||
} | ||
declare const createCurrentTimePlugin: ( | ||
config?: CurrentTimePluginConfig | ||
) => CurrentTimePluginImpl | ||
export { createCurrentTimePlugin } | ||
declare const createCurrentTimePlugin: (config?: CurrentTimePluginConfig) => CurrentTimePluginImpl; | ||
export { createCurrentTimePlugin }; |
@@ -1,181 +0,162 @@ | ||
'use strict' | ||
'use strict'; | ||
class NumberRangeError extends Error { | ||
constructor(min, max) { | ||
super(`Number must be between ${min} and ${max}.`) | ||
Object.defineProperty(this, 'min', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: min, | ||
}) | ||
Object.defineProperty(this, 'max', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: max, | ||
}) | ||
} | ||
constructor(min, max) { | ||
super(`Number must be between ${min} and ${max}.`); | ||
Object.defineProperty(this, "min", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: min | ||
}); | ||
Object.defineProperty(this, "max", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: max | ||
}); | ||
} | ||
} | ||
const doubleDigit = (number) => { | ||
if (number < 0 || number > 99) throw new NumberRangeError(0, 99) | ||
return String(number).padStart(2, '0') | ||
} | ||
if (number < 0 || number > 99) | ||
throw new NumberRangeError(0, 99); | ||
return String(number).padStart(2, '0'); | ||
}; | ||
const toDateString = (date) => { | ||
return `${date.getFullYear()}-${doubleDigit(date.getMonth() + 1)}-${doubleDigit(date.getDate())}` | ||
} | ||
return `${date.getFullYear()}-${doubleDigit(date.getMonth() + 1)}-${doubleDigit(date.getDate())}`; | ||
}; | ||
const toTimeString = (date) => { | ||
return `${doubleDigit(date.getHours())}:${doubleDigit(date.getMinutes())}` | ||
} | ||
return `${doubleDigit(date.getHours())}:${doubleDigit(date.getMinutes())}`; | ||
}; | ||
const toDateTimeString = (date) => { | ||
return `${toDateString(date)} ${toTimeString(date)}` | ||
} | ||
return `${toDateString(date)} ${toTimeString(date)}`; | ||
}; | ||
const timePointToPercentage = (timePointsInDay, dayBoundaries, timePoint) => { | ||
if (timePoint < dayBoundaries.start) { | ||
const firstDayTimePoints = 2400 - dayBoundaries.start | ||
return ((timePoint + firstDayTimePoints) / timePointsInDay) * 100 | ||
} | ||
return ((timePoint - dayBoundaries.start) / timePointsInDay) * 100 | ||
} | ||
if (timePoint < dayBoundaries.start) { | ||
const firstDayTimePoints = 2400 - dayBoundaries.start; | ||
return ((timePoint + firstDayTimePoints) / timePointsInDay) * 100; | ||
} | ||
return ((timePoint - dayBoundaries.start) / timePointsInDay) * 100; | ||
}; | ||
class InvalidTimeStringError extends Error { | ||
constructor(timeString) { | ||
super(`Invalid time string: ${timeString}`) | ||
} | ||
constructor(timeString) { | ||
super(`Invalid time string: ${timeString}`); | ||
} | ||
} | ||
// regex for strings between 00:00 and 23:59 | ||
const timeStringRegex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/ | ||
const timeStringRegex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/; | ||
const minuteTimePointMultiplier = 1.6666666666666667 // 100 / 60 | ||
const minuteTimePointMultiplier = 1.6666666666666667; // 100 / 60 | ||
const timePointsFromString = (timeString) => { | ||
if (!timeStringRegex.test(timeString)) | ||
throw new InvalidTimeStringError(timeString) | ||
const [hoursInt, minutesInt] = timeString | ||
.split(':') | ||
.map((time) => parseInt(time, 10)) | ||
let minutePoints = (minutesInt * minuteTimePointMultiplier).toString() | ||
if (minutePoints.split('.')[0].length < 2) minutePoints = `0${minutePoints}` | ||
return Number(hoursInt + minutePoints) | ||
} | ||
if (!timeStringRegex.test(timeString)) | ||
throw new InvalidTimeStringError(timeString); | ||
const [hoursInt, minutesInt] = timeString | ||
.split(':') | ||
.map((time) => parseInt(time, 10)); | ||
let minutePoints = (minutesInt * minuteTimePointMultiplier).toString(); | ||
if (minutePoints.split('.')[0].length < 2) | ||
minutePoints = `0${minutePoints}`; | ||
return Number(hoursInt + minutePoints); | ||
}; | ||
const timeFromDateTime = (dateTime) => { | ||
return dateTime.slice(11) | ||
} | ||
return dateTime.slice(11); | ||
}; | ||
const getYCoordinateInTimeGrid = ( | ||
dateTimeString, | ||
dayBoundaries, | ||
pointsPerDay | ||
) => { | ||
return timePointToPercentage( | ||
pointsPerDay, | ||
dayBoundaries, | ||
timePointsFromString(timeFromDateTime(dateTimeString)) | ||
) | ||
} | ||
const getYCoordinateInTimeGrid = (dateTimeString, dayBoundaries, pointsPerDay) => { | ||
return timePointToPercentage(pointsPerDay, dayBoundaries, timePointsFromString(timeFromDateTime(dateTimeString))); | ||
}; | ||
class CurrentTimePluginImpl { | ||
constructor(config = {}) { | ||
Object.defineProperty(this, 'config', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: config, | ||
}) | ||
Object.defineProperty(this, 'name', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 'current-time-plugin', | ||
}) | ||
Object.defineProperty(this, '$app', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0, | ||
}) | ||
Object.defineProperty(this, 'observer', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: null, | ||
}) | ||
} | ||
init($app) { | ||
this.$app = $app | ||
this.observer = new MutationObserver((mutationList) => { | ||
for (const mutation of mutationList) { | ||
if (mutation.type === 'childList') { | ||
this.setIndicator() | ||
constructor(config = {}) { | ||
Object.defineProperty(this, "config", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: config | ||
}); | ||
Object.defineProperty(this, "name", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 'current-time-plugin' | ||
}); | ||
Object.defineProperty(this, "$app", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "observer", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: null | ||
}); | ||
} | ||
init($app) { | ||
this.$app = $app; | ||
this.observer = new MutationObserver((mutationList) => { | ||
for (const mutation of mutationList) { | ||
if (mutation.type === 'childList') { | ||
this.setIndicator(); | ||
} | ||
} | ||
}); | ||
const calendarWrapper = $app.elements.calendarWrapper; | ||
if (!calendarWrapper) { | ||
throw new Error('Calendar wrapper not found'); | ||
} | ||
} | ||
}) | ||
const calendarWrapper = $app.elements.calendarWrapper | ||
if (!calendarWrapper) { | ||
throw new Error('Calendar wrapper not found') | ||
this.observer.observe(calendarWrapper, { | ||
childList: true, | ||
subtree: true, | ||
}); | ||
} | ||
this.observer.observe(calendarWrapper, { | ||
childList: true, | ||
subtree: true, | ||
}) | ||
} | ||
setIndicator(isRecursion = false) { | ||
const todayDateString = toDateString(new Date()) | ||
const nowDateTimeString = toDateTimeString(new Date()) | ||
const todayElement = this.$app.elements.calendarWrapper.querySelector( | ||
`[data-time-grid-date="${todayDateString}"]` | ||
) | ||
if (!todayElement) return | ||
const existingIndicator = todayElement.querySelector( | ||
'.sx__current-time-indicator' | ||
) | ||
if (existingIndicator && isRecursion) existingIndicator.remove() | ||
if (todayElement && !existingIndicator) { | ||
const currentTimeIndicator = document.createElement('div') | ||
currentTimeIndicator.classList.add('sx__current-time-indicator') | ||
const top = | ||
getYCoordinateInTimeGrid( | ||
nowDateTimeString, | ||
this.$app.config.dayBoundaries, | ||
this.$app.config.timePointsPerDay | ||
) + '%' | ||
currentTimeIndicator.style.top = top | ||
todayElement.appendChild(currentTimeIndicator) | ||
if (this.config.fullWeekWidth) { | ||
this.createFullWidthIndicator(top) | ||
} | ||
setTimeout( | ||
this.setIndicator.bind(this, true), | ||
60000 - (Date.now() % 60000) | ||
) | ||
setIndicator(isRecursion = false) { | ||
const todayDateString = toDateString(new Date()); | ||
const nowDateTimeString = toDateTimeString(new Date()); | ||
const todayElement = this.$app.elements.calendarWrapper.querySelector(`[data-time-grid-date="${todayDateString}"]`); | ||
if (!todayElement) | ||
return; | ||
const existingIndicator = todayElement.querySelector('.sx__current-time-indicator'); | ||
if (existingIndicator && isRecursion) | ||
existingIndicator.remove(); | ||
if (todayElement && !existingIndicator) { | ||
const currentTimeIndicator = document.createElement('div'); | ||
currentTimeIndicator.classList.add('sx__current-time-indicator'); | ||
const top = getYCoordinateInTimeGrid(nowDateTimeString, this.$app.config.dayBoundaries, this.$app.config.timePointsPerDay) + '%'; | ||
currentTimeIndicator.style.top = top; | ||
todayElement.appendChild(currentTimeIndicator); | ||
if (this.config.fullWeekWidth) { | ||
this.createFullWidthIndicator(top); | ||
} | ||
setTimeout(this.setIndicator.bind(this, true), 60000 - (Date.now() % 60000)); | ||
} | ||
} | ||
} | ||
createFullWidthIndicator(top) { | ||
const fullWeekTimeIndicator = document.createElement('div') | ||
fullWeekTimeIndicator.classList.add('sx__current-time-indicator-full-week') | ||
fullWeekTimeIndicator.style.top = top | ||
const weekGridWrapper = document.querySelector('.sx__week-grid') | ||
const existingFullWeekIndicator = | ||
weekGridWrapper === null || weekGridWrapper === void 0 | ||
? void 0 | ||
: weekGridWrapper.querySelector('.sx__current-time-indicator-full-week') | ||
if (existingFullWeekIndicator) { | ||
existingFullWeekIndicator.remove() | ||
createFullWidthIndicator(top) { | ||
const fullWeekTimeIndicator = document.createElement('div'); | ||
fullWeekTimeIndicator.classList.add('sx__current-time-indicator-full-week'); | ||
fullWeekTimeIndicator.style.top = top; | ||
const weekGridWrapper = document.querySelector('.sx__week-grid'); | ||
const existingFullWeekIndicator = weekGridWrapper === null || weekGridWrapper === void 0 ? void 0 : weekGridWrapper.querySelector('.sx__current-time-indicator-full-week'); | ||
if (existingFullWeekIndicator) { | ||
existingFullWeekIndicator.remove(); | ||
} | ||
if (weekGridWrapper) { | ||
weekGridWrapper.appendChild(fullWeekTimeIndicator); | ||
} | ||
} | ||
if (weekGridWrapper) { | ||
weekGridWrapper.appendChild(fullWeekTimeIndicator) | ||
destroy() { | ||
if (this.observer) { | ||
this.observer.disconnect(); | ||
} | ||
} | ||
} | ||
destroy() { | ||
if (this.observer) { | ||
this.observer.disconnect() | ||
} | ||
} | ||
} | ||
const createCurrentTimePlugin = (config) => new CurrentTimePluginImpl(config) | ||
const createCurrentTimePlugin = (config) => new CurrentTimePluginImpl(config); | ||
exports.createCurrentTimePlugin = createCurrentTimePlugin | ||
exports.createCurrentTimePlugin = createCurrentTimePlugin; |
@@ -1,51 +0,51 @@ | ||
import { Signal } from '@preact/signals' | ||
import { JSXInternal } from 'preact/src/jsx' | ||
import { Signal } from "@preact/signals"; | ||
import { JSXInternal } from "preact/src/jsx"; | ||
declare enum WeekDay { | ||
SUNDAY = 0, | ||
MONDAY = 1, | ||
TUESDAY = 2, | ||
WEDNESDAY = 3, | ||
THURSDAY = 4, | ||
FRIDAY = 5, | ||
SATURDAY = 6, | ||
SUNDAY = 0, | ||
MONDAY = 1, | ||
TUESDAY = 2, | ||
WEDNESDAY = 3, | ||
THURSDAY = 4, | ||
FRIDAY = 5, | ||
SATURDAY = 6 | ||
} | ||
type WeekWithDates = Date[] | ||
type MonthWithDates = Date[][] | ||
type WeekWithDates = Date[]; | ||
type MonthWithDates = Date[][]; | ||
declare enum Month { | ||
JANUARY = 0, | ||
FEBRUARY = 1, | ||
MARCH = 2, | ||
APRIL = 3, | ||
MAY = 4, | ||
JUNE = 5, | ||
JULY = 6, | ||
AUGUST = 7, | ||
SEPTEMBER = 8, | ||
OCTOBER = 9, | ||
NOVEMBER = 10, | ||
DECEMBER = 11, | ||
JANUARY = 0, | ||
FEBRUARY = 1, | ||
MARCH = 2, | ||
APRIL = 3, | ||
MAY = 4, | ||
JUNE = 5, | ||
JULY = 6, | ||
AUGUST = 7, | ||
SEPTEMBER = 8, | ||
OCTOBER = 9, | ||
NOVEMBER = 10, | ||
DECEMBER = 11 | ||
} | ||
interface TimeUnits { | ||
firstDayOfWeek: WeekDay | ||
getMonthWithTrailingAndLeadingDays(year: number, month: Month): MonthWithDates | ||
getWeekFor(date: Date): WeekWithDates | ||
getMonthsFor(year: number): Date[] | ||
firstDayOfWeek: WeekDay; | ||
getMonthWithTrailingAndLeadingDays(year: number, month: Month): MonthWithDates; | ||
getWeekFor(date: Date): WeekWithDates; | ||
getMonthsFor(year: number): Date[]; | ||
} | ||
declare enum DatePickerView { | ||
MONTH_DAYS = 'month-days', | ||
YEARS = 'years', | ||
MONTH_DAYS = "month-days", | ||
YEARS = "years" | ||
} | ||
interface DatePickerState { | ||
isOpen: Signal<boolean> | ||
selectedDate: Signal<string> | ||
inputDisplayedValue: Signal<string> | ||
datePickerDate: Signal<string> | ||
datePickerView: Signal<DatePickerView> | ||
inputWrapperElement: Signal<HTMLDivElement | undefined> | ||
open(): void | ||
close(): void | ||
toggle(): void | ||
setView(view: DatePickerView): void | ||
isOpen: Signal<boolean>; | ||
selectedDate: Signal<string>; | ||
inputDisplayedValue: Signal<string>; | ||
datePickerDate: Signal<string>; | ||
datePickerView: Signal<DatePickerView>; | ||
inputWrapperElement: Signal<HTMLDivElement | undefined>; | ||
open(): void; | ||
close(): void; | ||
toggle(): void; | ||
setView(view: DatePickerView): void; | ||
} | ||
type TranslateFn = (key: string) => string | ||
type TranslateFn = (key: string) => string; | ||
/** | ||
@@ -55,5 +55,5 @@ * This interface serves as a bridge between the AppSingleton for the date picker and calendar | ||
interface AppSingleton { | ||
timeUnitsImpl: TimeUnits | ||
datePickerState: DatePickerState | ||
translate: TranslateFn | ||
timeUnitsImpl: TimeUnits; | ||
datePickerState: DatePickerState; | ||
translate: TranslateFn; | ||
} | ||
@@ -64,321 +64,298 @@ /** | ||
interface Config { | ||
locale: string | ||
firstDayOfWeek: WeekDay | ||
locale: string; | ||
firstDayOfWeek: WeekDay; | ||
} | ||
declare enum Placement { | ||
TOP_START = 'top-start', | ||
TOP_END = 'top-end', | ||
BOTTOM_START = 'bottom-start', | ||
BOTTOM_END = 'bottom-end', | ||
TOP_START = "top-start", | ||
TOP_END = "top-end", | ||
BOTTOM_START = "bottom-start", | ||
BOTTOM_END = "bottom-end" | ||
} | ||
type DatePickerListeners = { | ||
onChange?: (date: string) => void | ||
} | ||
onChange?: (date: string) => void; | ||
}; | ||
type DatePickerStyle = { | ||
dark?: boolean | ||
fullWidth?: boolean | ||
} | ||
dark?: boolean; | ||
fullWidth?: boolean; | ||
}; | ||
interface DatePickerConfigInternal extends Config { | ||
min: string | ||
max: string | ||
placement: Placement | ||
listeners: DatePickerListeners | ||
style: DatePickerStyle | ||
teleportTo?: HTMLElement | ||
label?: string | ||
min: string; | ||
max: string; | ||
placement: Placement; | ||
listeners: DatePickerListeners; | ||
style: DatePickerStyle; | ||
teleportTo?: HTMLElement; | ||
label?: string; | ||
} | ||
// This enum is used to represent names of all internally built views of the calendar | ||
declare enum InternalViewName { | ||
Day = 'day', | ||
Week = 'week', | ||
MonthGrid = 'month-grid', | ||
MonthAgenda = 'month-agenda', | ||
Day = "day", | ||
Week = "week", | ||
MonthGrid = "month-grid", | ||
MonthAgenda = "month-agenda" | ||
} | ||
// Since implementers can use custom views, we need to have a type that combines the internal views with these custom views | ||
type ViewName = InternalViewName | string | ||
type ViewName = InternalViewName | string; | ||
type DateRange = { | ||
start: string | ||
end: string | ||
} | ||
start: string; | ||
end: string; | ||
}; | ||
interface RangeSetterConfig { | ||
date: string | ||
timeUnitsImpl: TimeUnits | ||
calendarConfig: CalendarConfigInternal | ||
range: Signal<DateRange | null> | ||
date: string; | ||
timeUnitsImpl: TimeUnits; | ||
calendarConfig: CalendarConfigInternal; | ||
range: Signal<DateRange | null>; | ||
} | ||
type PreactViewComponent = (props: { | ||
$app: CalendarAppSingleton | ||
id: string | ||
}) => JSXInternal.Element | ||
declare const addMonths: (to: string, nMonths: number) => string | ||
declare const addDays: (to: string, nDays: number) => string | ||
$app: CalendarAppSingleton; | ||
id: string; | ||
}) => JSXInternal.Element; | ||
declare const addMonths: (to: string, nMonths: number) => string; | ||
declare const addDays: (to: string, nDays: number) => string; | ||
type ViewConfig<FrameworkComponent = PreactViewComponent> = { | ||
/** | ||
* a unique identifier for the view | ||
* */ | ||
name: ViewName | ||
/** | ||
* text that will be displayed in the view dropdown | ||
* */ | ||
label: string | ||
/** | ||
* function that is called when a new date is selected | ||
* */ | ||
setDateRange: (config: RangeSetterConfig) => DateRange | ||
/** | ||
* should the view be displayed on small screens (< 700px calendar width) | ||
* */ | ||
hasSmallScreenCompat: boolean | ||
/** | ||
* should the view be displayed on wide screens (> 700px calendar width) | ||
* */ | ||
hasWideScreenCompat: boolean | ||
/** | ||
* The component you want to render | ||
* */ | ||
Component: FrameworkComponent | ||
/** | ||
* function that is called when the user clicks the backward/forward button | ||
* */ | ||
backwardForwardFn: typeof addDays | typeof addMonths | ||
/** | ||
* number of units to add into the backwardForwardFn function. Result behind the scenes for example: | ||
* backwardForwardFn = addDays | ||
* backwardForwardUnits = 1 | ||
* result (behind the scenes) = addDays(date, 1) | ||
* */ | ||
backwardForwardUnits: number | ||
} | ||
type View<FrameworkComponent = PreactViewComponent> = | ||
ViewConfig<FrameworkComponent> & { | ||
render(onElement: HTMLElement, $app: CalendarAppSingleton): void | ||
destroy(): void | ||
} | ||
type EventId = number | string | ||
type startDate = string | ||
type nDays = number | ||
type EventFragments = Record<startDate, nDays> | ||
/** | ||
* a unique identifier for the view | ||
* */ | ||
name: ViewName; | ||
/** | ||
* text that will be displayed in the view dropdown | ||
* */ | ||
label: string; | ||
/** | ||
* function that is called when a new date is selected | ||
* */ | ||
setDateRange: (config: RangeSetterConfig) => DateRange; | ||
/** | ||
* should the view be displayed on small screens (< 700px calendar width) | ||
* */ | ||
hasSmallScreenCompat: boolean; | ||
/** | ||
* should the view be displayed on wide screens (> 700px calendar width) | ||
* */ | ||
hasWideScreenCompat: boolean; | ||
/** | ||
* The component you want to render | ||
* */ | ||
Component: FrameworkComponent; | ||
/** | ||
* function that is called when the user clicks the backward/forward button | ||
* */ | ||
backwardForwardFn: typeof addDays | typeof addMonths; | ||
/** | ||
* number of units to add into the backwardForwardFn function. Result behind the scenes for example: | ||
* backwardForwardFn = addDays | ||
* backwardForwardUnits = 1 | ||
* result (behind the scenes) = addDays(date, 1) | ||
* */ | ||
backwardForwardUnits: number; | ||
}; | ||
type View<FrameworkComponent = PreactViewComponent> = ViewConfig<FrameworkComponent> & { | ||
render(onElement: HTMLElement, $app: CalendarAppSingleton): void; | ||
destroy(): void; | ||
}; | ||
type EventId = number | string; | ||
type startDate = string; | ||
type nDays = number; | ||
type EventFragments = Record<startDate, nDays>; | ||
type CalendarEventOptions = { | ||
disableDND?: boolean; | ||
disableResize?: boolean; | ||
additionalClasses?: string[]; | ||
}; | ||
interface CalendarEventExternal { | ||
id: EventId | ||
start: string | ||
end: string | ||
title?: string | ||
people?: string[] | ||
location?: string | ||
description?: string | ||
calendarId?: string | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
[key: string]: any | ||
id: EventId; | ||
start: string; | ||
end: string; | ||
title?: string; | ||
people?: string[]; | ||
location?: string; | ||
description?: string; | ||
calendarId?: string; | ||
_options?: CalendarEventOptions; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
[key: string]: any; | ||
} | ||
interface CalendarEventInternal extends CalendarEventExternal { | ||
// event duration | ||
_isSingleDayTimed: boolean | ||
_isSingleDayFullDay: boolean | ||
_isSingleHybridDayTimed: boolean | ||
_isMultiDayTimed: boolean | ||
_isMultiDayFullDay: boolean | ||
// week time grid | ||
_previousConcurrentEvents: number | undefined | ||
_totalConcurrentEvents: number | undefined | ||
// week date grid | ||
_nDaysInGrid: number | undefined | ||
// month grid | ||
_eventFragments: EventFragments | ||
_color: string | ||
_getForeignProperties(): Record<string, unknown> | ||
_getExternalEvent(): CalendarEventExternal | ||
// event duration | ||
_isSingleDayTimed: boolean; | ||
_isSingleDayFullDay: boolean; | ||
_isSingleHybridDayTimed: boolean; | ||
_isMultiDayTimed: boolean; | ||
_isMultiDayFullDay: boolean; | ||
// week time grid | ||
_previousConcurrentEvents: number | undefined; | ||
_totalConcurrentEvents: number | undefined; | ||
// week date grid | ||
_nDaysInGrid: number | undefined; | ||
// month grid | ||
_eventFragments: EventFragments; | ||
_color: string; | ||
_getForeignProperties(): Record<string, unknown>; | ||
_getExternalEvent(): CalendarEventExternal; | ||
} | ||
type DayBoundariesInternal = { | ||
start: number | ||
end: number | ||
start: number; | ||
end: number; | ||
}; | ||
interface TimeGridDragHandler { | ||
} | ||
interface TimeGridDragHandler {} | ||
type DayBoundariesDateTime = { | ||
start: string | ||
end: string | ||
start: string; | ||
end: string; | ||
}; | ||
interface DateGridDragHandler { | ||
} | ||
interface DateGridDragHandler {} | ||
interface EventCoordinates { | ||
clientX: number | ||
clientY: number | ||
clientX: number; | ||
clientY: number; | ||
} | ||
interface DragHandlerDependencies { | ||
$app: CalendarAppSingleton | ||
eventCoordinates: EventCoordinates | ||
eventCopy: CalendarEventInternal | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void | ||
$app: CalendarAppSingleton; | ||
eventCoordinates: EventCoordinates; | ||
eventCopy: CalendarEventInternal; | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void; | ||
} | ||
interface MonthGridDragHandler {} | ||
interface MonthGridDragHandler { | ||
} | ||
interface DragAndDropPlugin extends PluginBase { | ||
createTimeGridDragHandler( | ||
dependencies: DragHandlerDependencies, | ||
dayBoundariesDateTime: DayBoundariesDateTime | ||
): TimeGridDragHandler | ||
createDateGridDragHandler( | ||
dependencies: DragHandlerDependencies | ||
): DateGridDragHandler | ||
createMonthGridDragHandler( | ||
calendarEvent: CalendarEventInternal, | ||
$app: CalendarAppSingleton | ||
): MonthGridDragHandler | ||
createTimeGridDragHandler(dependencies: DragHandlerDependencies, dayBoundariesDateTime: DayBoundariesDateTime): TimeGridDragHandler; | ||
createDateGridDragHandler(dependencies: DragHandlerDependencies): DateGridDragHandler; | ||
createMonthGridDragHandler(calendarEvent: CalendarEventInternal, $app: CalendarAppSingleton): MonthGridDragHandler; | ||
} | ||
type EventModalProps = { | ||
$app: CalendarAppSingleton | ||
} | ||
$app: CalendarAppSingleton; | ||
}; | ||
interface EventModalPlugin extends PluginBase { | ||
calendarEvent: Signal<CalendarEventInternal | null> | ||
calendarEventDOMRect: Signal<DOMRect | null> | ||
calendarEventElement: Signal<HTMLElement | null> | ||
setCalendarEvent( | ||
event: CalendarEventInternal | null, | ||
eventTargetDOMRect: DOMRect | null | ||
): void | ||
ComponentFn(props: EventModalProps): JSXInternal.Element | ||
calendarEvent: Signal<CalendarEventInternal | null>; | ||
calendarEventDOMRect: Signal<DOMRect | null>; | ||
calendarEventElement: Signal<HTMLElement | null>; | ||
setCalendarEvent(event: CalendarEventInternal | null, eventTargetDOMRect: DOMRect | null): void; | ||
ComponentFn(props: EventModalProps): JSXInternal.Element; | ||
} | ||
interface CalendarCallbacks { | ||
onEventUpdate?: (event: CalendarEventExternal) => void | ||
onEventClick?: (event: CalendarEventExternal) => void | ||
onRangeUpdate?: (range: DateRange) => void | ||
onSelectedDateUpdate?: (date: string) => void | ||
onClickDate?: (date: string) => void | ||
onClickDateTime?: (dateTime: string) => void | ||
onClickPlusEvents?: (date: string) => void | ||
onEventUpdate?: (event: CalendarEventExternal) => void; | ||
onEventClick?: (event: CalendarEventExternal) => void; | ||
onRangeUpdate?: (range: DateRange) => void; | ||
onSelectedDateUpdate?: (date: string) => void; | ||
onClickDate?: (date: string) => void; | ||
onClickDateTime?: (dateTime: string) => void; | ||
onClickPlusEvents?: (date: string) => void; | ||
} | ||
type CustomComponentFns = { | ||
timeGridEvent?: CustomComponentFn | ||
dateGridEvent?: CustomComponentFn | ||
monthGridEvent?: CustomComponentFn | ||
monthAgendaEvent?: CustomComponentFn | ||
eventModal?: CustomComponentFn | ||
} | ||
timeGridEvent?: CustomComponentFn; | ||
dateGridEvent?: CustomComponentFn; | ||
monthGridEvent?: CustomComponentFn; | ||
monthAgendaEvent?: CustomComponentFn; | ||
eventModal?: CustomComponentFn; | ||
}; | ||
interface EventsFacade { | ||
get(id: EventId): CalendarEventExternal | undefined | ||
getAll(): CalendarEventExternal[] | ||
add(event: CalendarEventExternal): void | ||
update(event: CalendarEventExternal): void | ||
remove(id: EventId): void | ||
set(events: CalendarEventExternal[]): void | ||
get(id: EventId): CalendarEventExternal | undefined; | ||
getAll(): CalendarEventExternal[]; | ||
add(event: CalendarEventExternal): void; | ||
update(event: CalendarEventExternal): void; | ||
remove(id: EventId): void; | ||
set(events: CalendarEventExternal[]): void; | ||
} | ||
interface EventRecurrencePlugin extends PluginBase { | ||
updateRecurrenceDND( | ||
eventId: EventId, | ||
oldEventStart: string, | ||
newEventStart: string | ||
): void | ||
updateRecurrenceOnResize( | ||
eventId: EventId, | ||
oldEventEnd: string, | ||
newEventEnd: string | ||
): void | ||
eventsFacade: EventsFacade | ||
updateRecurrenceDND(eventId: EventId, oldEventStart: string, newEventStart: string): void; | ||
updateRecurrenceOnResize(eventId: EventId, oldEventEnd: string, newEventEnd: string): void; | ||
eventsFacade: EventsFacade; | ||
} | ||
interface ResizePlugin extends PluginBase { | ||
createTimeGridEventResizer( | ||
calendarEvent: CalendarEventInternal, | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void, | ||
mouseDownEvent: MouseEvent, | ||
dayBoundariesDateTime: { | ||
start: string | ||
end: string | ||
} | ||
): void | ||
createDateGridEventResizer( | ||
calendarEvent: CalendarEventInternal, | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void, | ||
mouseDownEvent: MouseEvent | ||
): void | ||
createTimeGridEventResizer(calendarEvent: CalendarEventInternal, updateCopy: (newCopy: CalendarEventInternal | undefined) => void, mouseDownEvent: MouseEvent, dayBoundariesDateTime: { | ||
start: string; | ||
end: string; | ||
}): void; | ||
createDateGridEventResizer(calendarEvent: CalendarEventInternal, updateCopy: (newCopy: CalendarEventInternal | undefined) => void, mouseDownEvent: MouseEvent): void; | ||
} | ||
type WeekOptions = { | ||
gridHeight: number | ||
} | ||
gridHeight: number; | ||
}; | ||
type MonthGridOptions = { | ||
nEventsPerDay: number | ||
} | ||
nEventsPerDay: number; | ||
}; | ||
type ColorDefinition = { | ||
main: string | ||
container: string | ||
onContainer: string | ||
} | ||
main: string; | ||
container: string; | ||
onContainer: string; | ||
}; | ||
type CalendarType = { | ||
colorName: string | ||
label?: string | ||
lightColors?: ColorDefinition | ||
darkColors?: ColorDefinition | ||
} | ||
colorName: string; | ||
label?: string; | ||
lightColors?: ColorDefinition; | ||
darkColors?: ColorDefinition; | ||
}; | ||
type Plugins = { | ||
dragAndDrop?: DragAndDropPlugin | ||
eventModal?: EventModalPlugin | ||
scrollController?: PluginBase | ||
eventRecurrence?: EventRecurrencePlugin | ||
resize?: ResizePlugin | ||
[key: string]: PluginBase | undefined | ||
} | ||
type CustomComponentFn = ( | ||
wrapperElement: HTMLElement, | ||
props: Record<string, unknown> | ||
) => void | ||
dragAndDrop?: DragAndDropPlugin; | ||
eventModal?: EventModalPlugin; | ||
scrollController?: PluginBase; | ||
eventRecurrence?: EventRecurrencePlugin; | ||
resize?: ResizePlugin; | ||
[key: string]: PluginBase | undefined; | ||
}; | ||
type CustomComponentFn = (wrapperElement: HTMLElement, props: Record<string, unknown>) => void; | ||
interface CalendarConfigInternal extends Config { | ||
defaultView: ViewName | ||
views: View[] | ||
dayBoundaries: DayBoundariesInternal | ||
weekOptions: WeekOptions | ||
monthGridOptions: MonthGridOptions | ||
calendars: Signal<Record<string, CalendarType>> | ||
plugins: Plugins | ||
isDark: boolean | ||
callbacks: CalendarCallbacks | ||
_customComponentFns: CustomComponentFns | ||
minDate?: string | ||
maxDate?: string | ||
// Getters | ||
isHybridDay: boolean | ||
timePointsPerDay: number | ||
defaultView: ViewName; | ||
views: View[]; | ||
dayBoundaries: DayBoundariesInternal; | ||
weekOptions: WeekOptions; | ||
monthGridOptions: MonthGridOptions; | ||
calendars: Signal<Record<string, CalendarType>>; | ||
plugins: Plugins; | ||
isDark: boolean; | ||
callbacks: CalendarCallbacks; | ||
_customComponentFns: CustomComponentFns; | ||
minDate?: string; | ||
maxDate?: string; | ||
// Getters | ||
isHybridDay: boolean; | ||
timePointsPerDay: number; | ||
} | ||
interface CalendarState { | ||
isCalendarSmall: Signal<boolean | undefined> | ||
view: Signal<ViewName> | ||
range: Signal<DateRange | null> | ||
isDark: Signal<boolean> | ||
setRange: (date: string) => void | ||
isCalendarSmall: Signal<boolean | undefined>; | ||
view: Signal<ViewName>; | ||
range: Signal<DateRange | null>; | ||
isDark: Signal<boolean>; | ||
setRange: (date: string) => void; | ||
} | ||
type EventsFilterPredicate = | ||
| ((event: CalendarEventInternal) => boolean) | ||
| undefined | ||
type EventsFilterPredicate = ((event: CalendarEventInternal) => boolean) | undefined; | ||
interface CalendarEvents { | ||
list: Signal<CalendarEventInternal[]> | ||
filterPredicate: Signal<EventsFilterPredicate> | ||
list: Signal<CalendarEventInternal[]>; | ||
filterPredicate: Signal<EventsFilterPredicate>; | ||
} | ||
interface CalendarElements { | ||
calendarWrapper: HTMLDivElement | undefined | ||
calendarWrapper: HTMLDivElement | undefined; | ||
} | ||
interface CalendarAppSingleton extends AppSingleton { | ||
config: CalendarConfigInternal | ||
datePickerConfig: DatePickerConfigInternal | ||
calendarState: CalendarState | ||
calendarEvents: CalendarEvents | ||
elements: CalendarElements | ||
config: CalendarConfigInternal; | ||
datePickerConfig: DatePickerConfigInternal; | ||
calendarState: CalendarState; | ||
calendarEvents: CalendarEvents; | ||
elements: CalendarElements; | ||
} | ||
interface PluginBase { | ||
name: string | ||
init?($app: CalendarAppSingleton): void | ||
destroy?(): void | ||
name: string; | ||
// TODO v2: change to `beforeRender` | ||
beforeInit?($app: CalendarAppSingleton): void; | ||
// TODO v2: change to `onRender` and remove $app parameter | ||
init?($app: CalendarAppSingleton): void; | ||
destroy?(): void; | ||
} | ||
interface CurrentTimePlugin extends PluginBase {} | ||
interface CurrentTimePlugin extends PluginBase { | ||
} | ||
type CurrentTimePluginConfig = { | ||
fullWeekWidth?: boolean | ||
} | ||
fullWeekWidth?: boolean; | ||
}; | ||
declare class CurrentTimePluginImpl implements CurrentTimePlugin { | ||
private config | ||
name: string | ||
$app: CalendarAppSingleton | ||
observer: MutationObserver | null | ||
constructor(config?: CurrentTimePluginConfig) | ||
init($app: CalendarAppSingleton): void | ||
private setIndicator | ||
private createFullWidthIndicator | ||
destroy(): void | ||
private config; | ||
name: string; | ||
$app: CalendarAppSingleton; | ||
observer: MutationObserver | null; | ||
constructor(config?: CurrentTimePluginConfig); | ||
init($app: CalendarAppSingleton): void; | ||
private setIndicator; | ||
private createFullWidthIndicator; | ||
destroy(): void; | ||
} | ||
declare const createCurrentTimePlugin: ( | ||
config?: CurrentTimePluginConfig | ||
) => CurrentTimePluginImpl | ||
export { createCurrentTimePlugin } | ||
declare const createCurrentTimePlugin: (config?: CurrentTimePluginConfig) => CurrentTimePluginImpl; | ||
export { createCurrentTimePlugin }; |
281
dist/core.js
class NumberRangeError extends Error { | ||
constructor(min, max) { | ||
super(`Number must be between ${min} and ${max}.`) | ||
Object.defineProperty(this, 'min', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: min, | ||
}) | ||
Object.defineProperty(this, 'max', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: max, | ||
}) | ||
} | ||
constructor(min, max) { | ||
super(`Number must be between ${min} and ${max}.`); | ||
Object.defineProperty(this, "min", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: min | ||
}); | ||
Object.defineProperty(this, "max", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: max | ||
}); | ||
} | ||
} | ||
const doubleDigit = (number) => { | ||
if (number < 0 || number > 99) throw new NumberRangeError(0, 99) | ||
return String(number).padStart(2, '0') | ||
} | ||
if (number < 0 || number > 99) | ||
throw new NumberRangeError(0, 99); | ||
return String(number).padStart(2, '0'); | ||
}; | ||
const toDateString = (date) => { | ||
return `${date.getFullYear()}-${doubleDigit(date.getMonth() + 1)}-${doubleDigit(date.getDate())}` | ||
} | ||
return `${date.getFullYear()}-${doubleDigit(date.getMonth() + 1)}-${doubleDigit(date.getDate())}`; | ||
}; | ||
const toTimeString = (date) => { | ||
return `${doubleDigit(date.getHours())}:${doubleDigit(date.getMinutes())}` | ||
} | ||
return `${doubleDigit(date.getHours())}:${doubleDigit(date.getMinutes())}`; | ||
}; | ||
const toDateTimeString = (date) => { | ||
return `${toDateString(date)} ${toTimeString(date)}` | ||
} | ||
return `${toDateString(date)} ${toTimeString(date)}`; | ||
}; | ||
const timePointToPercentage = (timePointsInDay, dayBoundaries, timePoint) => { | ||
if (timePoint < dayBoundaries.start) { | ||
const firstDayTimePoints = 2400 - dayBoundaries.start | ||
return ((timePoint + firstDayTimePoints) / timePointsInDay) * 100 | ||
} | ||
return ((timePoint - dayBoundaries.start) / timePointsInDay) * 100 | ||
} | ||
if (timePoint < dayBoundaries.start) { | ||
const firstDayTimePoints = 2400 - dayBoundaries.start; | ||
return ((timePoint + firstDayTimePoints) / timePointsInDay) * 100; | ||
} | ||
return ((timePoint - dayBoundaries.start) / timePointsInDay) * 100; | ||
}; | ||
class InvalidTimeStringError extends Error { | ||
constructor(timeString) { | ||
super(`Invalid time string: ${timeString}`) | ||
} | ||
constructor(timeString) { | ||
super(`Invalid time string: ${timeString}`); | ||
} | ||
} | ||
// regex for strings between 00:00 and 23:59 | ||
const timeStringRegex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/ | ||
const timeStringRegex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/; | ||
const minuteTimePointMultiplier = 1.6666666666666667 // 100 / 60 | ||
const minuteTimePointMultiplier = 1.6666666666666667; // 100 / 60 | ||
const timePointsFromString = (timeString) => { | ||
if (!timeStringRegex.test(timeString)) | ||
throw new InvalidTimeStringError(timeString) | ||
const [hoursInt, minutesInt] = timeString | ||
.split(':') | ||
.map((time) => parseInt(time, 10)) | ||
let minutePoints = (minutesInt * minuteTimePointMultiplier).toString() | ||
if (minutePoints.split('.')[0].length < 2) minutePoints = `0${minutePoints}` | ||
return Number(hoursInt + minutePoints) | ||
} | ||
if (!timeStringRegex.test(timeString)) | ||
throw new InvalidTimeStringError(timeString); | ||
const [hoursInt, minutesInt] = timeString | ||
.split(':') | ||
.map((time) => parseInt(time, 10)); | ||
let minutePoints = (minutesInt * minuteTimePointMultiplier).toString(); | ||
if (minutePoints.split('.')[0].length < 2) | ||
minutePoints = `0${minutePoints}`; | ||
return Number(hoursInt + minutePoints); | ||
}; | ||
const timeFromDateTime = (dateTime) => { | ||
return dateTime.slice(11) | ||
} | ||
return dateTime.slice(11); | ||
}; | ||
const getYCoordinateInTimeGrid = ( | ||
dateTimeString, | ||
dayBoundaries, | ||
pointsPerDay | ||
) => { | ||
return timePointToPercentage( | ||
pointsPerDay, | ||
dayBoundaries, | ||
timePointsFromString(timeFromDateTime(dateTimeString)) | ||
) | ||
} | ||
const getYCoordinateInTimeGrid = (dateTimeString, dayBoundaries, pointsPerDay) => { | ||
return timePointToPercentage(pointsPerDay, dayBoundaries, timePointsFromString(timeFromDateTime(dateTimeString))); | ||
}; | ||
class CurrentTimePluginImpl { | ||
constructor(config = {}) { | ||
Object.defineProperty(this, 'config', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: config, | ||
}) | ||
Object.defineProperty(this, 'name', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 'current-time-plugin', | ||
}) | ||
Object.defineProperty(this, '$app', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0, | ||
}) | ||
Object.defineProperty(this, 'observer', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: null, | ||
}) | ||
} | ||
init($app) { | ||
this.$app = $app | ||
this.observer = new MutationObserver((mutationList) => { | ||
for (const mutation of mutationList) { | ||
if (mutation.type === 'childList') { | ||
this.setIndicator() | ||
constructor(config = {}) { | ||
Object.defineProperty(this, "config", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: config | ||
}); | ||
Object.defineProperty(this, "name", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 'current-time-plugin' | ||
}); | ||
Object.defineProperty(this, "$app", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "observer", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: null | ||
}); | ||
} | ||
init($app) { | ||
this.$app = $app; | ||
this.observer = new MutationObserver((mutationList) => { | ||
for (const mutation of mutationList) { | ||
if (mutation.type === 'childList') { | ||
this.setIndicator(); | ||
} | ||
} | ||
}); | ||
const calendarWrapper = $app.elements.calendarWrapper; | ||
if (!calendarWrapper) { | ||
throw new Error('Calendar wrapper not found'); | ||
} | ||
} | ||
}) | ||
const calendarWrapper = $app.elements.calendarWrapper | ||
if (!calendarWrapper) { | ||
throw new Error('Calendar wrapper not found') | ||
this.observer.observe(calendarWrapper, { | ||
childList: true, | ||
subtree: true, | ||
}); | ||
} | ||
this.observer.observe(calendarWrapper, { | ||
childList: true, | ||
subtree: true, | ||
}) | ||
} | ||
setIndicator(isRecursion = false) { | ||
const todayDateString = toDateString(new Date()) | ||
const nowDateTimeString = toDateTimeString(new Date()) | ||
const todayElement = this.$app.elements.calendarWrapper.querySelector( | ||
`[data-time-grid-date="${todayDateString}"]` | ||
) | ||
if (!todayElement) return | ||
const existingIndicator = todayElement.querySelector( | ||
'.sx__current-time-indicator' | ||
) | ||
if (existingIndicator && isRecursion) existingIndicator.remove() | ||
if (todayElement && !existingIndicator) { | ||
const currentTimeIndicator = document.createElement('div') | ||
currentTimeIndicator.classList.add('sx__current-time-indicator') | ||
const top = | ||
getYCoordinateInTimeGrid( | ||
nowDateTimeString, | ||
this.$app.config.dayBoundaries, | ||
this.$app.config.timePointsPerDay | ||
) + '%' | ||
currentTimeIndicator.style.top = top | ||
todayElement.appendChild(currentTimeIndicator) | ||
if (this.config.fullWeekWidth) { | ||
this.createFullWidthIndicator(top) | ||
} | ||
setTimeout( | ||
this.setIndicator.bind(this, true), | ||
60000 - (Date.now() % 60000) | ||
) | ||
setIndicator(isRecursion = false) { | ||
const todayDateString = toDateString(new Date()); | ||
const nowDateTimeString = toDateTimeString(new Date()); | ||
const todayElement = this.$app.elements.calendarWrapper.querySelector(`[data-time-grid-date="${todayDateString}"]`); | ||
if (!todayElement) | ||
return; | ||
const existingIndicator = todayElement.querySelector('.sx__current-time-indicator'); | ||
if (existingIndicator && isRecursion) | ||
existingIndicator.remove(); | ||
if (todayElement && !existingIndicator) { | ||
const currentTimeIndicator = document.createElement('div'); | ||
currentTimeIndicator.classList.add('sx__current-time-indicator'); | ||
const top = getYCoordinateInTimeGrid(nowDateTimeString, this.$app.config.dayBoundaries, this.$app.config.timePointsPerDay) + '%'; | ||
currentTimeIndicator.style.top = top; | ||
todayElement.appendChild(currentTimeIndicator); | ||
if (this.config.fullWeekWidth) { | ||
this.createFullWidthIndicator(top); | ||
} | ||
setTimeout(this.setIndicator.bind(this, true), 60000 - (Date.now() % 60000)); | ||
} | ||
} | ||
} | ||
createFullWidthIndicator(top) { | ||
const fullWeekTimeIndicator = document.createElement('div') | ||
fullWeekTimeIndicator.classList.add('sx__current-time-indicator-full-week') | ||
fullWeekTimeIndicator.style.top = top | ||
const weekGridWrapper = document.querySelector('.sx__week-grid') | ||
const existingFullWeekIndicator = | ||
weekGridWrapper === null || weekGridWrapper === void 0 | ||
? void 0 | ||
: weekGridWrapper.querySelector('.sx__current-time-indicator-full-week') | ||
if (existingFullWeekIndicator) { | ||
existingFullWeekIndicator.remove() | ||
createFullWidthIndicator(top) { | ||
const fullWeekTimeIndicator = document.createElement('div'); | ||
fullWeekTimeIndicator.classList.add('sx__current-time-indicator-full-week'); | ||
fullWeekTimeIndicator.style.top = top; | ||
const weekGridWrapper = document.querySelector('.sx__week-grid'); | ||
const existingFullWeekIndicator = weekGridWrapper === null || weekGridWrapper === void 0 ? void 0 : weekGridWrapper.querySelector('.sx__current-time-indicator-full-week'); | ||
if (existingFullWeekIndicator) { | ||
existingFullWeekIndicator.remove(); | ||
} | ||
if (weekGridWrapper) { | ||
weekGridWrapper.appendChild(fullWeekTimeIndicator); | ||
} | ||
} | ||
if (weekGridWrapper) { | ||
weekGridWrapper.appendChild(fullWeekTimeIndicator) | ||
destroy() { | ||
if (this.observer) { | ||
this.observer.disconnect(); | ||
} | ||
} | ||
} | ||
destroy() { | ||
if (this.observer) { | ||
this.observer.disconnect() | ||
} | ||
} | ||
} | ||
const createCurrentTimePlugin = (config) => new CurrentTimePluginImpl(config) | ||
const createCurrentTimePlugin = (config) => new CurrentTimePluginImpl(config); | ||
export { createCurrentTimePlugin } | ||
export { createCurrentTimePlugin }; |
@@ -1,51 +0,51 @@ | ||
import { Signal } from '@preact/signals' | ||
import { JSXInternal } from 'preact/src/jsx' | ||
import { Signal } from "@preact/signals"; | ||
import { JSXInternal } from "preact/src/jsx"; | ||
declare enum WeekDay { | ||
SUNDAY = 0, | ||
MONDAY = 1, | ||
TUESDAY = 2, | ||
WEDNESDAY = 3, | ||
THURSDAY = 4, | ||
FRIDAY = 5, | ||
SATURDAY = 6, | ||
SUNDAY = 0, | ||
MONDAY = 1, | ||
TUESDAY = 2, | ||
WEDNESDAY = 3, | ||
THURSDAY = 4, | ||
FRIDAY = 5, | ||
SATURDAY = 6 | ||
} | ||
type WeekWithDates = Date[] | ||
type MonthWithDates = Date[][] | ||
type WeekWithDates = Date[]; | ||
type MonthWithDates = Date[][]; | ||
declare enum Month { | ||
JANUARY = 0, | ||
FEBRUARY = 1, | ||
MARCH = 2, | ||
APRIL = 3, | ||
MAY = 4, | ||
JUNE = 5, | ||
JULY = 6, | ||
AUGUST = 7, | ||
SEPTEMBER = 8, | ||
OCTOBER = 9, | ||
NOVEMBER = 10, | ||
DECEMBER = 11, | ||
JANUARY = 0, | ||
FEBRUARY = 1, | ||
MARCH = 2, | ||
APRIL = 3, | ||
MAY = 4, | ||
JUNE = 5, | ||
JULY = 6, | ||
AUGUST = 7, | ||
SEPTEMBER = 8, | ||
OCTOBER = 9, | ||
NOVEMBER = 10, | ||
DECEMBER = 11 | ||
} | ||
interface TimeUnits { | ||
firstDayOfWeek: WeekDay | ||
getMonthWithTrailingAndLeadingDays(year: number, month: Month): MonthWithDates | ||
getWeekFor(date: Date): WeekWithDates | ||
getMonthsFor(year: number): Date[] | ||
firstDayOfWeek: WeekDay; | ||
getMonthWithTrailingAndLeadingDays(year: number, month: Month): MonthWithDates; | ||
getWeekFor(date: Date): WeekWithDates; | ||
getMonthsFor(year: number): Date[]; | ||
} | ||
declare enum DatePickerView { | ||
MONTH_DAYS = 'month-days', | ||
YEARS = 'years', | ||
MONTH_DAYS = "month-days", | ||
YEARS = "years" | ||
} | ||
interface DatePickerState { | ||
isOpen: Signal<boolean> | ||
selectedDate: Signal<string> | ||
inputDisplayedValue: Signal<string> | ||
datePickerDate: Signal<string> | ||
datePickerView: Signal<DatePickerView> | ||
inputWrapperElement: Signal<HTMLDivElement | undefined> | ||
open(): void | ||
close(): void | ||
toggle(): void | ||
setView(view: DatePickerView): void | ||
isOpen: Signal<boolean>; | ||
selectedDate: Signal<string>; | ||
inputDisplayedValue: Signal<string>; | ||
datePickerDate: Signal<string>; | ||
datePickerView: Signal<DatePickerView>; | ||
inputWrapperElement: Signal<HTMLDivElement | undefined>; | ||
open(): void; | ||
close(): void; | ||
toggle(): void; | ||
setView(view: DatePickerView): void; | ||
} | ||
type TranslateFn = (key: string) => string | ||
type TranslateFn = (key: string) => string; | ||
/** | ||
@@ -55,5 +55,5 @@ * This interface serves as a bridge between the AppSingleton for the date picker and calendar | ||
interface AppSingleton { | ||
timeUnitsImpl: TimeUnits | ||
datePickerState: DatePickerState | ||
translate: TranslateFn | ||
timeUnitsImpl: TimeUnits; | ||
datePickerState: DatePickerState; | ||
translate: TranslateFn; | ||
} | ||
@@ -64,321 +64,298 @@ /** | ||
interface Config { | ||
locale: string | ||
firstDayOfWeek: WeekDay | ||
locale: string; | ||
firstDayOfWeek: WeekDay; | ||
} | ||
declare enum Placement { | ||
TOP_START = 'top-start', | ||
TOP_END = 'top-end', | ||
BOTTOM_START = 'bottom-start', | ||
BOTTOM_END = 'bottom-end', | ||
TOP_START = "top-start", | ||
TOP_END = "top-end", | ||
BOTTOM_START = "bottom-start", | ||
BOTTOM_END = "bottom-end" | ||
} | ||
type DatePickerListeners = { | ||
onChange?: (date: string) => void | ||
} | ||
onChange?: (date: string) => void; | ||
}; | ||
type DatePickerStyle = { | ||
dark?: boolean | ||
fullWidth?: boolean | ||
} | ||
dark?: boolean; | ||
fullWidth?: boolean; | ||
}; | ||
interface DatePickerConfigInternal extends Config { | ||
min: string | ||
max: string | ||
placement: Placement | ||
listeners: DatePickerListeners | ||
style: DatePickerStyle | ||
teleportTo?: HTMLElement | ||
label?: string | ||
min: string; | ||
max: string; | ||
placement: Placement; | ||
listeners: DatePickerListeners; | ||
style: DatePickerStyle; | ||
teleportTo?: HTMLElement; | ||
label?: string; | ||
} | ||
// This enum is used to represent names of all internally built views of the calendar | ||
declare enum InternalViewName { | ||
Day = 'day', | ||
Week = 'week', | ||
MonthGrid = 'month-grid', | ||
MonthAgenda = 'month-agenda', | ||
Day = "day", | ||
Week = "week", | ||
MonthGrid = "month-grid", | ||
MonthAgenda = "month-agenda" | ||
} | ||
// Since implementers can use custom views, we need to have a type that combines the internal views with these custom views | ||
type ViewName = InternalViewName | string | ||
type ViewName = InternalViewName | string; | ||
type DateRange = { | ||
start: string | ||
end: string | ||
} | ||
start: string; | ||
end: string; | ||
}; | ||
interface RangeSetterConfig { | ||
date: string | ||
timeUnitsImpl: TimeUnits | ||
calendarConfig: CalendarConfigInternal | ||
range: Signal<DateRange | null> | ||
date: string; | ||
timeUnitsImpl: TimeUnits; | ||
calendarConfig: CalendarConfigInternal; | ||
range: Signal<DateRange | null>; | ||
} | ||
type PreactViewComponent = (props: { | ||
$app: CalendarAppSingleton | ||
id: string | ||
}) => JSXInternal.Element | ||
declare const addMonths: (to: string, nMonths: number) => string | ||
declare const addDays: (to: string, nDays: number) => string | ||
$app: CalendarAppSingleton; | ||
id: string; | ||
}) => JSXInternal.Element; | ||
declare const addMonths: (to: string, nMonths: number) => string; | ||
declare const addDays: (to: string, nDays: number) => string; | ||
type ViewConfig<FrameworkComponent = PreactViewComponent> = { | ||
/** | ||
* a unique identifier for the view | ||
* */ | ||
name: ViewName | ||
/** | ||
* text that will be displayed in the view dropdown | ||
* */ | ||
label: string | ||
/** | ||
* function that is called when a new date is selected | ||
* */ | ||
setDateRange: (config: RangeSetterConfig) => DateRange | ||
/** | ||
* should the view be displayed on small screens (< 700px calendar width) | ||
* */ | ||
hasSmallScreenCompat: boolean | ||
/** | ||
* should the view be displayed on wide screens (> 700px calendar width) | ||
* */ | ||
hasWideScreenCompat: boolean | ||
/** | ||
* The component you want to render | ||
* */ | ||
Component: FrameworkComponent | ||
/** | ||
* function that is called when the user clicks the backward/forward button | ||
* */ | ||
backwardForwardFn: typeof addDays | typeof addMonths | ||
/** | ||
* number of units to add into the backwardForwardFn function. Result behind the scenes for example: | ||
* backwardForwardFn = addDays | ||
* backwardForwardUnits = 1 | ||
* result (behind the scenes) = addDays(date, 1) | ||
* */ | ||
backwardForwardUnits: number | ||
} | ||
type View<FrameworkComponent = PreactViewComponent> = | ||
ViewConfig<FrameworkComponent> & { | ||
render(onElement: HTMLElement, $app: CalendarAppSingleton): void | ||
destroy(): void | ||
} | ||
type EventId = number | string | ||
type startDate = string | ||
type nDays = number | ||
type EventFragments = Record<startDate, nDays> | ||
/** | ||
* a unique identifier for the view | ||
* */ | ||
name: ViewName; | ||
/** | ||
* text that will be displayed in the view dropdown | ||
* */ | ||
label: string; | ||
/** | ||
* function that is called when a new date is selected | ||
* */ | ||
setDateRange: (config: RangeSetterConfig) => DateRange; | ||
/** | ||
* should the view be displayed on small screens (< 700px calendar width) | ||
* */ | ||
hasSmallScreenCompat: boolean; | ||
/** | ||
* should the view be displayed on wide screens (> 700px calendar width) | ||
* */ | ||
hasWideScreenCompat: boolean; | ||
/** | ||
* The component you want to render | ||
* */ | ||
Component: FrameworkComponent; | ||
/** | ||
* function that is called when the user clicks the backward/forward button | ||
* */ | ||
backwardForwardFn: typeof addDays | typeof addMonths; | ||
/** | ||
* number of units to add into the backwardForwardFn function. Result behind the scenes for example: | ||
* backwardForwardFn = addDays | ||
* backwardForwardUnits = 1 | ||
* result (behind the scenes) = addDays(date, 1) | ||
* */ | ||
backwardForwardUnits: number; | ||
}; | ||
type View<FrameworkComponent = PreactViewComponent> = ViewConfig<FrameworkComponent> & { | ||
render(onElement: HTMLElement, $app: CalendarAppSingleton): void; | ||
destroy(): void; | ||
}; | ||
type EventId = number | string; | ||
type startDate = string; | ||
type nDays = number; | ||
type EventFragments = Record<startDate, nDays>; | ||
type CalendarEventOptions = { | ||
disableDND?: boolean; | ||
disableResize?: boolean; | ||
additionalClasses?: string[]; | ||
}; | ||
interface CalendarEventExternal { | ||
id: EventId | ||
start: string | ||
end: string | ||
title?: string | ||
people?: string[] | ||
location?: string | ||
description?: string | ||
calendarId?: string | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
[key: string]: any | ||
id: EventId; | ||
start: string; | ||
end: string; | ||
title?: string; | ||
people?: string[]; | ||
location?: string; | ||
description?: string; | ||
calendarId?: string; | ||
_options?: CalendarEventOptions; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
[key: string]: any; | ||
} | ||
interface CalendarEventInternal extends CalendarEventExternal { | ||
// event duration | ||
_isSingleDayTimed: boolean | ||
_isSingleDayFullDay: boolean | ||
_isSingleHybridDayTimed: boolean | ||
_isMultiDayTimed: boolean | ||
_isMultiDayFullDay: boolean | ||
// week time grid | ||
_previousConcurrentEvents: number | undefined | ||
_totalConcurrentEvents: number | undefined | ||
// week date grid | ||
_nDaysInGrid: number | undefined | ||
// month grid | ||
_eventFragments: EventFragments | ||
_color: string | ||
_getForeignProperties(): Record<string, unknown> | ||
_getExternalEvent(): CalendarEventExternal | ||
// event duration | ||
_isSingleDayTimed: boolean; | ||
_isSingleDayFullDay: boolean; | ||
_isSingleHybridDayTimed: boolean; | ||
_isMultiDayTimed: boolean; | ||
_isMultiDayFullDay: boolean; | ||
// week time grid | ||
_previousConcurrentEvents: number | undefined; | ||
_totalConcurrentEvents: number | undefined; | ||
// week date grid | ||
_nDaysInGrid: number | undefined; | ||
// month grid | ||
_eventFragments: EventFragments; | ||
_color: string; | ||
_getForeignProperties(): Record<string, unknown>; | ||
_getExternalEvent(): CalendarEventExternal; | ||
} | ||
type DayBoundariesInternal = { | ||
start: number | ||
end: number | ||
start: number; | ||
end: number; | ||
}; | ||
interface TimeGridDragHandler { | ||
} | ||
interface TimeGridDragHandler {} | ||
type DayBoundariesDateTime = { | ||
start: string | ||
end: string | ||
start: string; | ||
end: string; | ||
}; | ||
interface DateGridDragHandler { | ||
} | ||
interface DateGridDragHandler {} | ||
interface EventCoordinates { | ||
clientX: number | ||
clientY: number | ||
clientX: number; | ||
clientY: number; | ||
} | ||
interface DragHandlerDependencies { | ||
$app: CalendarAppSingleton | ||
eventCoordinates: EventCoordinates | ||
eventCopy: CalendarEventInternal | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void | ||
$app: CalendarAppSingleton; | ||
eventCoordinates: EventCoordinates; | ||
eventCopy: CalendarEventInternal; | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void; | ||
} | ||
interface MonthGridDragHandler {} | ||
interface MonthGridDragHandler { | ||
} | ||
interface DragAndDropPlugin extends PluginBase { | ||
createTimeGridDragHandler( | ||
dependencies: DragHandlerDependencies, | ||
dayBoundariesDateTime: DayBoundariesDateTime | ||
): TimeGridDragHandler | ||
createDateGridDragHandler( | ||
dependencies: DragHandlerDependencies | ||
): DateGridDragHandler | ||
createMonthGridDragHandler( | ||
calendarEvent: CalendarEventInternal, | ||
$app: CalendarAppSingleton | ||
): MonthGridDragHandler | ||
createTimeGridDragHandler(dependencies: DragHandlerDependencies, dayBoundariesDateTime: DayBoundariesDateTime): TimeGridDragHandler; | ||
createDateGridDragHandler(dependencies: DragHandlerDependencies): DateGridDragHandler; | ||
createMonthGridDragHandler(calendarEvent: CalendarEventInternal, $app: CalendarAppSingleton): MonthGridDragHandler; | ||
} | ||
type EventModalProps = { | ||
$app: CalendarAppSingleton | ||
} | ||
$app: CalendarAppSingleton; | ||
}; | ||
interface EventModalPlugin extends PluginBase { | ||
calendarEvent: Signal<CalendarEventInternal | null> | ||
calendarEventDOMRect: Signal<DOMRect | null> | ||
calendarEventElement: Signal<HTMLElement | null> | ||
setCalendarEvent( | ||
event: CalendarEventInternal | null, | ||
eventTargetDOMRect: DOMRect | null | ||
): void | ||
ComponentFn(props: EventModalProps): JSXInternal.Element | ||
calendarEvent: Signal<CalendarEventInternal | null>; | ||
calendarEventDOMRect: Signal<DOMRect | null>; | ||
calendarEventElement: Signal<HTMLElement | null>; | ||
setCalendarEvent(event: CalendarEventInternal | null, eventTargetDOMRect: DOMRect | null): void; | ||
ComponentFn(props: EventModalProps): JSXInternal.Element; | ||
} | ||
interface CalendarCallbacks { | ||
onEventUpdate?: (event: CalendarEventExternal) => void | ||
onEventClick?: (event: CalendarEventExternal) => void | ||
onRangeUpdate?: (range: DateRange) => void | ||
onSelectedDateUpdate?: (date: string) => void | ||
onClickDate?: (date: string) => void | ||
onClickDateTime?: (dateTime: string) => void | ||
onClickPlusEvents?: (date: string) => void | ||
onEventUpdate?: (event: CalendarEventExternal) => void; | ||
onEventClick?: (event: CalendarEventExternal) => void; | ||
onRangeUpdate?: (range: DateRange) => void; | ||
onSelectedDateUpdate?: (date: string) => void; | ||
onClickDate?: (date: string) => void; | ||
onClickDateTime?: (dateTime: string) => void; | ||
onClickPlusEvents?: (date: string) => void; | ||
} | ||
type CustomComponentFns = { | ||
timeGridEvent?: CustomComponentFn | ||
dateGridEvent?: CustomComponentFn | ||
monthGridEvent?: CustomComponentFn | ||
monthAgendaEvent?: CustomComponentFn | ||
eventModal?: CustomComponentFn | ||
} | ||
timeGridEvent?: CustomComponentFn; | ||
dateGridEvent?: CustomComponentFn; | ||
monthGridEvent?: CustomComponentFn; | ||
monthAgendaEvent?: CustomComponentFn; | ||
eventModal?: CustomComponentFn; | ||
}; | ||
interface EventsFacade { | ||
get(id: EventId): CalendarEventExternal | undefined | ||
getAll(): CalendarEventExternal[] | ||
add(event: CalendarEventExternal): void | ||
update(event: CalendarEventExternal): void | ||
remove(id: EventId): void | ||
set(events: CalendarEventExternal[]): void | ||
get(id: EventId): CalendarEventExternal | undefined; | ||
getAll(): CalendarEventExternal[]; | ||
add(event: CalendarEventExternal): void; | ||
update(event: CalendarEventExternal): void; | ||
remove(id: EventId): void; | ||
set(events: CalendarEventExternal[]): void; | ||
} | ||
interface EventRecurrencePlugin extends PluginBase { | ||
updateRecurrenceDND( | ||
eventId: EventId, | ||
oldEventStart: string, | ||
newEventStart: string | ||
): void | ||
updateRecurrenceOnResize( | ||
eventId: EventId, | ||
oldEventEnd: string, | ||
newEventEnd: string | ||
): void | ||
eventsFacade: EventsFacade | ||
updateRecurrenceDND(eventId: EventId, oldEventStart: string, newEventStart: string): void; | ||
updateRecurrenceOnResize(eventId: EventId, oldEventEnd: string, newEventEnd: string): void; | ||
eventsFacade: EventsFacade; | ||
} | ||
interface ResizePlugin extends PluginBase { | ||
createTimeGridEventResizer( | ||
calendarEvent: CalendarEventInternal, | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void, | ||
mouseDownEvent: MouseEvent, | ||
dayBoundariesDateTime: { | ||
start: string | ||
end: string | ||
} | ||
): void | ||
createDateGridEventResizer( | ||
calendarEvent: CalendarEventInternal, | ||
updateCopy: (newCopy: CalendarEventInternal | undefined) => void, | ||
mouseDownEvent: MouseEvent | ||
): void | ||
createTimeGridEventResizer(calendarEvent: CalendarEventInternal, updateCopy: (newCopy: CalendarEventInternal | undefined) => void, mouseDownEvent: MouseEvent, dayBoundariesDateTime: { | ||
start: string; | ||
end: string; | ||
}): void; | ||
createDateGridEventResizer(calendarEvent: CalendarEventInternal, updateCopy: (newCopy: CalendarEventInternal | undefined) => void, mouseDownEvent: MouseEvent): void; | ||
} | ||
type WeekOptions = { | ||
gridHeight: number | ||
} | ||
gridHeight: number; | ||
}; | ||
type MonthGridOptions = { | ||
nEventsPerDay: number | ||
} | ||
nEventsPerDay: number; | ||
}; | ||
type ColorDefinition = { | ||
main: string | ||
container: string | ||
onContainer: string | ||
} | ||
main: string; | ||
container: string; | ||
onContainer: string; | ||
}; | ||
type CalendarType = { | ||
colorName: string | ||
label?: string | ||
lightColors?: ColorDefinition | ||
darkColors?: ColorDefinition | ||
} | ||
colorName: string; | ||
label?: string; | ||
lightColors?: ColorDefinition; | ||
darkColors?: ColorDefinition; | ||
}; | ||
type Plugins = { | ||
dragAndDrop?: DragAndDropPlugin | ||
eventModal?: EventModalPlugin | ||
scrollController?: PluginBase | ||
eventRecurrence?: EventRecurrencePlugin | ||
resize?: ResizePlugin | ||
[key: string]: PluginBase | undefined | ||
} | ||
type CustomComponentFn = ( | ||
wrapperElement: HTMLElement, | ||
props: Record<string, unknown> | ||
) => void | ||
dragAndDrop?: DragAndDropPlugin; | ||
eventModal?: EventModalPlugin; | ||
scrollController?: PluginBase; | ||
eventRecurrence?: EventRecurrencePlugin; | ||
resize?: ResizePlugin; | ||
[key: string]: PluginBase | undefined; | ||
}; | ||
type CustomComponentFn = (wrapperElement: HTMLElement, props: Record<string, unknown>) => void; | ||
interface CalendarConfigInternal extends Config { | ||
defaultView: ViewName | ||
views: View[] | ||
dayBoundaries: DayBoundariesInternal | ||
weekOptions: WeekOptions | ||
monthGridOptions: MonthGridOptions | ||
calendars: Signal<Record<string, CalendarType>> | ||
plugins: Plugins | ||
isDark: boolean | ||
callbacks: CalendarCallbacks | ||
_customComponentFns: CustomComponentFns | ||
minDate?: string | ||
maxDate?: string | ||
// Getters | ||
isHybridDay: boolean | ||
timePointsPerDay: number | ||
defaultView: ViewName; | ||
views: View[]; | ||
dayBoundaries: DayBoundariesInternal; | ||
weekOptions: WeekOptions; | ||
monthGridOptions: MonthGridOptions; | ||
calendars: Signal<Record<string, CalendarType>>; | ||
plugins: Plugins; | ||
isDark: boolean; | ||
callbacks: CalendarCallbacks; | ||
_customComponentFns: CustomComponentFns; | ||
minDate?: string; | ||
maxDate?: string; | ||
// Getters | ||
isHybridDay: boolean; | ||
timePointsPerDay: number; | ||
} | ||
interface CalendarState { | ||
isCalendarSmall: Signal<boolean | undefined> | ||
view: Signal<ViewName> | ||
range: Signal<DateRange | null> | ||
isDark: Signal<boolean> | ||
setRange: (date: string) => void | ||
isCalendarSmall: Signal<boolean | undefined>; | ||
view: Signal<ViewName>; | ||
range: Signal<DateRange | null>; | ||
isDark: Signal<boolean>; | ||
setRange: (date: string) => void; | ||
} | ||
type EventsFilterPredicate = | ||
| ((event: CalendarEventInternal) => boolean) | ||
| undefined | ||
type EventsFilterPredicate = ((event: CalendarEventInternal) => boolean) | undefined; | ||
interface CalendarEvents { | ||
list: Signal<CalendarEventInternal[]> | ||
filterPredicate: Signal<EventsFilterPredicate> | ||
list: Signal<CalendarEventInternal[]>; | ||
filterPredicate: Signal<EventsFilterPredicate>; | ||
} | ||
interface CalendarElements { | ||
calendarWrapper: HTMLDivElement | undefined | ||
calendarWrapper: HTMLDivElement | undefined; | ||
} | ||
interface CalendarAppSingleton extends AppSingleton { | ||
config: CalendarConfigInternal | ||
datePickerConfig: DatePickerConfigInternal | ||
calendarState: CalendarState | ||
calendarEvents: CalendarEvents | ||
elements: CalendarElements | ||
config: CalendarConfigInternal; | ||
datePickerConfig: DatePickerConfigInternal; | ||
calendarState: CalendarState; | ||
calendarEvents: CalendarEvents; | ||
elements: CalendarElements; | ||
} | ||
interface PluginBase { | ||
name: string | ||
init?($app: CalendarAppSingleton): void | ||
destroy?(): void | ||
name: string; | ||
// TODO v2: change to `beforeRender` | ||
beforeInit?($app: CalendarAppSingleton): void; | ||
// TODO v2: change to `onRender` and remove $app parameter | ||
init?($app: CalendarAppSingleton): void; | ||
destroy?(): void; | ||
} | ||
interface CurrentTimePlugin extends PluginBase {} | ||
interface CurrentTimePlugin extends PluginBase { | ||
} | ||
type CurrentTimePluginConfig = { | ||
fullWeekWidth?: boolean | ||
} | ||
fullWeekWidth?: boolean; | ||
}; | ||
declare class CurrentTimePluginImpl implements CurrentTimePlugin { | ||
private config | ||
name: string | ||
$app: CalendarAppSingleton | ||
observer: MutationObserver | null | ||
constructor(config?: CurrentTimePluginConfig) | ||
init($app: CalendarAppSingleton): void | ||
private setIndicator | ||
private createFullWidthIndicator | ||
destroy(): void | ||
private config; | ||
name: string; | ||
$app: CalendarAppSingleton; | ||
observer: MutationObserver | null; | ||
constructor(config?: CurrentTimePluginConfig); | ||
init($app: CalendarAppSingleton): void; | ||
private setIndicator; | ||
private createFullWidthIndicator; | ||
destroy(): void; | ||
} | ||
declare const createCurrentTimePlugin: ( | ||
config?: CurrentTimePluginConfig | ||
) => CurrentTimePluginImpl | ||
export { createCurrentTimePlugin } | ||
declare const createCurrentTimePlugin: (config?: CurrentTimePluginConfig) => CurrentTimePluginImpl; | ||
export { createCurrentTimePlugin }; |
@@ -1,195 +0,168 @@ | ||
;(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' | ||
? factory(exports) | ||
: typeof define === 'function' && define.amd | ||
? define(['exports'], factory) | ||
: ((global = | ||
typeof globalThis !== 'undefined' ? globalThis : global || self), | ||
factory((global['@schedule-x/current-time'] = {}))) | ||
})(this, function (exports) { | ||
'use strict' | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@schedule-x/current-time"] = {})); | ||
})(this, (function (exports) { 'use strict'; | ||
class NumberRangeError extends Error { | ||
constructor(min, max) { | ||
super(`Number must be between ${min} and ${max}.`) | ||
Object.defineProperty(this, 'min', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: min, | ||
}) | ||
Object.defineProperty(this, 'max', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: max, | ||
}) | ||
} | ||
constructor(min, max) { | ||
super(`Number must be between ${min} and ${max}.`); | ||
Object.defineProperty(this, "min", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: min | ||
}); | ||
Object.defineProperty(this, "max", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: max | ||
}); | ||
} | ||
} | ||
const doubleDigit = (number) => { | ||
if (number < 0 || number > 99) throw new NumberRangeError(0, 99) | ||
return String(number).padStart(2, '0') | ||
} | ||
if (number < 0 || number > 99) | ||
throw new NumberRangeError(0, 99); | ||
return String(number).padStart(2, '0'); | ||
}; | ||
const toDateString = (date) => { | ||
return `${date.getFullYear()}-${doubleDigit(date.getMonth() + 1)}-${doubleDigit(date.getDate())}` | ||
} | ||
return `${date.getFullYear()}-${doubleDigit(date.getMonth() + 1)}-${doubleDigit(date.getDate())}`; | ||
}; | ||
const toTimeString = (date) => { | ||
return `${doubleDigit(date.getHours())}:${doubleDigit(date.getMinutes())}` | ||
} | ||
return `${doubleDigit(date.getHours())}:${doubleDigit(date.getMinutes())}`; | ||
}; | ||
const toDateTimeString = (date) => { | ||
return `${toDateString(date)} ${toTimeString(date)}` | ||
} | ||
return `${toDateString(date)} ${toTimeString(date)}`; | ||
}; | ||
const timePointToPercentage = (timePointsInDay, dayBoundaries, timePoint) => { | ||
if (timePoint < dayBoundaries.start) { | ||
const firstDayTimePoints = 2400 - dayBoundaries.start | ||
return ((timePoint + firstDayTimePoints) / timePointsInDay) * 100 | ||
} | ||
return ((timePoint - dayBoundaries.start) / timePointsInDay) * 100 | ||
} | ||
if (timePoint < dayBoundaries.start) { | ||
const firstDayTimePoints = 2400 - dayBoundaries.start; | ||
return ((timePoint + firstDayTimePoints) / timePointsInDay) * 100; | ||
} | ||
return ((timePoint - dayBoundaries.start) / timePointsInDay) * 100; | ||
}; | ||
class InvalidTimeStringError extends Error { | ||
constructor(timeString) { | ||
super(`Invalid time string: ${timeString}`) | ||
} | ||
constructor(timeString) { | ||
super(`Invalid time string: ${timeString}`); | ||
} | ||
} | ||
// regex for strings between 00:00 and 23:59 | ||
const timeStringRegex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/ | ||
const timeStringRegex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/; | ||
const minuteTimePointMultiplier = 1.6666666666666667 // 100 / 60 | ||
const minuteTimePointMultiplier = 1.6666666666666667; // 100 / 60 | ||
const timePointsFromString = (timeString) => { | ||
if (!timeStringRegex.test(timeString)) | ||
throw new InvalidTimeStringError(timeString) | ||
const [hoursInt, minutesInt] = timeString | ||
.split(':') | ||
.map((time) => parseInt(time, 10)) | ||
let minutePoints = (minutesInt * minuteTimePointMultiplier).toString() | ||
if (minutePoints.split('.')[0].length < 2) minutePoints = `0${minutePoints}` | ||
return Number(hoursInt + minutePoints) | ||
} | ||
if (!timeStringRegex.test(timeString)) | ||
throw new InvalidTimeStringError(timeString); | ||
const [hoursInt, minutesInt] = timeString | ||
.split(':') | ||
.map((time) => parseInt(time, 10)); | ||
let minutePoints = (minutesInt * minuteTimePointMultiplier).toString(); | ||
if (minutePoints.split('.')[0].length < 2) | ||
minutePoints = `0${minutePoints}`; | ||
return Number(hoursInt + minutePoints); | ||
}; | ||
const timeFromDateTime = (dateTime) => { | ||
return dateTime.slice(11) | ||
} | ||
return dateTime.slice(11); | ||
}; | ||
const getYCoordinateInTimeGrid = ( | ||
dateTimeString, | ||
dayBoundaries, | ||
pointsPerDay | ||
) => { | ||
return timePointToPercentage( | ||
pointsPerDay, | ||
dayBoundaries, | ||
timePointsFromString(timeFromDateTime(dateTimeString)) | ||
) | ||
} | ||
const getYCoordinateInTimeGrid = (dateTimeString, dayBoundaries, pointsPerDay) => { | ||
return timePointToPercentage(pointsPerDay, dayBoundaries, timePointsFromString(timeFromDateTime(dateTimeString))); | ||
}; | ||
class CurrentTimePluginImpl { | ||
constructor(config = {}) { | ||
Object.defineProperty(this, 'config', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: config, | ||
}) | ||
Object.defineProperty(this, 'name', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 'current-time-plugin', | ||
}) | ||
Object.defineProperty(this, '$app', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0, | ||
}) | ||
Object.defineProperty(this, 'observer', { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: null, | ||
}) | ||
} | ||
init($app) { | ||
this.$app = $app | ||
this.observer = new MutationObserver((mutationList) => { | ||
for (const mutation of mutationList) { | ||
if (mutation.type === 'childList') { | ||
this.setIndicator() | ||
constructor(config = {}) { | ||
Object.defineProperty(this, "config", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: config | ||
}); | ||
Object.defineProperty(this, "name", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 'current-time-plugin' | ||
}); | ||
Object.defineProperty(this, "$app", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "observer", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: null | ||
}); | ||
} | ||
init($app) { | ||
this.$app = $app; | ||
this.observer = new MutationObserver((mutationList) => { | ||
for (const mutation of mutationList) { | ||
if (mutation.type === 'childList') { | ||
this.setIndicator(); | ||
} | ||
} | ||
}); | ||
const calendarWrapper = $app.elements.calendarWrapper; | ||
if (!calendarWrapper) { | ||
throw new Error('Calendar wrapper not found'); | ||
} | ||
} | ||
}) | ||
const calendarWrapper = $app.elements.calendarWrapper | ||
if (!calendarWrapper) { | ||
throw new Error('Calendar wrapper not found') | ||
this.observer.observe(calendarWrapper, { | ||
childList: true, | ||
subtree: true, | ||
}); | ||
} | ||
this.observer.observe(calendarWrapper, { | ||
childList: true, | ||
subtree: true, | ||
}) | ||
} | ||
setIndicator(isRecursion = false) { | ||
const todayDateString = toDateString(new Date()) | ||
const nowDateTimeString = toDateTimeString(new Date()) | ||
const todayElement = this.$app.elements.calendarWrapper.querySelector( | ||
`[data-time-grid-date="${todayDateString}"]` | ||
) | ||
if (!todayElement) return | ||
const existingIndicator = todayElement.querySelector( | ||
'.sx__current-time-indicator' | ||
) | ||
if (existingIndicator && isRecursion) existingIndicator.remove() | ||
if (todayElement && !existingIndicator) { | ||
const currentTimeIndicator = document.createElement('div') | ||
currentTimeIndicator.classList.add('sx__current-time-indicator') | ||
const top = | ||
getYCoordinateInTimeGrid( | ||
nowDateTimeString, | ||
this.$app.config.dayBoundaries, | ||
this.$app.config.timePointsPerDay | ||
) + '%' | ||
currentTimeIndicator.style.top = top | ||
todayElement.appendChild(currentTimeIndicator) | ||
if (this.config.fullWeekWidth) { | ||
this.createFullWidthIndicator(top) | ||
} | ||
setTimeout( | ||
this.setIndicator.bind(this, true), | ||
60000 - (Date.now() % 60000) | ||
) | ||
setIndicator(isRecursion = false) { | ||
const todayDateString = toDateString(new Date()); | ||
const nowDateTimeString = toDateTimeString(new Date()); | ||
const todayElement = this.$app.elements.calendarWrapper.querySelector(`[data-time-grid-date="${todayDateString}"]`); | ||
if (!todayElement) | ||
return; | ||
const existingIndicator = todayElement.querySelector('.sx__current-time-indicator'); | ||
if (existingIndicator && isRecursion) | ||
existingIndicator.remove(); | ||
if (todayElement && !existingIndicator) { | ||
const currentTimeIndicator = document.createElement('div'); | ||
currentTimeIndicator.classList.add('sx__current-time-indicator'); | ||
const top = getYCoordinateInTimeGrid(nowDateTimeString, this.$app.config.dayBoundaries, this.$app.config.timePointsPerDay) + '%'; | ||
currentTimeIndicator.style.top = top; | ||
todayElement.appendChild(currentTimeIndicator); | ||
if (this.config.fullWeekWidth) { | ||
this.createFullWidthIndicator(top); | ||
} | ||
setTimeout(this.setIndicator.bind(this, true), 60000 - (Date.now() % 60000)); | ||
} | ||
} | ||
} | ||
createFullWidthIndicator(top) { | ||
const fullWeekTimeIndicator = document.createElement('div') | ||
fullWeekTimeIndicator.classList.add( | ||
'sx__current-time-indicator-full-week' | ||
) | ||
fullWeekTimeIndicator.style.top = top | ||
const weekGridWrapper = document.querySelector('.sx__week-grid') | ||
const existingFullWeekIndicator = | ||
weekGridWrapper === null || weekGridWrapper === void 0 | ||
? void 0 | ||
: weekGridWrapper.querySelector( | ||
'.sx__current-time-indicator-full-week' | ||
) | ||
if (existingFullWeekIndicator) { | ||
existingFullWeekIndicator.remove() | ||
createFullWidthIndicator(top) { | ||
const fullWeekTimeIndicator = document.createElement('div'); | ||
fullWeekTimeIndicator.classList.add('sx__current-time-indicator-full-week'); | ||
fullWeekTimeIndicator.style.top = top; | ||
const weekGridWrapper = document.querySelector('.sx__week-grid'); | ||
const existingFullWeekIndicator = weekGridWrapper === null || weekGridWrapper === void 0 ? void 0 : weekGridWrapper.querySelector('.sx__current-time-indicator-full-week'); | ||
if (existingFullWeekIndicator) { | ||
existingFullWeekIndicator.remove(); | ||
} | ||
if (weekGridWrapper) { | ||
weekGridWrapper.appendChild(fullWeekTimeIndicator); | ||
} | ||
} | ||
if (weekGridWrapper) { | ||
weekGridWrapper.appendChild(fullWeekTimeIndicator) | ||
destroy() { | ||
if (this.observer) { | ||
this.observer.disconnect(); | ||
} | ||
} | ||
} | ||
destroy() { | ||
if (this.observer) { | ||
this.observer.disconnect() | ||
} | ||
} | ||
} | ||
const createCurrentTimePlugin = (config) => new CurrentTimePluginImpl(config) | ||
const createCurrentTimePlugin = (config) => new CurrentTimePluginImpl(config); | ||
exports.createCurrentTimePlugin = createCurrentTimePlugin | ||
}) | ||
exports.createCurrentTimePlugin = createCurrentTimePlugin; | ||
})); |
{ | ||
"name": "@schedule-x/current-time", | ||
"version": "1.43.0", | ||
"version": "1.44.0", | ||
"description": "Schedule-X plugin for displaying an indicator for the current time", | ||
@@ -36,3 +36,3 @@ "author": { | ||
"homepage": "https://schedule-x.dev", | ||
"gitHead": "c3696b1b9e8cbad792a41ad6e36a732e9bd982ac" | ||
"gitHead": "bc64a6613d557c2378364be84f43d0871415a045" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
60625
10
1685