@dxos/react-ui-calendar 0.9.1-main.c7dcc2e112 → 0.9.1-staging.ee54ba693a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/Calendar/Calendar.tsx", "../../../src/components/Calendar/context.ts", "../../../src/components/Calendar/util.ts", "../../../src/components/Calendar/Week.tsx", "../../../src/components/Calendar/Weekdays.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { addDays, format, startOfDay } from 'date-fns';\nimport React, {\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type PropsWithChildren,\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useResizeDetector } from 'react-resize-detector';\nimport { List, type ListProps, type ListRowRenderer } from 'react-virtualized';\n\nimport { Event } from '@dxos/async';\nimport { IconButton, useTranslation } from '@dxos/react-ui';\nimport { composable, composableProps } from '@dxos/react-ui';\nimport { mx } from '@dxos/ui-theme';\n\nimport { translationKey } from '#translations';\n\nimport {\n type CalendarContextValue,\n type CalendarController,\n CalendarContextProvider,\n type CalendarScrollEvent,\n type Range,\n useCalendarContext,\n} from './context';\nimport { getDate, getRowIndex, gridEpoch, isSameDay } from './util';\nimport { CalendarWeek, type CalendarEvent, type CalendarWeekProps } from './Week';\nimport { Weekdays } from './Weekdays';\n\nconst maxRows = 50 * 100;\nconst start = gridEpoch;\nconst size = 40;\nconst defaultWidth = 7 * size;\n\n// Auto-scroll while dragging near a vertical edge.\nconst EDGE_SCROLL_ZONE = 32; // px\nconst EDGE_SCROLL_MAX_SPEED = 12; // px per frame\n\nconst DATE_CLASS_NAMES = {\n current: 'ring-2 ring-primary-500',\n today: 'border-2 border-amber-500 bg-amber-500/50 text-inverse-fg',\n busy: 'border border-green-700',\n starred: 'border-2 border-dashed border-amber-500',\n};\n\n//\n// Range\n//\n\n/** Normalize an ordered pair of dates into a Range (start-of-day, from <= to). */\nconst makeRange = (a: Date, b: Date): Range => {\n const dayA = startOfDay(a);\n const dayB = startOfDay(b);\n return dayA <= dayB ? { from: dayA, to: dayB } : { from: dayB, to: dayA };\n};\n\n/** Inclusive day-level membership check. */\nconst isInRange = (date: Date, range: Range | undefined): boolean => {\n if (!range) {\n return false;\n }\n const day = startOfDay(date).getTime();\n return day >= range.from.getTime() && day <= range.to.getTime();\n};\n\n/** Resolve a DOM element back to the Date its cell represents. */\nconst cellDate = (el: Element | null): Date | undefined => {\n let current: Element | null = el;\n while (current && current !== document.body) {\n const iso = current.getAttribute?.('data-date');\n if (iso) {\n return new Date(iso);\n }\n current = current.parentElement;\n }\n return undefined;\n};\n\n//\n// Root\n//\n\ntype CalendarRootProps = PropsWithChildren<Partial<Pick<CalendarContextValue, 'weekStartsOn'>>>;\n\nconst CalendarRoot = forwardRef<CalendarController, CalendarRootProps>(\n ({ children, weekStartsOn = 1 }, forwardedRef) => {\n const event = useMemo(() => new Event<CalendarScrollEvent>(), []);\n const [selected, setSelected] = useState<Date | undefined>();\n const [index, setIndex] = useState<number | undefined>();\n const [range, setRange] = useState<Range | undefined>();\n const [pendingRange, setPendingRange] = useState<Range | undefined>();\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n scrollTo: (date: Date) => {\n event.emit({ type: 'scroll', date });\n },\n select: (date: Date) => {\n event.emit({ type: 'select', date });\n },\n }),\n [event],\n );\n\n return (\n <CalendarContextProvider\n weekStartsOn={weekStartsOn}\n event={event}\n index={index}\n setIndex={setIndex}\n selected={selected}\n setSelected={setSelected}\n range={range}\n setRange={setRange}\n pendingRange={pendingRange}\n setPendingRange={setPendingRange}\n >\n {children}\n </CalendarContextProvider>\n );\n },\n);\n\n//\n// Header\n//\n\nconst CALENDAR_TOOLBAR_NAME = 'CalendarHeader';\n\ntype CalendarToolbarProps = {};\n\nconst CalendarToolbar = composable<HTMLDivElement, CalendarToolbarProps>(({ classNames, ...props }, forwardedRef) => {\n const { t } = useTranslation(translationKey);\n const { weekStartsOn, event, index, selected } = useCalendarContext(CALENDAR_TOOLBAR_NAME);\n const top = useMemo(() => getDate(start, index ?? 0, 6, weekStartsOn), [index, weekStartsOn]);\n const today = useMemo(() => new Date(), []);\n\n const handleToday = useCallback(() => {\n event.emit({ type: 'scroll', date: today });\n }, [event, start, today]);\n\n return (\n <div\n {...composableProps(props, {\n role: 'none',\n classNames: ['shrink-0 grid! grid-cols-3 items-center bg-toolbar-surface', classNames],\n })}\n ref={forwardedRef}\n >\n <div className='flex justify-start'>\n <IconButton\n variant='ghost'\n icon='ph--calendar--regular'\n iconOnly\n classNames='aspect-square'\n label={t('today.button')}\n onClick={handleToday}\n />\n </div>\n <div className='flex justify-center p-2 text-description'>{format(selected ?? top, 'MMMM')}</div>\n <div className='flex justify-end p-2 text-description'>{(selected ?? top).getFullYear()}</div>\n </div>\n );\n});\n\nCalendarToolbar.displayName = CALENDAR_TOOLBAR_NAME;\n\n//\n// Grid\n//\n\nconst CALENDAR_GRID_NAME = 'CalendarGrid';\n\n/** Semantic kind of a {@link DateMarker}; the grid maps each kind to its own border treatment. */\nexport type DateMarkerTag = 'busy' | 'star';\n\n/**\n * A date (or inclusive date range) to mark on the grid. */\nexport type DateMarker = { startDate: Date; endDate?: Date; tag?: DateMarkerTag };\n\ntype CalendarGridProps = {\n rows?: number;\n /**\n * Dates to mark on the grid; each marked day gets a border keyed off its `tag` kind (defaults to `busy`).\n */\n dates?: DateMarker[];\n /**\n * Date the grid scrolls into view on mount, and whenever this value changes.\n * Defaults to today. Pass a stable (memoized) Date so the grid does not\n * re-scroll on every render.\n */\n initialDate?: Date;\n /**\n * Weeks of context kept above a date when scrolling it into view (on mount and via the controller's\n * `scrollTo`), so the date sits below the top edge rather than pinned to it. Defaults to 2.\n */\n scrollMargin?: number;\n /** Fired when a user selects a single date (click or arrow key). */\n onSelect?: (event: { date: Date }) => void;\n /**\n * Fired when a user commits a multi-day range, either by a drag gesture or\n * by shift+arrow extension. The range is normalized: `from <= to`, both at\n * start-of-day. Not fired for single-day selections (use {@link onSelect}).\n */\n onSelectRange?: (event: { range: Range }) => void;\n};\n\nconst CalendarGrid = composable<HTMLDivElement, CalendarGridProps>(\n (\n { classNames, rows, dates = [], initialDate, scrollMargin = 2, onSelect, onSelectRange, ...props },\n forwardedRef,\n ) => {\n const { weekStartsOn, event, setIndex, selected, setSelected, range, setRange, pendingRange, setPendingRange } =\n useCalendarContext(CALENDAR_GRID_NAME);\n const { ref: containerRef, width = 0, height = 0 } = useResizeDetector();\n const maxHeight = rows ? rows * size : undefined;\n const listRef = useRef<List>(null);\n const gridRef = useRef<HTMLDivElement>(null);\n const today = useMemo(() => new Date(), []);\n\n // Map each marked ISO day to its tag kind, expanding ranges. `star` wins over `busy` on the same\n // day so a starred event keeps its highlighted border.\n const dateMarkers = useMemo(() => {\n const markers = new Map<string, DateMarkerTag>();\n for (const { startDate, endDate, tag = 'busy' } of dates) {\n const end = endDate ? startOfDay(endDate) : startOfDay(startDate);\n for (let date = startOfDay(startDate); date <= end; date = addDays(date, 1)) {\n const iso = date.toISOString();\n if (markers.get(iso) !== 'star') {\n markers.set(iso, tag);\n }\n }\n }\n\n return markers;\n }, [dates]);\n\n const getMarker = useCallback(\n (date: Date): { tag: DateMarkerTag } | undefined => {\n const iso = startOfDay(date).toISOString();\n const tag = dateMarkers.get(iso);\n return tag ? { tag } : undefined;\n },\n [dateMarkers],\n );\n\n const [initialized, setInitialized] = useState(false);\n useEffect(() => {\n const index = getRowIndex(start, initialDate ?? today, weekStartsOn);\n // Keep `scrollMargin` weeks of context above the target row.\n listRef.current?.scrollToRow(Math.max(0, index - scrollMargin));\n }, [initialized, start, today, initialDate, weekStartsOn, scrollMargin]);\n\n useEffect(() => {\n return event.on((event) => {\n // `select` also sets the grid's selection (e.g. when the active event changes); the grid still\n // owns selection — a user click sets it locally and isn't overwritten until the next `select`.\n if (event.type === 'select') {\n setSelected(event.date);\n }\n const index = getRowIndex(start, event.date, weekStartsOn);\n listRef.current?.scrollToRow(Math.max(0, index - scrollMargin));\n });\n }, [event, start, weekStartsOn, scrollMargin, setSelected]);\n\n //\n // Selection refs.\n //\n // `anchorRef` is the immovable end of a range gesture (pointerdown date or\n // initial day when shift+arrow starts). `focusRef` is the moving end\n // (pointer-under-cursor during drag, or the cursor after each shift+arrow).\n // Both refs are kept in sync across mouse drag and keyboard nav so that\n // the user can fluidly mix gestures (e.g., drag a range, then shift+arrow\n // to fine-tune).\n //\n const anchorRef = useRef<Date | undefined>(undefined);\n const focusRef = useRef<Date | undefined>(undefined);\n const draggingRef = useRef(false);\n\n // Pointer tracking for edge-scroll while dragging.\n const pointerXRef = useRef<number>(0);\n const pointerYRef = useRef<number>(0);\n const scrollTopRef = useRef(0);\n const scrollRafRef = useRef<number | undefined>(undefined);\n\n // Scroll the target date into view only if it's outside the visible window.\n // Horizontal moves (left/right within the same week) leave the row index\n // unchanged and are a no-op.\n const scrollIntoView = useCallback(\n (date: Date) => {\n const targetRow = getRowIndex(start, date, weekStartsOn);\n const visibleHeight = maxHeight ?? height;\n if (!visibleHeight) {\n return;\n }\n // Rows fully inside the viewport. Use ceil/floor (not floor of scrollTop) so a partially\n // visible row at either edge counts as \"not fully visible\" even when scrollTop is not a\n // multiple of the row height (which it isn't after a bottom-aligned scroll).\n const firstFullyVisibleRow = Math.ceil(scrollTopRef.current / size);\n const lastFullyVisibleRow = Math.floor((scrollTopRef.current + visibleHeight) / size) - 1;\n if (targetRow < firstFullyVisibleRow) {\n // Align the top edge of the target row with the top edge of the viewport.\n listRef.current?.scrollToPosition(targetRow * size);\n } else if (targetRow > lastFullyVisibleRow) {\n // Align the bottom edge of the target row with the bottom edge of the viewport (using the\n // full visible height, not a row-rounded height, so the row sits flush against the edge).\n listRef.current?.scrollToPosition(Math.max(0, (targetRow + 1) * size - visibleHeight));\n }\n },\n [height, maxHeight, weekStartsOn],\n );\n\n const updateRangeFromAnchor = useCallback(\n (focus: Date, fireRange = false) => {\n const anchor = anchorRef.current;\n if (!anchor) {\n return;\n }\n focusRef.current = focus;\n if (isSameDay(anchor, focus)) {\n setRange(undefined);\n setSelected(anchor);\n } else {\n setSelected(undefined);\n const committed = makeRange(anchor, focus);\n setRange(committed);\n if (fireRange) {\n onSelectRange?.({ range: committed });\n }\n }\n },\n [onSelectRange, setRange, setSelected],\n );\n\n //\n // Drag-to-select range.\n //\n // `prevSelectedRef` snapshots the single-day selection that was active\n // when the gesture began. A click on the *same* already-selected day\n // toggles the selection off on pointerup; a click on any other day (or\n // when no day was selected) just sets the new selection.\n //\n const prevSelectedRef = useRef<Date | undefined>(undefined);\n\n const handleDayPointerDown = useCallback(\n (date: Date, ev: ReactPointerEvent<HTMLDivElement>) => {\n ev.preventDefault();\n prevSelectedRef.current = selected;\n anchorRef.current = date;\n focusRef.current = date;\n draggingRef.current = true;\n // Immediate visual feedback: render the single-select ring on the anchor day.\n setRange(undefined);\n setPendingRange(undefined);\n setSelected(date);\n // Focus the grid so subsequent keyboard nav works.\n gridRef.current?.focus({ preventScroll: true });\n },\n [selected, setPendingRange, setRange, setSelected],\n );\n\n const handleDayPointerEnter = useCallback(\n (date: Date) => {\n if (!draggingRef.current) {\n return;\n }\n const anchor = anchorRef.current;\n if (!anchor) {\n return;\n }\n focusRef.current = date;\n // Always render a pending range while dragging — even a single-day range\n // (when the pointer is on the anchor cell or returns to it). Otherwise\n // the anchor cell would appear empty mid-drag.\n setSelected(undefined);\n setPendingRange(makeRange(anchor, date));\n },\n [setPendingRange, setSelected],\n );\n\n const handleDayPointerUp = useCallback(\n (date: Date) => {\n const anchor = anchorRef.current;\n const wasDragging = draggingRef.current;\n draggingRef.current = false;\n setPendingRange(undefined);\n if (!wasDragging || !anchor) {\n return;\n }\n focusRef.current = date;\n if (isSameDay(anchor, date)) {\n // Single click — toggle off if clicking the previously-selected day,\n // otherwise set as selected. (pointerenter may have cleared the ring\n // mid-drag to show a 1-day pending-range fill; restore here.)\n if (prevSelectedRef.current && isSameDay(prevSelectedRef.current, date)) {\n setSelected(undefined);\n anchorRef.current = undefined;\n focusRef.current = undefined;\n return;\n }\n setSelected(anchor);\n onSelect?.({ date });\n return;\n }\n // Drag commit — `selected` was already cleared by pointerenter.\n const committed = makeRange(anchor, date);\n setRange(committed);\n onSelectRange?.({ range: committed });\n },\n [onSelect, onSelectRange, setPendingRange, setRange, setSelected],\n );\n\n // Cancel drag if the pointer is released outside the grid.\n useEffect(() => {\n const cancel = () => {\n if (draggingRef.current) {\n draggingRef.current = false;\n setPendingRange(undefined);\n }\n };\n window.addEventListener('pointerup', cancel);\n window.addEventListener('pointercancel', cancel);\n return () => {\n window.removeEventListener('pointerup', cancel);\n window.removeEventListener('pointercancel', cancel);\n };\n }, [setPendingRange]);\n\n //\n // Edge auto-scroll while dragging near top/bottom of the grid viewport.\n //\n const tickEdgeScroll = useCallback(() => {\n scrollRafRef.current = undefined;\n if (!draggingRef.current) {\n return;\n }\n const rect = containerRef.current?.getBoundingClientRect();\n if (!rect) {\n return;\n }\n const y = pointerYRef.current;\n let delta = 0;\n if (y < rect.top + EDGE_SCROLL_ZONE) {\n delta = -EDGE_SCROLL_MAX_SPEED * Math.min(1, Math.max(0, (rect.top + EDGE_SCROLL_ZONE - y) / EDGE_SCROLL_ZONE));\n } else if (y > rect.bottom - EDGE_SCROLL_ZONE) {\n delta =\n EDGE_SCROLL_MAX_SPEED * Math.min(1, Math.max(0, (y - (rect.bottom - EDGE_SCROLL_ZONE)) / EDGE_SCROLL_ZONE));\n }\n if (delta !== 0) {\n const newScroll = Math.max(0, scrollTopRef.current + delta);\n listRef.current?.scrollToPosition(newScroll);\n // After scroll, the cell under the (stationary) pointer changes.\n // Look up the new cell and update the pending range accordingly.\n const date = cellDate(document.elementFromPoint(pointerXRef.current, y));\n const anchor = anchorRef.current;\n if (date && anchor) {\n focusRef.current = date;\n if (isSameDay(anchor, date)) {\n setPendingRange(undefined);\n setSelected(anchor);\n } else {\n setSelected(undefined);\n setPendingRange(makeRange(anchor, date));\n }\n }\n scrollRafRef.current = requestAnimationFrame(tickEdgeScroll);\n }\n }, [containerRef, setPendingRange, setSelected]);\n\n useEffect(() => {\n const handleMove = (ev: PointerEvent) => {\n if (!draggingRef.current) {\n return;\n }\n pointerXRef.current = ev.clientX;\n pointerYRef.current = ev.clientY;\n if (scrollRafRef.current === undefined) {\n scrollRafRef.current = requestAnimationFrame(tickEdgeScroll);\n }\n };\n window.addEventListener('pointermove', handleMove);\n return () => {\n window.removeEventListener('pointermove', handleMove);\n if (scrollRafRef.current !== undefined) {\n cancelAnimationFrame(scrollRafRef.current);\n scrollRafRef.current = undefined;\n }\n };\n }, [tickEdgeScroll]);\n\n //\n // Keyboard nav: arrow keys move single selection; shift+arrow expands range.\n //\n const handleKeyDown = useCallback(\n (ev: ReactKeyboardEvent<HTMLDivElement>) => {\n let dx = 0;\n switch (ev.key) {\n case 'ArrowLeft':\n dx = -1;\n break;\n case 'ArrowRight':\n dx = 1;\n break;\n case 'ArrowUp':\n dx = -7;\n break;\n case 'ArrowDown':\n dx = 7;\n break;\n default:\n return;\n }\n ev.preventDefault();\n\n if (ev.shiftKey) {\n // Bootstrap anchor/focus from current state if needed.\n let anchor = anchorRef.current;\n let focus = focusRef.current;\n if (!anchor) {\n // No prior gesture — seed from current selected/range/today.\n if (selected) {\n anchor = startOfDay(selected);\n focus = anchor;\n } else if (range) {\n anchor = range.from;\n focus = range.to;\n } else {\n anchor = startOfDay(today);\n focus = anchor;\n }\n anchorRef.current = anchor;\n focusRef.current = focus;\n }\n const newFocus = addDays(focus ?? anchor, dx);\n updateRangeFromAnchor(newFocus, true);\n scrollIntoView(newFocus);\n } else {\n // Plain arrow — move single selection; clear any range gesture state.\n const current = selected ?? focusRef.current ?? anchorRef.current ?? today;\n const next = addDays(startOfDay(current), dx);\n anchorRef.current = next;\n focusRef.current = next;\n setRange(undefined);\n setPendingRange(undefined);\n setSelected(next);\n onSelect?.({ date: next });\n scrollIntoView(next);\n }\n },\n [onSelect, range, scrollIntoView, selected, setPendingRange, setRange, setSelected, today, updateRangeFromAnchor],\n );\n\n const activeRange = pendingRange ?? range;\n\n const handleScroll = useCallback<NonNullable<ListProps['onScroll']>>((info) => {\n scrollTopRef.current = info.scrollTop;\n setIndex(Math.round(info.scrollTop / size));\n }, []);\n\n const rowRenderer = useCallback<ListRowRenderer>(\n ({ key, index, style }) => {\n // Zebra-stripe alternating months with a subtle neutral overlay over the grid surface, so\n // the banding is independent of (and robust to) surface-token retuning.\n const getBgColor = (date: Date) => (date.getMonth() % 2 === 0 ? 'bg-group-surface' : 'bg-group-alt-surface');\n\n return (\n <div key={key} style={style} className='grid'>\n <div className='grid grid-cols-7 bg-input-surface' style={{ gridTemplateColumns: `repeat(7, ${size}px)` }}>\n {Array.from({ length: 7 }).map((_, i) => {\n const date = getDate(start, index, i, weekStartsOn);\n const marker = getMarker(date);\n const isToday = isSameDay(date, today);\n const isCurrent = isSameDay(date, selected);\n const dateClassNames = isToday\n ? DATE_CLASS_NAMES.today\n : marker?.tag === 'star'\n ? DATE_CLASS_NAMES.starred\n : marker\n ? DATE_CLASS_NAMES.busy\n : undefined;\n\n const inRange = isInRange(date, activeRange);\n\n return (\n <div\n key={i}\n data-date={startOfDay(date).toISOString()}\n className={mx('relative flex justify-center cursor-pointer select-none', getBgColor(date))}\n onPointerDown={(ev) => handleDayPointerDown(date, ev)}\n onPointerEnter={() => handleDayPointerEnter(date)}\n onPointerUp={() => handleDayPointerUp(date)}\n >\n {/* Selection range */}\n {inRange && <div className='absolute inset-0 bg-primary-500/20' />}\n {/* Month */}\n {!dateClassNames && date.getDate() === 1 && (\n <span className='absolute top-0 text-xs text-description'>{format(date, 'MMM')}</span>\n )}\n {/* Day + Marker */}\n <div\n className={mx(\n 'absolute inset-1 rounded-full flex justify-center items-center text-sm text-description',\n dateClassNames,\n )}\n >\n {date.getDate()}\n </div>\n {/* Current */}\n {isCurrent && <div className={mx('absolute inset-0.5 rounded-full', DATE_CLASS_NAMES.current)} />}\n </div>\n );\n })}\n </div>\n </div>\n );\n },\n [activeRange, handleDayPointerDown, handleDayPointerEnter, handleDayPointerUp, getMarker, selected, weekStartsOn],\n );\n\n return (\n <div\n {...composableProps(props, {\n role: 'none',\n classNames: ['flex flex-col h-full w-full justify-center overflow-hidden outline-hidden', classNames],\n })}\n ref={(node: HTMLDivElement | null) => {\n gridRef.current = node;\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else if (forwardedRef) {\n (forwardedRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n }}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n >\n {/* Day of week labels */}\n <div style={{ width: defaultWidth }}>\n <Weekdays weekStartsOn={weekStartsOn} columnWidth={size} />\n </div>\n\n {/* Grid */}\n <div className='flex flex-col h-full w-full justify-center overflow-hidden' ref={containerRef}>\n <List\n ref={listRef}\n role='none'\n className='scrollbar-none outline-hidden'\n width={width}\n height={maxHeight ?? height}\n rowCount={maxRows}\n rowHeight={size}\n rowRenderer={rowRenderer}\n scrollToAlignment='start'\n onScroll={handleScroll}\n onRowsRendered={() => setInitialized(true)}\n />\n </div>\n </div>\n );\n },\n);\n\nCalendarGrid.displayName = CALENDAR_GRID_NAME;\n\n//\n// Calendar\n//\n\nexport const Calendar = {\n Root: CalendarRoot,\n Toolbar: CalendarToolbar,\n Grid: CalendarGrid,\n Week: CalendarWeek,\n};\n\nexport type {\n CalendarController,\n CalendarEvent,\n CalendarGridProps,\n CalendarRootProps,\n CalendarToolbarProps,\n CalendarWeekProps,\n Range,\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { createContext } from '@radix-ui/react-context';\nimport { type Day } from 'date-fns';\nimport { type Dispatch, type SetStateAction } from 'react';\n\nimport { type Event } from '@dxos/async';\n\n//\n// Range\n//\n\n/**\n * Inclusive date range. `from <= to`. Both endpoints are anchored at the\n * start of their day; callers should not rely on time-of-day precision.\n */\nexport type Range = {\n from: Date;\n to: Date;\n};\n\n//\n// Controller\n//\n\nexport type CalendarController = {\n /** Bring a date into view without changing the selection. */\n scrollTo: (date: Date) => void;\n /** Set the grid's selected day (and scroll it into view) — e.g. when the active event changes. */\n select: (date: Date) => void;\n};\n\n//\n// Context\n//\n\n/** Imperative grid signal: `scroll` brings a date into view; `select` also sets it as the selected day. */\nexport type CalendarScrollEvent = {\n type: 'scroll' | 'select';\n date: Date;\n};\n\nexport type CalendarContextValue = {\n weekStartsOn: Day;\n event: Event<CalendarScrollEvent>;\n index: number | undefined;\n setIndex: Dispatch<SetStateAction<number | undefined>>;\n selected: Date | undefined;\n setSelected: Dispatch<SetStateAction<Date | undefined>>;\n /** Committed date range, set by the most recent drag or shift+arrow selection. */\n range: Range | undefined;\n setRange: Dispatch<SetStateAction<Range | undefined>>;\n /** Live drag preview; non-undefined only while the user is dragging. */\n pendingRange: Range | undefined;\n setPendingRange: Dispatch<SetStateAction<Range | undefined>>;\n};\n\nexport const [CalendarContextProvider, useCalendarContext] = createContext<CalendarContextValue>('Calendar');\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Day, differenceInCalendarDays, startOfDay } from 'date-fns';\n\n/**\n * Fixed origin (row 0, column 0 anchor) for the infinite month grid. All row-index math is relative\n * to this epoch, so any view that reports a row index to the shared context must use the same value.\n */\nexport const gridEpoch = new Date('1970-01-01');\n\nexport const getDate = (start: Date, weekNumber: number, dayOfWeek: number, weekStartsOn: Day): Date => {\n const result = new Date(start);\n const startDayOfWeek = start.getDay(); // 0 = Sunday, 1 = Monday, etc.\n const adjustedStartDay = (startDayOfWeek === 0 ? 7 : startDayOfWeek) - weekStartsOn; // Adjust for weekStartsOn.\n result.setDate(start.getDate() - adjustedStartDay + weekNumber * 7 + dayOfWeek);\n return result;\n};\n\n/**\n * Inverse of {@link getDate}: returns the row index for a given date, matching\n * the grid layout (which respects `weekStartsOn`).\n *\n * Uses `differenceInCalendarDays` (DST-safe) — naive ms subtraction silently\n * loses an hour each DST transition, which accumulates over decades and\n * eventually shifts the row boundary by one day. `differenceInWeeks` is also\n * unsuitable because it computes raw 7-day chunks anchored at the start\n * date's weekday rather than the grid's `weekStartsOn` column.\n */\nexport const getRowIndex = (start: Date, date: Date, weekStartsOn: Day): number => {\n const startDayOfWeek = start.getDay();\n const adjustedStartDay = (startDayOfWeek === 0 ? 7 : startDayOfWeek) - weekStartsOn;\n const row0Start = new Date(start);\n row0Start.setDate(start.getDate() - adjustedStartDay);\n return Math.floor(differenceInCalendarDays(date, row0Start) / 7);\n};\n\nexport const isSameDay = (date1: Date, date2: Date | undefined): boolean => {\n return (\n !!date2 &&\n date1.getFullYear() === date2.getFullYear() &&\n date1.getMonth() === date2.getMonth() &&\n date1.getDate() === date2.getDate()\n );\n};\n\n//\n// Time-grid (week view) helpers.\n//\n\nexport const MINUTES_PER_DAY = 24 * 60;\n\n/** Default snap granularity for create/move/resize gestures, in minutes. */\nexport const SNAP_MINUTES = 15;\n\n/** Minutes elapsed since the start of `date`'s day (0 .. 1439). */\nexport const minutesOfDay = (date: Date): number => (date.getTime() - startOfDay(date).getTime()) / 60_000;\n\n/** Return a new Date on the same calendar day as `date`, at `minutes` past midnight. */\nexport const setMinutesOfDay = (date: Date, minutes: number): Date =>\n new Date(startOfDay(date).getTime() + minutes * 60_000);\n\n/** Convert a vertical offset (px from the top of the day column) into minutes past midnight. */\nexport const yToMinutes = (y: number, hourHeight: number): number => (y / hourHeight) * 60;\n\n/** Convert minutes past midnight into a vertical offset (px from the top of the day column). */\nexport const minutesToY = (minutes: number, hourHeight: number): number => (minutes / 60) * hourHeight;\n\n/** Round `minutes` to the nearest `step`, clamped to a single day. */\nexport const snapMinutes = (minutes: number, step: number = SNAP_MINUTES): number => {\n const snapped = Math.round(minutes / step) * step;\n return Math.max(0, Math.min(MINUTES_PER_DAY, snapped));\n};\n\n/**\n * Side-by-side layout slot for an overlapping-event cluster: the event occupies\n * `1 / columnCount` of the day column width, offset by `columnIndex` columns.\n */\nexport type EventLayout = { columnIndex: number; columnCount: number };\n\n/**\n * Compute side-by-side columns for events within a single day. Events that overlap in\n * time (transitively) form a cluster and split the column width evenly; non-overlapping\n * events each get the full width. Input order is irrelevant; results are keyed by index\n * into `events`.\n */\nexport const layoutDayEvents = <T extends { start: Date; end: Date }>(events: T[]): Map<number, EventLayout> => {\n const layout = new Map<number, EventLayout>();\n // Preserve original indices so callers can map results back to their event list.\n const ordered = events\n .map((event, index) => ({ index, start: event.start.getTime(), end: event.end.getTime() }))\n .sort((a, b) => a.start - b.start);\n\n let cluster: { index: number; start: number; end: number }[] = [];\n let clusterMax = -Infinity;\n\n const flush = () => {\n if (cluster.length === 0) {\n return;\n }\n // Greedy column packing: place each event in the first column whose previous event has ended.\n const columnEnds: number[] = [];\n const assigned = cluster.map(({ index, start, end }) => {\n let column = columnEnds.findIndex((columnEnd) => columnEnd <= start);\n if (column === -1) {\n column = columnEnds.length;\n }\n columnEnds[column] = end;\n return { index, column };\n });\n const columnCount = columnEnds.length;\n for (const { index, column } of assigned) {\n layout.set(index, { columnIndex: column, columnCount });\n }\n cluster = [];\n clusterMax = -Infinity;\n };\n\n for (const entry of ordered) {\n if (cluster.length > 0 && entry.start >= clusterMax) {\n // No overlap with the running cluster — close it out before starting fresh.\n flush();\n }\n cluster.push(entry);\n clusterMax = Math.max(clusterMax, entry.end);\n }\n flush();\n\n return layout;\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { addDays, startOfDay, startOfWeek } from 'date-fns';\nimport React, {\n type PointerEvent as ReactPointerEvent,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { composable, composableProps } from '@dxos/react-ui';\nimport { mx } from '@dxos/ui-theme';\n\nimport { useCalendarContext } from './context';\nimport {\n MINUTES_PER_DAY,\n SNAP_MINUTES,\n getRowIndex,\n gridEpoch,\n isSameDay,\n layoutDayEvents,\n minutesOfDay,\n minutesToY,\n setMinutesOfDay,\n snapMinutes,\n yToMinutes,\n} from './util';\nimport { Weekdays } from './Weekdays';\n\nconst CALENDAR_WEEK_NAME = 'CalendarWeek';\n\nconst HOUR_HEIGHT = 48; // px per hour.\nconst GUTTER_WIDTH = 56; // px, hour-label column.\nconst RESIZE_HANDLE = 6; // px, top/bottom hit zone for resizing.\nconst MIN_DURATION = SNAP_MINUTES; // Minimum event duration (minutes).\nconst INITIAL_HOUR = 8; // Hour scrolled into view on mount.\n\n/** An event rendered on the week's time grid. Times are interpreted in local time. */\nexport type CalendarEvent = {\n id: string;\n title?: string;\n start: Date;\n end: Date;\n};\n\ntype CalendarWeekProps = {\n /** Any date within the week to display; the week is derived via `weekStartsOn`. Defaults to today. */\n date?: Date;\n events?: CalendarEvent[];\n /** Fired when the user drags out a new time slot on an empty part of a day column. */\n onEventCreate?: (event: { start: Date; end: Date }) => void;\n /** Fired when an event is moved (duration preserved) or resized (one edge changed). */\n onEventUpdate?: (event: { id: string; start: Date; end: Date }) => void;\n};\n\ntype GestureKind = 'create' | 'move' | 'resize-start' | 'resize-end';\n\n// A live drag gesture. `anchorMinutes` is the fixed edge for create/resize; `grabOffset` is the\n// pointer's offset (minutes) into the event when moving, so the event doesn't jump under the cursor.\ntype Gesture = {\n kind: GestureKind;\n day: Date;\n eventId?: string;\n anchorMinutes: number;\n grabOffset: number;\n durationMinutes: number;\n};\n\n// A pending edit (the gesture's live result) overlaid on the source events while dragging.\ntype Draft = { start: Date; end: Date; eventId?: string };\n\nconst CalendarWeek = composable<HTMLDivElement, CalendarWeekProps>(\n ({ classNames, date, events = [], onEventCreate, onEventUpdate, ...props }, forwardedRef) => {\n const { weekStartsOn, event: scrollEvent, setIndex } = useCalendarContext(CALENDAR_WEEK_NAME);\n const today = useMemo(() => new Date(), []);\n\n // Anchor of the displayed week. Seeded from the `date` prop and re-synced when it changes, but also\n // driven by the shared scroll/select signal (e.g. the Toolbar's Today button, controller.scrollTo).\n const [viewDate, setViewDate] = useState<Date>(() => date ?? today);\n useEffect(() => {\n if (date) {\n setViewDate(date);\n }\n }, [date]);\n useEffect(() => {\n return scrollEvent.on(({ date }) => setViewDate(date));\n }, [scrollEvent]);\n\n const weekDays = useMemo(() => {\n const weekStart = startOfWeek(viewDate, { weekStartsOn });\n return Array.from({ length: 7 }, (_, index) => startOfDay(addDays(weekStart, index)));\n }, [viewDate, weekStartsOn]);\n\n // Report the displayed week to the shared context so the Toolbar's month/year label tracks it.\n useEffect(() => {\n setIndex(getRowIndex(gridEpoch, weekDays[0], weekStartsOn));\n }, [weekDays, weekStartsOn, setIndex]);\n\n // Index events by day so each column lays out independently.\n const eventsByDay = useMemo(() => {\n const byDay = weekDays.map(() => [] as CalendarEvent[]);\n for (const event of events) {\n const dayIndex = weekDays.findIndex((day) => isSameDay(day, event.start));\n if (dayIndex >= 0) {\n byDay[dayIndex].push(event);\n }\n }\n return byDay;\n }, [events, weekDays]);\n\n const scrollRef = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n scrollRef.current?.scrollTo({ top: minutesToY(INITIAL_HOUR * 60, HOUR_HEIGHT) });\n }, []);\n\n //\n // Drag gesture: create / move / resize. All vertical-only, snapped to `SNAP_MINUTES`.\n //\n const gestureRef = useRef<Gesture | undefined>(undefined);\n const columnsRef = useRef<(HTMLDivElement | null)[]>([]);\n const [draft, setDraft] = useState<Draft | undefined>(undefined);\n\n // All day columns share the same vertical geometry, so any column resolves clientY → minutes.\n const pointerMinutes = useCallback((clientY: number): number => {\n const rect = columnsRef.current.find(Boolean)?.getBoundingClientRect();\n if (!rect) {\n return 0;\n }\n return Math.max(0, Math.min(MINUTES_PER_DAY, yToMinutes(clientY - rect.top, HOUR_HEIGHT)));\n }, []);\n\n // The day column under clientX (used to move events across days); undefined when outside all columns.\n const dayFromX = useCallback(\n (clientX: number): Date | undefined => {\n const index = columnsRef.current.findIndex((node) => {\n const rect = node?.getBoundingClientRect();\n return rect && clientX >= rect.left && clientX < rect.right;\n });\n return index >= 0 ? weekDays[index] : undefined;\n },\n [weekDays],\n );\n\n const applyGesture = useCallback(\n (clientX: number, clientY: number): Draft | undefined => {\n const gesture = gestureRef.current;\n if (!gesture) {\n return undefined;\n }\n const { kind, day, eventId, anchorMinutes, grabOffset, durationMinutes } = gesture;\n const raw = pointerMinutes(clientY);\n switch (kind) {\n case 'create': {\n const focus = snapMinutes(raw);\n const from = Math.min(anchorMinutes, focus);\n const to = Math.max(anchorMinutes, focus);\n const end = Math.max(to, from + MIN_DURATION);\n return { eventId, start: setMinutesOfDay(day, from), end: setMinutesOfDay(day, end) };\n }\n case 'move': {\n // Moving may cross day columns; the time-of-day and duration are preserved.\n const targetDay = dayFromX(clientX) ?? day;\n let start = snapMinutes(raw - grabOffset);\n start = Math.max(0, Math.min(MINUTES_PER_DAY - durationMinutes, start));\n return {\n eventId,\n start: setMinutesOfDay(targetDay, start),\n end: setMinutesOfDay(targetDay, start + durationMinutes),\n };\n }\n case 'resize-start': {\n const start = Math.min(snapMinutes(raw), anchorMinutes - MIN_DURATION);\n return { eventId, start: setMinutesOfDay(day, start), end: setMinutesOfDay(day, anchorMinutes) };\n }\n case 'resize-end': {\n const end = Math.max(snapMinutes(raw), anchorMinutes + MIN_DURATION);\n return { eventId, start: setMinutesOfDay(day, anchorMinutes), end: setMinutesOfDay(day, end) };\n }\n }\n },\n [dayFromX, pointerMinutes],\n );\n\n // Latest commit callbacks, read at pointerup so the listeners attached on pointerdown never go stale.\n const callbacksRef = useRef({ onEventCreate, onEventUpdate });\n callbacksRef.current = { onEventCreate, onEventUpdate };\n\n // Removes the active gesture's window listeners; replaced on each `beginGesture` and called on unmount.\n const detachRef = useRef<() => void>(() => {});\n\n const beginGesture = useCallback(\n (gesture: Gesture, ev: ReactPointerEvent) => {\n ev.preventDefault();\n ev.stopPropagation();\n gestureRef.current = gesture;\n setDraft(applyGesture(ev.clientX, ev.clientY));\n\n // Attach listeners imperatively (not via effect) so no pointer event is missed in the window\n // between pointerdown and the next render.\n const handleMove = (moveEv: PointerEvent) => {\n if (gestureRef.current) {\n setDraft(applyGesture(moveEv.clientX, moveEv.clientY));\n }\n };\n\n const handleUp = (upEv: PointerEvent) => {\n const active = gestureRef.current;\n const result = applyGesture(upEv.clientX, upEv.clientY);\n detachRef.current();\n gestureRef.current = undefined;\n setDraft(undefined);\n if (active && result) {\n if (active.kind === 'create') {\n callbacksRef.current.onEventCreate?.({ start: result.start, end: result.end });\n } else if (result.eventId) {\n callbacksRef.current.onEventUpdate?.({ id: result.eventId, start: result.start, end: result.end });\n }\n }\n };\n\n detachRef.current = () => {\n window.removeEventListener('pointermove', handleMove);\n window.removeEventListener('pointerup', handleUp);\n window.removeEventListener('pointercancel', handleUp);\n };\n window.addEventListener('pointermove', handleMove);\n window.addEventListener('pointerup', handleUp);\n window.addEventListener('pointercancel', handleUp);\n },\n [applyGesture],\n );\n\n useEffect(() => () => detachRef.current(), []);\n\n const handleColumnPointerDown = useCallback(\n (day: Date, event: ReactPointerEvent<HTMLDivElement>) => {\n // Only the column background starts a create gesture; events stop propagation.\n const rect = event.currentTarget.getBoundingClientRect();\n const anchor = snapMinutes(yToMinutes(event.clientY - rect.top, HOUR_HEIGHT));\n beginGesture({ kind: 'create', day, anchorMinutes: anchor, grabOffset: 0, durationMinutes: 0 }, event);\n },\n [beginGesture],\n );\n\n // Render an event block bound to the gesture handlers for a given day column. `day` is the column\n // the block currently sits in (its original day, or the target day while being dragged across).\n const renderEvent = useCallback(\n (event: CalendarEvent, day: Date, start: Date, end: Date, columnIndex: number, columnCount: number) => (\n <EventBlock\n key={event.id}\n event={event}\n start={start}\n end={end}\n columnIndex={columnIndex}\n columnCount={columnCount}\n onMoveStart={(ev) =>\n beginGesture(\n {\n kind: 'move',\n day,\n eventId: event.id,\n anchorMinutes: 0,\n grabOffset: pointerMinutes(ev.clientY) - minutesOfDay(event.start),\n durationMinutes: minutesOfDay(event.end) - minutesOfDay(event.start),\n },\n ev,\n )\n }\n onResizeStart={(ev) =>\n beginGesture(\n {\n kind: 'resize-start',\n day,\n eventId: event.id,\n anchorMinutes: minutesOfDay(event.end),\n grabOffset: 0,\n durationMinutes: 0,\n },\n ev,\n )\n }\n onResizeEnd={(ev) =>\n beginGesture(\n {\n kind: 'resize-end',\n day,\n eventId: event.id,\n anchorMinutes: minutesOfDay(event.start),\n grabOffset: 0,\n durationMinutes: 0,\n },\n ev,\n )\n }\n />\n ),\n [beginGesture, pointerMinutes],\n );\n\n // The event under an active move, resolved once so the origin column can drop it and the target\n // column can render it while the pointer is over a different day.\n const draggedEvent = draft?.eventId ? events.find((event) => event.id === draft.eventId) : undefined;\n\n return (\n <div\n {...composableProps(props, {\n classNames: ['flex flex-col h-full w-full overflow-hidden outline-hidden', classNames],\n })}\n ref={forwardedRef}\n >\n <Weekdays weekStartsOn={weekStartsOn} gutter={GUTTER_WIDTH} dates={weekDays} />\n\n <div ref={scrollRef} className='flex-1 overflow-y-auto _scrollbar-thin'>\n <div\n className='grid relative'\n style={{\n height: minutesToY(MINUTES_PER_DAY, HOUR_HEIGHT),\n gridTemplateColumns: `${GUTTER_WIDTH}px repeat(7, 1fr)`,\n }}\n >\n {/* Hour gutter + faint hour lines spanning all columns. */}\n <div className='relative'>\n {Array.from({ length: 24 }, (_, hour) => (\n <div\n key={hour}\n className='absolute right-1 -translate-y-1/2 text-xs text-description tabular-nums'\n style={{ top: minutesToY(hour * 60, HOUR_HEIGHT) }}\n >\n {hour === 0 ? '' : `${hour.toString().padStart(2, '0')}:00`}\n </div>\n ))}\n </div>\n\n {weekDays.map((day, dayIndex) => {\n const dayEvents = eventsByDay[dayIndex];\n const layout = layoutDayEvents(dayEvents);\n const isToday = isSameDay(day, today);\n // The draft when it currently belongs to this column (origin for resize, target for a cross-day move).\n const draftHere = draft && isSameDay(day, draft.start) ? draft : undefined;\n return (\n <div\n key={day.toISOString()}\n ref={(node) => {\n columnsRef.current[dayIndex] = node;\n }}\n data-date={day.toISOString()}\n className={mx(\n 'relative border-l border-separator cursor-cell select-none',\n dayIndex === 6 && 'border-r',\n isToday && 'bg-primary-500/5',\n )}\n onPointerDown={(ev) => handleColumnPointerDown(day, ev)}\n >\n {/* Faint hour lines. */}\n {Array.from({ length: 24 }, (_, hour) => (\n <div\n key={hour}\n className='absolute inset-x-0 border-t border-separator/60'\n style={{ top: minutesToY(hour * 60, HOUR_HEIGHT) }}\n />\n ))}\n\n {/* Events. */}\n {dayEvents.map((event, index) => {\n const slot = layout.get(index) ?? { columnIndex: 0, columnCount: 1 };\n const editing = draft && draft.eventId === event.id ? draft : undefined;\n // Dropped from this column while the move drags it onto another day.\n if (editing && !isSameDay(editing.start, day)) {\n return null;\n }\n const start = editing ? editing.start : event.start;\n const end = editing ? editing.end : event.end;\n return renderEvent(event, day, start, end, slot.columnIndex, slot.columnCount);\n })}\n\n {/* Event being dragged in from another day — rendered full-width above this column's events. */}\n {draftHere &&\n draggedEvent &&\n !dayEvents.some((event) => event.id === draggedEvent.id) &&\n renderEvent(draggedEvent, day, draftHere.start, draftHere.end, 0, 1)}\n\n {/* Pending create rectangle. */}\n {draftHere && !draftHere.eventId && <PendingBlock start={draftHere.start} end={draftHere.end} />}\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n },\n);\n\nCalendarWeek.displayName = CALENDAR_WEEK_NAME;\n\n//\n// EventBlock\n//\n\nconst formatTime = (date: Date): string => {\n const minutes = minutesOfDay(date);\n const hour = Math.floor(minutes / 60);\n const minute = Math.round(minutes % 60);\n return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;\n};\n\ntype EventBlockProps = {\n event: CalendarEvent;\n start: Date;\n end: Date;\n columnIndex: number;\n columnCount: number;\n onMoveStart: (ev: ReactPointerEvent<HTMLDivElement>) => void;\n onResizeStart: (ev: ReactPointerEvent<HTMLDivElement>) => void;\n onResizeEnd: (ev: ReactPointerEvent<HTMLDivElement>) => void;\n};\n\nconst EventBlock = ({\n event,\n start,\n end,\n columnIndex,\n columnCount,\n onMoveStart,\n onResizeStart,\n onResizeEnd,\n}: EventBlockProps) => {\n const top = minutesToY(minutesOfDay(start), HOUR_HEIGHT);\n const height = Math.max(minutesToY(minutesOfDay(end) - minutesOfDay(start), HOUR_HEIGHT), RESIZE_HANDLE * 2);\n // Leave a 1px gutter between side-by-side columns.\n const widthPct = 100 / columnCount;\n return (\n <div\n className='absolute rounded-sm bg-primary-500/80 text-inverse-fg overflow-hidden cursor-move shadow-sm'\n style={{\n top,\n height,\n left: `calc(${columnIndex * widthPct}% + 1px)`,\n width: `calc(${widthPct}% - 2px)`,\n }}\n onPointerDown={onMoveStart}\n >\n <div\n className='absolute inset-x-0 top-0 cursor-ns-resize'\n style={{ height: RESIZE_HANDLE }}\n onPointerDown={onResizeStart}\n />\n <div className='px-1 py-0.5 text-xs leading-tight'>\n <div className='font-medium truncate'>{event.title ?? '(untitled)'}</div>\n {/* <div className='tabular-nums opacity-80'>\n {formatTime(start)}–{formatTime(end)}\n </div> */}\n </div>\n <div\n className='absolute inset-x-0 bottom-0 cursor-ns-resize'\n style={{ height: RESIZE_HANDLE }}\n onPointerDown={onResizeEnd}\n />\n </div>\n );\n};\n\n//\n// PendingBlock\n//\n\nconst PendingBlock = ({ start, end }: { start: Date; end: Date }) => {\n const top = minutesToY(minutesOfDay(start), HOUR_HEIGHT);\n const height = minutesToY(minutesOfDay(end) - minutesOfDay(start), HOUR_HEIGHT);\n return (\n <div\n className='absolute inset-x-0 rounded bg-primary-500/40 border border-primary-500 pointer-events-none'\n style={{ top, height }}\n >\n <div className='px-1 py-0.5 text-xs tabular-nums text-inverse-fg'>\n {formatTime(start)}–{formatTime(end)}\n </div>\n </div>\n );\n};\n\nexport { CalendarWeek };\nexport type { CalendarWeekProps };\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Day, addDays, format, startOfWeek } from 'date-fns';\nimport React, { useMemo } from 'react';\n\nimport { mx } from '@dxos/ui-theme';\n\nimport { isSameDay } from './util';\n\nexport type WeekdaysProps = {\n weekStartsOn: Day;\n /** Fixed column width in px (e.g. the month grid's cells); omit for flexible `1fr` columns. */\n columnWidth?: number;\n /** Leading spacer width in px, aligning the labels over a body gutter (e.g. the week view's hour column). */\n gutter?: number;\n /** When provided, the matching day-of-month number is rendered beneath each label (week view). */\n dates?: Date[];\n};\n\n/**\n * Shared day-of-week header for the calendar views. Renders seven short day labels\n * ordered by `weekStartsOn`; when `dates` is supplied it also shows the day number and\n * highlights today.\n */\nexport const Weekdays = ({ weekStartsOn, columnWidth, gutter, dates }: WeekdaysProps) => {\n const labels = useMemo(() => {\n const weekStart = startOfWeek(new Date(), { weekStartsOn });\n return Array.from({ length: 7 }, (_, index) => format(addDays(weekStart, index), 'EEE'));\n }, [weekStartsOn]);\n\n const today = useMemo(() => new Date(), []);\n const columnTemplate = columnWidth ? `repeat(7, ${columnWidth}px)` : 'repeat(7, 1fr)';\n\n return (\n <div\n className='grid w-full shrink-0'\n style={{ gridTemplateColumns: gutter ? `${gutter}px ${columnTemplate}` : columnTemplate }}\n >\n {gutter != null && <div aria-hidden />}\n {labels.map((label, index) => {\n const date = dates?.[index];\n const isToday = !!date && isSameDay(date, today);\n return (\n <div\n key={index}\n className={mx('flex flex-col items-center p-2 text-sm font-thin', isToday && 'text-accent-text')}\n >\n <span>{label}</span>\n {date && <span className='text-lg font-normal tabular-nums'>{date.getDate()}</span>}\n </div>\n );\n })}\n </div>\n );\n};\n"],
4
+ "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { addDays, format, startOfDay } from 'date-fns';\nimport React, {\n type PropsWithChildren,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useResizeDetector } from 'react-resize-detector';\nimport { List, type ListProps, type ListRowRenderer } from 'react-virtualized';\n\nimport { Event } from '@dxos/async';\nimport { IconButton, useTranslation } from '@dxos/react-ui';\nimport { composable, composableProps } from '@dxos/react-ui';\nimport { mx } from '@dxos/ui-theme';\n\nimport { translationKey } from '#translations';\n\nimport {\n CalendarContextProvider,\n type CalendarContextValue,\n type CalendarController,\n type CalendarScrollEvent,\n type Range,\n useCalendarContext,\n} from './context';\nimport { getDate, getRowIndex, gridEpoch, isSameDay } from './util';\nimport { type CalendarEvent, CalendarWeek, type CalendarWeekProps } from './Week';\nimport { Weekdays } from './Weekdays';\n\nconst maxRows = 50 * 100;\nconst start = gridEpoch;\nconst size = 40;\nconst defaultWidth = 7 * size;\n\n// Auto-scroll while dragging near a vertical edge.\nconst EDGE_SCROLL_ZONE = 32; // px\nconst EDGE_SCROLL_MAX_SPEED = 12; // px per frame\n\nconst DATE_CLASS_NAMES = {\n current: 'ring-2 ring-primary-500',\n today: 'border-2 border-amber-500 bg-amber-500/50 text-inverse-fg',\n busy: 'border border-green-700',\n starred: 'border-2 border-dashed border-amber-500',\n};\n\n//\n// Range\n//\n\n/** Normalize an ordered pair of dates into a Range (start-of-day, from <= to). */\nconst makeRange = (a: Date, b: Date): Range => {\n const dayA = startOfDay(a);\n const dayB = startOfDay(b);\n return dayA <= dayB ? { from: dayA, to: dayB } : { from: dayB, to: dayA };\n};\n\n/** Inclusive day-level membership check. */\nconst isInRange = (date: Date, range: Range | undefined): boolean => {\n if (!range) {\n return false;\n }\n const day = startOfDay(date).getTime();\n return day >= range.from.getTime() && day <= range.to.getTime();\n};\n\n/** Resolve a DOM element back to the Date its cell represents. */\nconst cellDate = (el: Element | null): Date | undefined => {\n let current: Element | null = el;\n while (current && current !== document.body) {\n const iso = current.getAttribute?.('data-date');\n if (iso) {\n return new Date(iso);\n }\n current = current.parentElement;\n }\n return undefined;\n};\n\n//\n// Root\n//\n\ntype CalendarRootProps = PropsWithChildren<Partial<Pick<CalendarContextValue, 'weekStartsOn'>>>;\n\nconst CalendarRoot = forwardRef<CalendarController, CalendarRootProps>(\n ({ children, weekStartsOn = 1 }, forwardedRef) => {\n const event = useMemo(() => new Event<CalendarScrollEvent>(), []);\n const [selected, setSelected] = useState<Date | undefined>();\n const [index, setIndex] = useState<number | undefined>();\n const [range, setRange] = useState<Range | undefined>();\n const [pendingRange, setPendingRange] = useState<Range | undefined>();\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n scrollTo: (date: Date) => {\n event.emit({ type: 'scroll', date });\n },\n select: (date: Date) => {\n event.emit({ type: 'select', date });\n },\n }),\n [event],\n );\n\n return (\n <CalendarContextProvider\n weekStartsOn={weekStartsOn}\n event={event}\n index={index}\n setIndex={setIndex}\n selected={selected}\n setSelected={setSelected}\n range={range}\n setRange={setRange}\n pendingRange={pendingRange}\n setPendingRange={setPendingRange}\n >\n {children}\n </CalendarContextProvider>\n );\n },\n);\n\n//\n// Header\n//\n\nconst CALENDAR_TOOLBAR_NAME = 'CalendarHeader';\n\ntype CalendarToolbarProps = {};\n\nconst CalendarToolbar = composable<HTMLDivElement, CalendarToolbarProps>(({ classNames, ...props }, forwardedRef) => {\n const { t } = useTranslation(translationKey);\n const { weekStartsOn, event, index, selected } = useCalendarContext(CALENDAR_TOOLBAR_NAME);\n const top = useMemo(() => getDate(start, index ?? 0, 6, weekStartsOn), [index, weekStartsOn]);\n const today = useMemo(() => new Date(), []);\n\n const handleToday = useCallback(() => {\n event.emit({ type: 'scroll', date: today });\n }, [event, start, today]);\n\n return (\n <div\n {...composableProps(props, {\n role: 'none',\n classNames: ['shrink-0 grid! grid-cols-3 items-center bg-toolbar-surface', classNames],\n })}\n ref={forwardedRef}\n >\n <div className='flex justify-start'>\n <IconButton\n variant='ghost'\n icon='ph--calendar--regular'\n iconOnly\n classNames='aspect-square'\n label={t('today.button')}\n onClick={handleToday}\n />\n </div>\n <div className='flex justify-center p-2 text-description'>{format(selected ?? top, 'MMMM')}</div>\n <div className='flex justify-end p-2 text-description'>{(selected ?? top).getFullYear()}</div>\n </div>\n );\n});\n\nCalendarToolbar.displayName = CALENDAR_TOOLBAR_NAME;\n\n//\n// Grid\n//\n\nconst CALENDAR_GRID_NAME = 'CalendarGrid';\n\n/** Semantic kind of a {@link DateMarker}; the grid maps each kind to its own border treatment. */\nexport type DateMarkerTag = 'busy' | 'star';\n\n/**\n * A date (or inclusive date range) to mark on the grid. */\nexport type DateMarker = { startDate: Date; endDate?: Date; tag?: DateMarkerTag };\n\ntype CalendarGridProps = {\n rows?: number;\n /**\n * Dates to mark on the grid; each marked day gets a border keyed off its `tag` kind (defaults to `busy`).\n */\n dates?: DateMarker[];\n /**\n * Date the grid scrolls into view on mount, and whenever this value changes.\n * Defaults to today. Pass a stable (memoized) Date so the grid does not\n * re-scroll on every render.\n */\n initialDate?: Date;\n /**\n * Weeks of context kept above a date when scrolling it into view (on mount and via the controller's\n * `scrollTo`), so the date sits below the top edge rather than pinned to it. Defaults to 2.\n */\n scrollMargin?: number;\n /** Fired when a user selects a single date (click or arrow key). */\n onSelect?: (event: { date: Date }) => void;\n /**\n * Fired when a user commits a multi-day range, either by a drag gesture or\n * by shift+arrow extension. The range is normalized: `from <= to`, both at\n * start-of-day. Not fired for single-day selections (use {@link onSelect}).\n */\n onSelectRange?: (event: { range: Range }) => void;\n};\n\nconst CalendarGrid = composable<HTMLDivElement, CalendarGridProps>(\n (\n { classNames, rows, dates = [], initialDate, scrollMargin = 2, onSelect, onSelectRange, ...props },\n forwardedRef,\n ) => {\n const { weekStartsOn, event, setIndex, selected, setSelected, range, setRange, pendingRange, setPendingRange } =\n useCalendarContext(CALENDAR_GRID_NAME);\n const { ref: containerRef, width = 0, height = 0 } = useResizeDetector();\n const maxHeight = rows ? rows * size : undefined;\n const listRef = useRef<List>(null);\n const gridRef = useRef<HTMLDivElement>(null);\n const today = useMemo(() => new Date(), []);\n\n // Map each marked ISO day to its tag kind, expanding ranges. `star` wins over `busy` on the same\n // day so a starred event keeps its highlighted border.\n const dateMarkers = useMemo(() => {\n const markers = new Map<string, DateMarkerTag>();\n for (const { startDate, endDate, tag = 'busy' } of dates) {\n const end = endDate ? startOfDay(endDate) : startOfDay(startDate);\n for (let date = startOfDay(startDate); date <= end; date = addDays(date, 1)) {\n const iso = date.toISOString();\n if (markers.get(iso) !== 'star') {\n markers.set(iso, tag);\n }\n }\n }\n\n return markers;\n }, [dates]);\n\n const getMarker = useCallback(\n (date: Date): { tag: DateMarkerTag } | undefined => {\n const iso = startOfDay(date).toISOString();\n const tag = dateMarkers.get(iso);\n return tag ? { tag } : undefined;\n },\n [dateMarkers],\n );\n\n const [initialized, setInitialized] = useState(false);\n useEffect(() => {\n const index = getRowIndex(start, initialDate ?? today, weekStartsOn);\n // Keep `scrollMargin` weeks of context above the target row.\n listRef.current?.scrollToRow(Math.max(0, index - scrollMargin));\n }, [initialized, start, today, initialDate, weekStartsOn, scrollMargin]);\n\n useEffect(() => {\n return event.on((event) => {\n // `select` also sets the grid's selection (e.g. when the active event changes); the grid still\n // owns selection — a user click sets it locally and isn't overwritten until the next `select`.\n if (event.type === 'select') {\n setSelected(event.date);\n }\n const index = getRowIndex(start, event.date, weekStartsOn);\n listRef.current?.scrollToRow(Math.max(0, index - scrollMargin));\n });\n }, [event, start, weekStartsOn, scrollMargin, setSelected]);\n\n //\n // Selection refs.\n //\n // `anchorRef` is the immovable end of a range gesture (pointerdown date or\n // initial day when shift+arrow starts). `focusRef` is the moving end\n // (pointer-under-cursor during drag, or the cursor after each shift+arrow).\n // Both refs are kept in sync across mouse drag and keyboard nav so that\n // the user can fluidly mix gestures (e.g., drag a range, then shift+arrow\n // to fine-tune).\n //\n const anchorRef = useRef<Date | undefined>(undefined);\n const focusRef = useRef<Date | undefined>(undefined);\n const draggingRef = useRef(false);\n\n // Pointer tracking for edge-scroll while dragging.\n const pointerXRef = useRef<number>(0);\n const pointerYRef = useRef<number>(0);\n const scrollTopRef = useRef(0);\n const scrollRafRef = useRef<number | undefined>(undefined);\n\n // Scroll the target date into view only if it's outside the visible window.\n // Horizontal moves (left/right within the same week) leave the row index\n // unchanged and are a no-op.\n const scrollIntoView = useCallback(\n (date: Date) => {\n const targetRow = getRowIndex(start, date, weekStartsOn);\n const visibleHeight = maxHeight ?? height;\n if (!visibleHeight) {\n return;\n }\n // Rows fully inside the viewport. Use ceil/floor (not floor of scrollTop) so a partially\n // visible row at either edge counts as \"not fully visible\" even when scrollTop is not a\n // multiple of the row height (which it isn't after a bottom-aligned scroll).\n const firstFullyVisibleRow = Math.ceil(scrollTopRef.current / size);\n const lastFullyVisibleRow = Math.floor((scrollTopRef.current + visibleHeight) / size) - 1;\n if (targetRow < firstFullyVisibleRow) {\n // Align the top edge of the target row with the top edge of the viewport.\n listRef.current?.scrollToPosition(targetRow * size);\n } else if (targetRow > lastFullyVisibleRow) {\n // Align the bottom edge of the target row with the bottom edge of the viewport (using the\n // full visible height, not a row-rounded height, so the row sits flush against the edge).\n listRef.current?.scrollToPosition(Math.max(0, (targetRow + 1) * size - visibleHeight));\n }\n },\n [height, maxHeight, weekStartsOn],\n );\n\n const updateRangeFromAnchor = useCallback(\n (focus: Date, fireRange = false) => {\n const anchor = anchorRef.current;\n if (!anchor) {\n return;\n }\n focusRef.current = focus;\n if (isSameDay(anchor, focus)) {\n setRange(undefined);\n setSelected(anchor);\n } else {\n setSelected(undefined);\n const committed = makeRange(anchor, focus);\n setRange(committed);\n if (fireRange) {\n onSelectRange?.({ range: committed });\n }\n }\n },\n [onSelectRange, setRange, setSelected],\n );\n\n //\n // Drag-to-select range.\n //\n // `prevSelectedRef` snapshots the single-day selection that was active\n // when the gesture began. A click on the *same* already-selected day\n // toggles the selection off on pointerup; a click on any other day (or\n // when no day was selected) just sets the new selection.\n //\n const prevSelectedRef = useRef<Date | undefined>(undefined);\n\n const handleDayPointerDown = useCallback(\n (date: Date, ev: ReactPointerEvent<HTMLDivElement>) => {\n ev.preventDefault();\n prevSelectedRef.current = selected;\n anchorRef.current = date;\n focusRef.current = date;\n draggingRef.current = true;\n // Immediate visual feedback: render the single-select ring on the anchor day.\n setRange(undefined);\n setPendingRange(undefined);\n setSelected(date);\n // Focus the grid so subsequent keyboard nav works.\n gridRef.current?.focus({ preventScroll: true });\n },\n [selected, setPendingRange, setRange, setSelected],\n );\n\n const handleDayPointerEnter = useCallback(\n (date: Date) => {\n if (!draggingRef.current) {\n return;\n }\n const anchor = anchorRef.current;\n if (!anchor) {\n return;\n }\n focusRef.current = date;\n // Always render a pending range while dragging — even a single-day range\n // (when the pointer is on the anchor cell or returns to it). Otherwise\n // the anchor cell would appear empty mid-drag.\n setSelected(undefined);\n setPendingRange(makeRange(anchor, date));\n },\n [setPendingRange, setSelected],\n );\n\n const handleDayPointerUp = useCallback(\n (date: Date) => {\n const anchor = anchorRef.current;\n const wasDragging = draggingRef.current;\n draggingRef.current = false;\n setPendingRange(undefined);\n if (!wasDragging || !anchor) {\n return;\n }\n focusRef.current = date;\n if (isSameDay(anchor, date)) {\n // Single click — toggle off if clicking the previously-selected day,\n // otherwise set as selected. (pointerenter may have cleared the ring\n // mid-drag to show a 1-day pending-range fill; restore here.)\n if (prevSelectedRef.current && isSameDay(prevSelectedRef.current, date)) {\n setSelected(undefined);\n anchorRef.current = undefined;\n focusRef.current = undefined;\n return;\n }\n setSelected(anchor);\n onSelect?.({ date });\n return;\n }\n // Drag commit — `selected` was already cleared by pointerenter.\n const committed = makeRange(anchor, date);\n setRange(committed);\n onSelectRange?.({ range: committed });\n },\n [onSelect, onSelectRange, setPendingRange, setRange, setSelected],\n );\n\n // Cancel drag if the pointer is released outside the grid.\n useEffect(() => {\n const cancel = () => {\n if (draggingRef.current) {\n draggingRef.current = false;\n setPendingRange(undefined);\n }\n };\n window.addEventListener('pointerup', cancel);\n window.addEventListener('pointercancel', cancel);\n return () => {\n window.removeEventListener('pointerup', cancel);\n window.removeEventListener('pointercancel', cancel);\n };\n }, [setPendingRange]);\n\n //\n // Edge auto-scroll while dragging near top/bottom of the grid viewport.\n //\n const tickEdgeScroll = useCallback(() => {\n scrollRafRef.current = undefined;\n if (!draggingRef.current) {\n return;\n }\n const rect = containerRef.current?.getBoundingClientRect();\n if (!rect) {\n return;\n }\n const y = pointerYRef.current;\n let delta = 0;\n if (y < rect.top + EDGE_SCROLL_ZONE) {\n delta = -EDGE_SCROLL_MAX_SPEED * Math.min(1, Math.max(0, (rect.top + EDGE_SCROLL_ZONE - y) / EDGE_SCROLL_ZONE));\n } else if (y > rect.bottom - EDGE_SCROLL_ZONE) {\n delta =\n EDGE_SCROLL_MAX_SPEED * Math.min(1, Math.max(0, (y - (rect.bottom - EDGE_SCROLL_ZONE)) / EDGE_SCROLL_ZONE));\n }\n if (delta !== 0) {\n const newScroll = Math.max(0, scrollTopRef.current + delta);\n listRef.current?.scrollToPosition(newScroll);\n // After scroll, the cell under the (stationary) pointer changes.\n // Look up the new cell and update the pending range accordingly.\n const date = cellDate(document.elementFromPoint(pointerXRef.current, y));\n const anchor = anchorRef.current;\n if (date && anchor) {\n focusRef.current = date;\n if (isSameDay(anchor, date)) {\n setPendingRange(undefined);\n setSelected(anchor);\n } else {\n setSelected(undefined);\n setPendingRange(makeRange(anchor, date));\n }\n }\n scrollRafRef.current = requestAnimationFrame(tickEdgeScroll);\n }\n }, [containerRef, setPendingRange, setSelected]);\n\n useEffect(() => {\n const handleMove = (ev: PointerEvent) => {\n if (!draggingRef.current) {\n return;\n }\n pointerXRef.current = ev.clientX;\n pointerYRef.current = ev.clientY;\n if (scrollRafRef.current === undefined) {\n scrollRafRef.current = requestAnimationFrame(tickEdgeScroll);\n }\n };\n window.addEventListener('pointermove', handleMove);\n return () => {\n window.removeEventListener('pointermove', handleMove);\n if (scrollRafRef.current !== undefined) {\n cancelAnimationFrame(scrollRafRef.current);\n scrollRafRef.current = undefined;\n }\n };\n }, [tickEdgeScroll]);\n\n //\n // Keyboard nav: arrow keys move single selection; shift+arrow expands range.\n //\n const handleKeyDown = useCallback(\n (ev: ReactKeyboardEvent<HTMLDivElement>) => {\n let dx = 0;\n switch (ev.key) {\n case 'ArrowLeft':\n dx = -1;\n break;\n case 'ArrowRight':\n dx = 1;\n break;\n case 'ArrowUp':\n dx = -7;\n break;\n case 'ArrowDown':\n dx = 7;\n break;\n default:\n return;\n }\n ev.preventDefault();\n\n if (ev.shiftKey) {\n // Bootstrap anchor/focus from current state if needed.\n let anchor = anchorRef.current;\n let focus = focusRef.current;\n if (!anchor) {\n // No prior gesture — seed from current selected/range/today.\n if (selected) {\n anchor = startOfDay(selected);\n focus = anchor;\n } else if (range) {\n anchor = range.from;\n focus = range.to;\n } else {\n anchor = startOfDay(today);\n focus = anchor;\n }\n anchorRef.current = anchor;\n focusRef.current = focus;\n }\n const newFocus = addDays(focus ?? anchor, dx);\n updateRangeFromAnchor(newFocus, true);\n scrollIntoView(newFocus);\n } else {\n // Plain arrow — move single selection; clear any range gesture state.\n const current = selected ?? focusRef.current ?? anchorRef.current ?? today;\n const next = addDays(startOfDay(current), dx);\n anchorRef.current = next;\n focusRef.current = next;\n setRange(undefined);\n setPendingRange(undefined);\n setSelected(next);\n onSelect?.({ date: next });\n scrollIntoView(next);\n }\n },\n [onSelect, range, scrollIntoView, selected, setPendingRange, setRange, setSelected, today, updateRangeFromAnchor],\n );\n\n const activeRange = pendingRange ?? range;\n\n const handleScroll = useCallback<NonNullable<ListProps['onScroll']>>((info) => {\n scrollTopRef.current = info.scrollTop;\n setIndex(Math.round(info.scrollTop / size));\n }, []);\n\n const rowRenderer = useCallback<ListRowRenderer>(\n ({ key, index, style }) => {\n // Zebra-stripe alternating months with a subtle neutral overlay over the grid surface, so\n // the banding is independent of (and robust to) surface-token retuning.\n const getBgColor = (date: Date) => (date.getMonth() % 2 === 0 ? 'bg-group-surface' : 'bg-group-alt-surface');\n\n return (\n <div key={key} style={style} className='grid'>\n <div className='grid grid-cols-7 bg-input-surface' style={{ gridTemplateColumns: `repeat(7, ${size}px)` }}>\n {Array.from({ length: 7 }).map((_, i) => {\n const date = getDate(start, index, i, weekStartsOn);\n const marker = getMarker(date);\n const isToday = isSameDay(date, today);\n const isCurrent = isSameDay(date, selected);\n const dateClassNames = isToday\n ? DATE_CLASS_NAMES.today\n : marker?.tag === 'star'\n ? DATE_CLASS_NAMES.starred\n : marker\n ? DATE_CLASS_NAMES.busy\n : undefined;\n\n const inRange = isInRange(date, activeRange);\n\n return (\n <div\n key={i}\n data-date={startOfDay(date).toISOString()}\n className={mx('relative flex justify-center cursor-pointer select-none', getBgColor(date))}\n onPointerDown={(ev) => handleDayPointerDown(date, ev)}\n onPointerEnter={() => handleDayPointerEnter(date)}\n onPointerUp={() => handleDayPointerUp(date)}\n >\n {/* Selection range */}\n {inRange && <div className='absolute inset-0 bg-primary-500/20' />}\n {/* Month */}\n {!dateClassNames && date.getDate() === 1 && (\n <span className='absolute top-0 text-xs text-description'>{format(date, 'MMM')}</span>\n )}\n {/* Day + Marker */}\n <div\n className={mx(\n 'absolute inset-1 rounded-full flex justify-center items-center text-sm text-description',\n dateClassNames,\n )}\n >\n {date.getDate()}\n </div>\n {/* Current */}\n {isCurrent && <div className={mx('absolute inset-0.5 rounded-full', DATE_CLASS_NAMES.current)} />}\n </div>\n );\n })}\n </div>\n </div>\n );\n },\n [activeRange, handleDayPointerDown, handleDayPointerEnter, handleDayPointerUp, getMarker, selected, weekStartsOn],\n );\n\n return (\n <div\n {...composableProps(props, {\n role: 'none',\n classNames: ['flex flex-col h-full w-full justify-center overflow-hidden outline-hidden', classNames],\n })}\n ref={(node: HTMLDivElement | null) => {\n gridRef.current = node;\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else if (forwardedRef) {\n (forwardedRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n }}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n >\n {/* Day of week labels */}\n <div style={{ width: defaultWidth }}>\n <Weekdays weekStartsOn={weekStartsOn} columnWidth={size} />\n </div>\n\n {/* Grid */}\n <div className='flex flex-col h-full w-full justify-center overflow-hidden' ref={containerRef}>\n <List\n ref={listRef}\n role='none'\n className='scrollbar-none outline-hidden'\n width={width}\n height={maxHeight ?? height}\n rowCount={maxRows}\n rowHeight={size}\n rowRenderer={rowRenderer}\n scrollToAlignment='start'\n onScroll={handleScroll}\n onRowsRendered={() => setInitialized(true)}\n />\n </div>\n </div>\n );\n },\n);\n\nCalendarGrid.displayName = CALENDAR_GRID_NAME;\n\n//\n// Calendar\n//\n\nexport const Calendar = {\n Root: CalendarRoot,\n Toolbar: CalendarToolbar,\n Grid: CalendarGrid,\n Week: CalendarWeek,\n};\n\nexport type {\n CalendarController,\n CalendarEvent,\n CalendarGridProps,\n CalendarRootProps,\n CalendarToolbarProps,\n CalendarWeekProps,\n Range,\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { createContext } from '@radix-ui/react-context';\nimport { type Day } from 'date-fns';\nimport { type Dispatch, type SetStateAction } from 'react';\n\nimport { type Event } from '@dxos/async';\n\n//\n// Range\n//\n\n/**\n * Inclusive date range. `from <= to`. Both endpoints are anchored at the\n * start of their day; callers should not rely on time-of-day precision.\n */\nexport type Range = {\n from: Date;\n to: Date;\n};\n\n//\n// Controller\n//\n\nexport type CalendarController = {\n /** Bring a date into view without changing the selection. */\n scrollTo: (date: Date) => void;\n /** Set the grid's selected day (and scroll it into view) — e.g. when the active event changes. */\n select: (date: Date) => void;\n};\n\n//\n// Context\n//\n\n/** Imperative grid signal: `scroll` brings a date into view; `select` also sets it as the selected day. */\nexport type CalendarScrollEvent = {\n type: 'scroll' | 'select';\n date: Date;\n};\n\nexport type CalendarContextValue = {\n weekStartsOn: Day;\n event: Event<CalendarScrollEvent>;\n index: number | undefined;\n setIndex: Dispatch<SetStateAction<number | undefined>>;\n selected: Date | undefined;\n setSelected: Dispatch<SetStateAction<Date | undefined>>;\n /** Committed date range, set by the most recent drag or shift+arrow selection. */\n range: Range | undefined;\n setRange: Dispatch<SetStateAction<Range | undefined>>;\n /** Live drag preview; non-undefined only while the user is dragging. */\n pendingRange: Range | undefined;\n setPendingRange: Dispatch<SetStateAction<Range | undefined>>;\n};\n\nexport const [CalendarContextProvider, useCalendarContext] = createContext<CalendarContextValue>('Calendar');\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Day, differenceInCalendarDays, startOfDay } from 'date-fns';\n\n/**\n * Fixed origin (row 0, column 0 anchor) for the infinite month grid. All row-index math is relative\n * to this epoch, so any view that reports a row index to the shared context must use the same value.\n */\nexport const gridEpoch = new Date('1970-01-01');\n\nexport const getDate = (start: Date, weekNumber: number, dayOfWeek: number, weekStartsOn: Day): Date => {\n const result = new Date(start);\n const startDayOfWeek = start.getDay(); // 0 = Sunday, 1 = Monday, etc.\n const adjustedStartDay = (startDayOfWeek === 0 ? 7 : startDayOfWeek) - weekStartsOn; // Adjust for weekStartsOn.\n result.setDate(start.getDate() - adjustedStartDay + weekNumber * 7 + dayOfWeek);\n return result;\n};\n\n/**\n * Inverse of {@link getDate}: returns the row index for a given date, matching\n * the grid layout (which respects `weekStartsOn`).\n *\n * Uses `differenceInCalendarDays` (DST-safe) — naive ms subtraction silently\n * loses an hour each DST transition, which accumulates over decades and\n * eventually shifts the row boundary by one day. `differenceInWeeks` is also\n * unsuitable because it computes raw 7-day chunks anchored at the start\n * date's weekday rather than the grid's `weekStartsOn` column.\n */\nexport const getRowIndex = (start: Date, date: Date, weekStartsOn: Day): number => {\n const startDayOfWeek = start.getDay();\n const adjustedStartDay = (startDayOfWeek === 0 ? 7 : startDayOfWeek) - weekStartsOn;\n const row0Start = new Date(start);\n row0Start.setDate(start.getDate() - adjustedStartDay);\n return Math.floor(differenceInCalendarDays(date, row0Start) / 7);\n};\n\nexport const isSameDay = (date1: Date, date2: Date | undefined): boolean => {\n return (\n !!date2 &&\n date1.getFullYear() === date2.getFullYear() &&\n date1.getMonth() === date2.getMonth() &&\n date1.getDate() === date2.getDate()\n );\n};\n\n//\n// Time-grid (week view) helpers.\n//\n\nexport const MINUTES_PER_DAY = 24 * 60;\n\n/** Default snap granularity for create/move/resize gestures, in minutes. */\nexport const SNAP_MINUTES = 15;\n\n/** Minutes elapsed since the start of `date`'s day (0 .. 1439). */\nexport const minutesOfDay = (date: Date): number => (date.getTime() - startOfDay(date).getTime()) / 60_000;\n\n/** Return a new Date on the same calendar day as `date`, at `minutes` past midnight. */\nexport const setMinutesOfDay = (date: Date, minutes: number): Date =>\n new Date(startOfDay(date).getTime() + minutes * 60_000);\n\n/** Convert a vertical offset (px from the top of the day column) into minutes past midnight. */\nexport const yToMinutes = (y: number, hourHeight: number): number => (y / hourHeight) * 60;\n\n/** Convert minutes past midnight into a vertical offset (px from the top of the day column). */\nexport const minutesToY = (minutes: number, hourHeight: number): number => (minutes / 60) * hourHeight;\n\n/** Round `minutes` to the nearest `step`, clamped to a single day. */\nexport const snapMinutes = (minutes: number, step: number = SNAP_MINUTES): number => {\n const snapped = Math.round(minutes / step) * step;\n return Math.max(0, Math.min(MINUTES_PER_DAY, snapped));\n};\n\n/**\n * Side-by-side layout slot for an overlapping-event cluster: the event occupies\n * `1 / columnCount` of the day column width, offset by `columnIndex` columns.\n */\nexport type EventLayout = { columnIndex: number; columnCount: number };\n\n/**\n * Compute side-by-side columns for events within a single day. Events that overlap in\n * time (transitively) form a cluster and split the column width evenly; non-overlapping\n * events each get the full width. Input order is irrelevant; results are keyed by index\n * into `events`.\n */\nexport const layoutDayEvents = <T extends { start: Date; end: Date }>(events: T[]): Map<number, EventLayout> => {\n const layout = new Map<number, EventLayout>();\n // Preserve original indices so callers can map results back to their event list.\n const ordered = events\n .map((event, index) => ({ index, start: event.start.getTime(), end: event.end.getTime() }))\n .sort((a, b) => a.start - b.start);\n\n let cluster: { index: number; start: number; end: number }[] = [];\n let clusterMax = -Infinity;\n\n const flush = () => {\n if (cluster.length === 0) {\n return;\n }\n // Greedy column packing: place each event in the first column whose previous event has ended.\n const columnEnds: number[] = [];\n const assigned = cluster.map(({ index, start, end }) => {\n let column = columnEnds.findIndex((columnEnd) => columnEnd <= start);\n if (column === -1) {\n column = columnEnds.length;\n }\n columnEnds[column] = end;\n return { index, column };\n });\n const columnCount = columnEnds.length;\n for (const { index, column } of assigned) {\n layout.set(index, { columnIndex: column, columnCount });\n }\n cluster = [];\n clusterMax = -Infinity;\n };\n\n for (const entry of ordered) {\n if (cluster.length > 0 && entry.start >= clusterMax) {\n // No overlap with the running cluster — close it out before starting fresh.\n flush();\n }\n cluster.push(entry);\n clusterMax = Math.max(clusterMax, entry.end);\n }\n flush();\n\n return layout;\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { addDays, startOfDay, startOfWeek } from 'date-fns';\nimport React, {\n type PointerEvent as ReactPointerEvent,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { composable, composableProps } from '@dxos/react-ui';\nimport { mx } from '@dxos/ui-theme';\n\nimport { useCalendarContext } from './context';\nimport {\n MINUTES_PER_DAY,\n SNAP_MINUTES,\n getRowIndex,\n gridEpoch,\n isSameDay,\n layoutDayEvents,\n minutesOfDay,\n minutesToY,\n setMinutesOfDay,\n snapMinutes,\n yToMinutes,\n} from './util';\nimport { Weekdays } from './Weekdays';\n\nconst CALENDAR_WEEK_NAME = 'CalendarWeek';\n\nconst HOUR_HEIGHT = 48; // px per hour.\nconst GUTTER_WIDTH = 56; // px, hour-label column.\nconst RESIZE_HANDLE = 6; // px, top/bottom hit zone for resizing.\nconst MIN_DURATION = SNAP_MINUTES; // Minimum event duration (minutes).\nconst INITIAL_HOUR = 8; // Hour scrolled into view on mount.\n\n/** An event rendered on the week's time grid. Times are interpreted in local time. */\nexport type CalendarEvent = {\n id: string;\n title?: string;\n start: Date;\n end: Date;\n};\n\ntype CalendarWeekProps = {\n /** Any date within the week to display; the week is derived via `weekStartsOn`. Defaults to today. */\n date?: Date;\n events?: CalendarEvent[];\n /** Fired when the user drags out a new time slot on an empty part of a day column. */\n onEventCreate?: (event: { start: Date; end: Date }) => void;\n /** Fired when an event is moved (duration preserved) or resized (one edge changed). */\n onEventUpdate?: (event: { id: string; start: Date; end: Date }) => void;\n};\n\ntype GestureKind = 'create' | 'move' | 'resize-start' | 'resize-end';\n\n// A live drag gesture. `anchorMinutes` is the fixed edge for create/resize; `grabOffset` is the\n// pointer's offset (minutes) into the event when moving, so the event doesn't jump under the cursor.\ntype Gesture = {\n kind: GestureKind;\n day: Date;\n eventId?: string;\n anchorMinutes: number;\n grabOffset: number;\n durationMinutes: number;\n};\n\n// A pending edit (the gesture's live result) overlaid on the source events while dragging.\ntype Draft = { start: Date; end: Date; eventId?: string };\n\nconst CalendarWeek = composable<HTMLDivElement, CalendarWeekProps>(\n ({ classNames, date, events = [], onEventCreate, onEventUpdate, ...props }, forwardedRef) => {\n const { weekStartsOn, event: scrollEvent, setIndex } = useCalendarContext(CALENDAR_WEEK_NAME);\n const today = useMemo(() => new Date(), []);\n\n // Anchor of the displayed week. Seeded from the `date` prop and re-synced when it changes, but also\n // driven by the shared scroll/select signal (e.g. the Toolbar's Today button, controller.scrollTo).\n const [viewDate, setViewDate] = useState<Date>(() => date ?? today);\n useEffect(() => {\n if (date) {\n setViewDate(date);\n }\n }, [date]);\n useEffect(() => {\n return scrollEvent.on(({ date }) => setViewDate(date));\n }, [scrollEvent]);\n\n const weekDays = useMemo(() => {\n const weekStart = startOfWeek(viewDate, { weekStartsOn });\n return Array.from({ length: 7 }, (_, index) => startOfDay(addDays(weekStart, index)));\n }, [viewDate, weekStartsOn]);\n\n // Report the displayed week to the shared context so the Toolbar's month/year label tracks it.\n useEffect(() => {\n setIndex(getRowIndex(gridEpoch, weekDays[0], weekStartsOn));\n }, [weekDays, weekStartsOn, setIndex]);\n\n // Index events by day so each column lays out independently.\n const eventsByDay = useMemo(() => {\n const byDay = weekDays.map(() => [] as CalendarEvent[]);\n for (const event of events) {\n const dayIndex = weekDays.findIndex((day) => isSameDay(day, event.start));\n if (dayIndex >= 0) {\n byDay[dayIndex].push(event);\n }\n }\n return byDay;\n }, [events, weekDays]);\n\n const scrollRef = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n scrollRef.current?.scrollTo({ top: minutesToY(INITIAL_HOUR * 60, HOUR_HEIGHT) });\n }, []);\n\n //\n // Drag gesture: create / move / resize. All vertical-only, snapped to `SNAP_MINUTES`.\n //\n const gestureRef = useRef<Gesture | undefined>(undefined);\n const columnsRef = useRef<(HTMLDivElement | null)[]>([]);\n const [draft, setDraft] = useState<Draft | undefined>(undefined);\n\n // All day columns share the same vertical geometry, so any column resolves clientY → minutes.\n const pointerMinutes = useCallback((clientY: number): number => {\n const rect = columnsRef.current.find(Boolean)?.getBoundingClientRect();\n if (!rect) {\n return 0;\n }\n return Math.max(0, Math.min(MINUTES_PER_DAY, yToMinutes(clientY - rect.top, HOUR_HEIGHT)));\n }, []);\n\n // The day column under clientX (used to move events across days); undefined when outside all columns.\n const dayFromX = useCallback(\n (clientX: number): Date | undefined => {\n const index = columnsRef.current.findIndex((node) => {\n const rect = node?.getBoundingClientRect();\n return rect && clientX >= rect.left && clientX < rect.right;\n });\n return index >= 0 ? weekDays[index] : undefined;\n },\n [weekDays],\n );\n\n const applyGesture = useCallback(\n (clientX: number, clientY: number): Draft | undefined => {\n const gesture = gestureRef.current;\n if (!gesture) {\n return undefined;\n }\n const { kind, day, eventId, anchorMinutes, grabOffset, durationMinutes } = gesture;\n const raw = pointerMinutes(clientY);\n switch (kind) {\n case 'create': {\n const focus = snapMinutes(raw);\n const from = Math.min(anchorMinutes, focus);\n const to = Math.max(anchorMinutes, focus);\n const end = Math.max(to, from + MIN_DURATION);\n return { eventId, start: setMinutesOfDay(day, from), end: setMinutesOfDay(day, end) };\n }\n case 'move': {\n // Moving may cross day columns; the time-of-day and duration are preserved.\n const targetDay = dayFromX(clientX) ?? day;\n let start = snapMinutes(raw - grabOffset);\n start = Math.max(0, Math.min(MINUTES_PER_DAY - durationMinutes, start));\n return {\n eventId,\n start: setMinutesOfDay(targetDay, start),\n end: setMinutesOfDay(targetDay, start + durationMinutes),\n };\n }\n case 'resize-start': {\n const start = Math.min(snapMinutes(raw), anchorMinutes - MIN_DURATION);\n return { eventId, start: setMinutesOfDay(day, start), end: setMinutesOfDay(day, anchorMinutes) };\n }\n case 'resize-end': {\n const end = Math.max(snapMinutes(raw), anchorMinutes + MIN_DURATION);\n return { eventId, start: setMinutesOfDay(day, anchorMinutes), end: setMinutesOfDay(day, end) };\n }\n }\n },\n [dayFromX, pointerMinutes],\n );\n\n // Latest commit callbacks, read at pointerup so the listeners attached on pointerdown never go stale.\n const callbacksRef = useRef({ onEventCreate, onEventUpdate });\n callbacksRef.current = { onEventCreate, onEventUpdate };\n\n // Removes the active gesture's window listeners; replaced on each `beginGesture` and called on unmount.\n const detachRef = useRef<() => void>(() => {});\n\n const beginGesture = useCallback(\n (gesture: Gesture, ev: ReactPointerEvent) => {\n ev.preventDefault();\n ev.stopPropagation();\n gestureRef.current = gesture;\n setDraft(applyGesture(ev.clientX, ev.clientY));\n\n // Attach listeners imperatively (not via effect) so no pointer event is missed in the window\n // between pointerdown and the next render.\n const handleMove = (moveEv: PointerEvent) => {\n if (gestureRef.current) {\n setDraft(applyGesture(moveEv.clientX, moveEv.clientY));\n }\n };\n\n const handleUp = (upEv: PointerEvent) => {\n const active = gestureRef.current;\n const result = applyGesture(upEv.clientX, upEv.clientY);\n detachRef.current();\n gestureRef.current = undefined;\n setDraft(undefined);\n if (active && result) {\n if (active.kind === 'create') {\n callbacksRef.current.onEventCreate?.({ start: result.start, end: result.end });\n } else if (result.eventId) {\n callbacksRef.current.onEventUpdate?.({ id: result.eventId, start: result.start, end: result.end });\n }\n }\n };\n\n detachRef.current = () => {\n window.removeEventListener('pointermove', handleMove);\n window.removeEventListener('pointerup', handleUp);\n window.removeEventListener('pointercancel', handleUp);\n };\n window.addEventListener('pointermove', handleMove);\n window.addEventListener('pointerup', handleUp);\n window.addEventListener('pointercancel', handleUp);\n },\n [applyGesture],\n );\n\n useEffect(() => () => detachRef.current(), []);\n\n const handleColumnPointerDown = useCallback(\n (day: Date, event: ReactPointerEvent<HTMLDivElement>) => {\n // Only the column background starts a create gesture; events stop propagation.\n const rect = event.currentTarget.getBoundingClientRect();\n const anchor = snapMinutes(yToMinutes(event.clientY - rect.top, HOUR_HEIGHT));\n beginGesture({ kind: 'create', day, anchorMinutes: anchor, grabOffset: 0, durationMinutes: 0 }, event);\n },\n [beginGesture],\n );\n\n // Render an event block bound to the gesture handlers for a given day column. `day` is the column\n // the block currently sits in (its original day, or the target day while being dragged across).\n const renderEvent = useCallback(\n (event: CalendarEvent, day: Date, start: Date, end: Date, columnIndex: number, columnCount: number) => (\n <EventBlock\n key={event.id}\n event={event}\n start={start}\n end={end}\n columnIndex={columnIndex}\n columnCount={columnCount}\n onMoveStart={(ev) =>\n beginGesture(\n {\n kind: 'move',\n day,\n eventId: event.id,\n anchorMinutes: 0,\n grabOffset: pointerMinutes(ev.clientY) - minutesOfDay(event.start),\n durationMinutes: minutesOfDay(event.end) - minutesOfDay(event.start),\n },\n ev,\n )\n }\n onResizeStart={(ev) =>\n beginGesture(\n {\n kind: 'resize-start',\n day,\n eventId: event.id,\n anchorMinutes: minutesOfDay(event.end),\n grabOffset: 0,\n durationMinutes: 0,\n },\n ev,\n )\n }\n onResizeEnd={(ev) =>\n beginGesture(\n {\n kind: 'resize-end',\n day,\n eventId: event.id,\n anchorMinutes: minutesOfDay(event.start),\n grabOffset: 0,\n durationMinutes: 0,\n },\n ev,\n )\n }\n />\n ),\n [beginGesture, pointerMinutes],\n );\n\n // The event under an active move, resolved once so the origin column can drop it and the target\n // column can render it while the pointer is over a different day.\n const draggedEvent = draft?.eventId ? events.find((event) => event.id === draft.eventId) : undefined;\n\n return (\n <div\n {...composableProps(props, {\n classNames: ['flex flex-col h-full w-full overflow-hidden outline-hidden', classNames],\n })}\n ref={forwardedRef}\n >\n <Weekdays weekStartsOn={weekStartsOn} gutter={GUTTER_WIDTH} dates={weekDays} />\n\n <div ref={scrollRef} className='flex-1 overflow-y-auto _scrollbar-thin'>\n <div\n className='grid relative'\n style={{\n height: minutesToY(MINUTES_PER_DAY, HOUR_HEIGHT),\n gridTemplateColumns: `${GUTTER_WIDTH}px repeat(7, 1fr)`,\n }}\n >\n {/* Hour gutter + faint hour lines spanning all columns. */}\n <div className='relative'>\n {Array.from({ length: 24 }, (_, hour) => (\n <div\n key={hour}\n className='absolute right-1 -translate-y-1/2 text-xs text-description tabular-nums'\n style={{ top: minutesToY(hour * 60, HOUR_HEIGHT) }}\n >\n {hour === 0 ? '' : `${hour.toString().padStart(2, '0')}:00`}\n </div>\n ))}\n </div>\n\n {weekDays.map((day, dayIndex) => {\n const dayEvents = eventsByDay[dayIndex];\n const layout = layoutDayEvents(dayEvents);\n const isToday = isSameDay(day, today);\n // The draft when it currently belongs to this column (origin for resize, target for a cross-day move).\n const draftHere = draft && isSameDay(day, draft.start) ? draft : undefined;\n return (\n <div\n key={day.toISOString()}\n ref={(node) => {\n columnsRef.current[dayIndex] = node;\n }}\n data-date={day.toISOString()}\n className={mx(\n 'relative border-l border-separator cursor-cell select-none',\n dayIndex === 6 && 'border-r',\n isToday && 'bg-primary-500/5',\n )}\n onPointerDown={(ev) => handleColumnPointerDown(day, ev)}\n >\n {/* Faint hour lines. */}\n {Array.from({ length: 24 }, (_, hour) => (\n <div\n key={hour}\n className='absolute inset-x-0 border-t border-separator/60'\n style={{ top: minutesToY(hour * 60, HOUR_HEIGHT) }}\n />\n ))}\n\n {/* Events. */}\n {dayEvents.map((event, index) => {\n const slot = layout.get(index) ?? { columnIndex: 0, columnCount: 1 };\n const editing = draft && draft.eventId === event.id ? draft : undefined;\n // Dropped from this column while the move drags it onto another day.\n if (editing && !isSameDay(editing.start, day)) {\n return null;\n }\n const start = editing ? editing.start : event.start;\n const end = editing ? editing.end : event.end;\n return renderEvent(event, day, start, end, slot.columnIndex, slot.columnCount);\n })}\n\n {/* Event being dragged in from another day — rendered full-width above this column's events. */}\n {draftHere &&\n draggedEvent &&\n !dayEvents.some((event) => event.id === draggedEvent.id) &&\n renderEvent(draggedEvent, day, draftHere.start, draftHere.end, 0, 1)}\n\n {/* Pending create rectangle. */}\n {draftHere && !draftHere.eventId && <PendingBlock start={draftHere.start} end={draftHere.end} />}\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n },\n);\n\nCalendarWeek.displayName = CALENDAR_WEEK_NAME;\n\n//\n// EventBlock\n//\n\nconst formatTime = (date: Date): string => {\n const minutes = minutesOfDay(date);\n const hour = Math.floor(minutes / 60);\n const minute = Math.round(minutes % 60);\n return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;\n};\n\ntype EventBlockProps = {\n event: CalendarEvent;\n start: Date;\n end: Date;\n columnIndex: number;\n columnCount: number;\n onMoveStart: (ev: ReactPointerEvent<HTMLDivElement>) => void;\n onResizeStart: (ev: ReactPointerEvent<HTMLDivElement>) => void;\n onResizeEnd: (ev: ReactPointerEvent<HTMLDivElement>) => void;\n};\n\nconst EventBlock = ({\n event,\n start,\n end,\n columnIndex,\n columnCount,\n onMoveStart,\n onResizeStart,\n onResizeEnd,\n}: EventBlockProps) => {\n const top = minutesToY(minutesOfDay(start), HOUR_HEIGHT);\n const height = Math.max(minutesToY(minutesOfDay(end) - minutesOfDay(start), HOUR_HEIGHT), RESIZE_HANDLE * 2);\n // Leave a 1px gutter between side-by-side columns.\n const widthPct = 100 / columnCount;\n return (\n <div\n className='absolute rounded-sm bg-primary-500/80 text-inverse-fg overflow-hidden cursor-move shadow-sm'\n style={{\n top,\n height,\n left: `calc(${columnIndex * widthPct}% + 1px)`,\n width: `calc(${widthPct}% - 2px)`,\n }}\n onPointerDown={onMoveStart}\n >\n <div\n className='absolute inset-x-0 top-0 cursor-ns-resize'\n style={{ height: RESIZE_HANDLE }}\n onPointerDown={onResizeStart}\n />\n <div className='px-1 py-0.5 text-xs leading-tight'>\n <div className='font-medium truncate'>{event.title ?? '(untitled)'}</div>\n {/* <div className='tabular-nums opacity-80'>\n {formatTime(start)}–{formatTime(end)}\n </div> */}\n </div>\n <div\n className='absolute inset-x-0 bottom-0 cursor-ns-resize'\n style={{ height: RESIZE_HANDLE }}\n onPointerDown={onResizeEnd}\n />\n </div>\n );\n};\n\n//\n// PendingBlock\n//\n\nconst PendingBlock = ({ start, end }: { start: Date; end: Date }) => {\n const top = minutesToY(minutesOfDay(start), HOUR_HEIGHT);\n const height = minutesToY(minutesOfDay(end) - minutesOfDay(start), HOUR_HEIGHT);\n return (\n <div\n className='absolute inset-x-0 rounded bg-primary-500/40 border border-primary-500 pointer-events-none'\n style={{ top, height }}\n >\n <div className='px-1 py-0.5 text-xs tabular-nums text-inverse-fg'>\n {formatTime(start)}–{formatTime(end)}\n </div>\n </div>\n );\n};\n\nexport { CalendarWeek };\nexport type { CalendarWeekProps };\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Day, addDays, format, startOfWeek } from 'date-fns';\nimport React, { useMemo } from 'react';\n\nimport { mx } from '@dxos/ui-theme';\n\nimport { isSameDay } from './util';\n\nexport type WeekdaysProps = {\n weekStartsOn: Day;\n /** Fixed column width in px (e.g. the month grid's cells); omit for flexible `1fr` columns. */\n columnWidth?: number;\n /** Leading spacer width in px, aligning the labels over a body gutter (e.g. the week view's hour column). */\n gutter?: number;\n /** When provided, the matching day-of-month number is rendered beneath each label (week view). */\n dates?: Date[];\n};\n\n/**\n * Shared day-of-week header for the calendar views. Renders seven short day labels\n * ordered by `weekStartsOn`; when `dates` is supplied it also shows the day number and\n * highlights today.\n */\nexport const Weekdays = ({ weekStartsOn, columnWidth, gutter, dates }: WeekdaysProps) => {\n const labels = useMemo(() => {\n const weekStart = startOfWeek(new Date(), { weekStartsOn });\n return Array.from({ length: 7 }, (_, index) => format(addDays(weekStart, index), 'EEE'));\n }, [weekStartsOn]);\n\n const today = useMemo(() => new Date(), []);\n const columnTemplate = columnWidth ? `repeat(7, ${columnWidth}px)` : 'repeat(7, 1fr)';\n\n return (\n <div\n className='grid w-full shrink-0'\n style={{ gridTemplateColumns: gutter ? `${gutter}px ${columnTemplate}` : columnTemplate }}\n >\n {gutter != null && <div aria-hidden />}\n {labels.map((label, index) => {\n const date = dates?.[index];\n const isToday = !!date && isSameDay(date, today);\n return (\n <div\n key={index}\n className={mx('flex flex-col items-center p-2 text-sm font-thin', isToday && 'text-accent-text')}\n >\n <span>{label}</span>\n {date && <span className='text-lg font-normal tabular-nums'>{date.getDate()}</span>}\n </div>\n );\n })}\n </div>\n );\n};\n"],
5
5
  "mappings": "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",
6
6
  "names": ["addDays", "format", "startOfDay", "React", "forwardRef", "useCallback", "useEffect", "useImperativeHandle", "useMemo", "useRef", "useState", "useResizeDetector", "List", "Event", "IconButton", "useTranslation", "composable", "composableProps", "mx", "translationKey", "createContext", "CalendarContextProvider", "useCalendarContext", "differenceInCalendarDays", "startOfDay", "gridEpoch", "Date", "getDate", "start", "weekNumber", "dayOfWeek", "weekStartsOn", "result", "startDayOfWeek", "getDay", "adjustedStartDay", "setDate", "getRowIndex", "date", "row0Start", "Math", "floor", "isSameDay", "date1", "date2", "getFullYear", "getMonth", "MINUTES_PER_DAY", "SNAP_MINUTES", "minutesOfDay", "getTime", "setMinutesOfDay", "minutes", "yToMinutes", "y", "hourHeight", "minutesToY", "snapMinutes", "step", "snapped", "round", "max", "min", "layoutDayEvents", "events", "layout", "Map", "ordered", "map", "event", "index", "end", "sort", "a", "b", "cluster", "clusterMax", "flush", "length", "columnEnds", "assigned", "column", "findIndex", "columnEnd", "columnCount", "set", "columnIndex", "entry", "push", "addDays", "startOfDay", "startOfWeek", "React", "useCallback", "useEffect", "useLayoutEffect", "useMemo", "useRef", "useState", "composable", "composableProps", "mx", "addDays", "format", "startOfWeek", "React", "useMemo", "mx", "Weekdays", "weekStartsOn", "columnWidth", "gutter", "dates", "labels", "useMemo", "weekStart", "startOfWeek", "Date", "Array", "from", "length", "_", "index", "format", "addDays", "today", "columnTemplate", "div", "className", "style", "gridTemplateColumns", "aria-hidden", "map", "label", "date", "isToday", "isSameDay", "key", "mx", "span", "getDate", "CALENDAR_WEEK_NAME", "HOUR_HEIGHT", "GUTTER_WIDTH", "RESIZE_HANDLE", "MIN_DURATION", "SNAP_MINUTES", "INITIAL_HOUR", "CalendarWeek", "composable", "classNames", "date", "events", "onEventCreate", "onEventUpdate", "props", "forwardedRef", "weekStartsOn", "event", "scrollEvent", "setIndex", "useCalendarContext", "today", "useMemo", "Date", "viewDate", "setViewDate", "useState", "useEffect", "on", "weekDays", "weekStart", "startOfWeek", "Array", "from", "length", "_", "index", "startOfDay", "addDays", "getRowIndex", "gridEpoch", "eventsByDay", "byDay", "map", "dayIndex", "findIndex", "day", "isSameDay", "start", "push", "scrollRef", "useRef", "useLayoutEffect", "current", "scrollTo", "top", "minutesToY", "gestureRef", "undefined", "columnsRef", "draft", "setDraft", "pointerMinutes", "useCallback", "clientY", "rect", "find", "Boolean", "getBoundingClientRect", "Math", "max", "min", "MINUTES_PER_DAY", "yToMinutes", "dayFromX", "clientX", "node", "left", "right", "applyGesture", "gesture", "kind", "eventId", "anchorMinutes", "grabOffset", "durationMinutes", "raw", "focus", "snapMinutes", "to", "end", "setMinutesOfDay", "targetDay", "callbacksRef", "detachRef", "beginGesture", "ev", "preventDefault", "stopPropagation", "handleMove", "moveEv", "handleUp", "upEv", "active", "result", "id", "window", "removeEventListener", "addEventListener", "handleColumnPointerDown", "currentTarget", "anchor", "renderEvent", "columnIndex", "columnCount", "React", "EventBlock", "key", "onMoveStart", "minutesOfDay", "onResizeStart", "onResizeEnd", "draggedEvent", "div", "composableProps", "ref", "Weekdays", "gutter", "dates", "className", "style", "height", "gridTemplateColumns", "hour", "toString", "padStart", "dayEvents", "layout", "layoutDayEvents", "isToday", "draftHere", "toISOString", "data-date", "mx", "onPointerDown", "slot", "get", "editing", "some", "PendingBlock", "displayName", "formatTime", "minutes", "floor", "minute", "round", "widthPct", "width", "title", "maxRows", "start", "gridEpoch", "size", "defaultWidth", "EDGE_SCROLL_ZONE", "EDGE_SCROLL_MAX_SPEED", "DATE_CLASS_NAMES", "current", "today", "busy", "starred", "makeRange", "a", "b", "dayA", "startOfDay", "dayB", "from", "to", "isInRange", "date", "range", "day", "getTime", "cellDate", "el", "document", "body", "iso", "getAttribute", "Date", "parentElement", "undefined", "CalendarRoot", "forwardRef", "children", "weekStartsOn", "forwardedRef", "event", "useMemo", "Event", "selected", "setSelected", "useState", "index", "setIndex", "setRange", "pendingRange", "setPendingRange", "useImperativeHandle", "scrollTo", "emit", "type", "select", "React", "CalendarContextProvider", "CALENDAR_TOOLBAR_NAME", "CalendarToolbar", "composable", "classNames", "props", "t", "useTranslation", "translationKey", "useCalendarContext", "top", "getDate", "handleToday", "useCallback", "div", "composableProps", "role", "ref", "className", "IconButton", "variant", "icon", "iconOnly", "label", "onClick", "format", "getFullYear", "displayName", "CALENDAR_GRID_NAME", "CalendarGrid", "rows", "dates", "initialDate", "scrollMargin", "onSelect", "onSelectRange", "containerRef", "width", "height", "useResizeDetector", "maxHeight", "listRef", "useRef", "gridRef", "dateMarkers", "markers", "Map", "startDate", "endDate", "tag", "end", "addDays", "toISOString", "get", "set", "getMarker", "initialized", "setInitialized", "useEffect", "getRowIndex", "scrollToRow", "Math", "max", "on", "anchorRef", "focusRef", "draggingRef", "pointerXRef", "pointerYRef", "scrollTopRef", "scrollRafRef", "scrollIntoView", "targetRow", "visibleHeight", "firstFullyVisibleRow", "ceil", "lastFullyVisibleRow", "floor", "scrollToPosition", "updateRangeFromAnchor", "focus", "fireRange", "anchor", "isSameDay", "committed", "prevSelectedRef", "handleDayPointerDown", "ev", "preventDefault", "preventScroll", "handleDayPointerEnter", "handleDayPointerUp", "wasDragging", "cancel", "window", "addEventListener", "removeEventListener", "tickEdgeScroll", "rect", "getBoundingClientRect", "y", "delta", "min", "bottom", "newScroll", "elementFromPoint", "requestAnimationFrame", "handleMove", "clientX", "clientY", "cancelAnimationFrame", "handleKeyDown", "dx", "key", "shiftKey", "newFocus", "next", "activeRange", "handleScroll", "info", "scrollTop", "round", "rowRenderer", "style", "getBgColor", "getMonth", "gridTemplateColumns", "Array", "length", "map", "_", "i", "marker", "isToday", "isCurrent", "dateClassNames", "inRange", "data-date", "mx", "onPointerDown", "onPointerEnter", "onPointerUp", "span", "node", "tabIndex", "onKeyDown", "Weekdays", "columnWidth", "List", "rowCount", "rowHeight", "scrollToAlignment", "onScroll", "onRowsRendered", "Calendar", "Root", "Toolbar", "Grid", "Week", "CalendarWeek"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"src/components/Calendar/context.ts":{"bytes":2909,"imports":[{"path":"@radix-ui/react-context","kind":"import-statement","external":true}],"format":"esm"},"src/components/Calendar/util.ts":{"bytes":16608,"imports":[{"path":"date-fns","kind":"import-statement","external":true}],"format":"esm"},"src/components/Calendar/Weekdays.tsx":{"bytes":6351,"imports":[{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"src/components/Calendar/util.ts","kind":"import-statement","original":"./util"}],"format":"esm"},"src/components/Calendar/Week.tsx":{"bytes":56249,"imports":[{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"src/components/Calendar/context.ts","kind":"import-statement","original":"./context"},{"path":"src/components/Calendar/util.ts","kind":"import-statement","original":"./util"},{"path":"src/components/Calendar/Weekdays.tsx","kind":"import-statement","original":"./Weekdays"}],"format":"esm"},"src/components/Calendar/Calendar.tsx":{"bytes":76889,"imports":[{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react-resize-detector","kind":"import-statement","external":true},{"path":"react-virtualized","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"src/components/Calendar/context.ts","kind":"import-statement","original":"./context"},{"path":"src/components/Calendar/util.ts","kind":"import-statement","original":"./util"},{"path":"src/components/Calendar/Week.tsx","kind":"import-statement","original":"./Week"},{"path":"src/components/Calendar/Weekdays.tsx","kind":"import-statement","original":"./Weekdays"}],"format":"esm"},"src/components/Calendar/index.ts":{"bytes":376,"imports":[{"path":"src/components/Calendar/Calendar.tsx","kind":"import-statement","original":"./Calendar"}],"format":"esm"},"src/components/index.ts":{"bytes":376,"imports":[{"path":"src/components/Calendar/index.ts","kind":"import-statement","original":"./Calendar"}],"format":"esm"},"src/index.ts":{"bytes":382,"imports":[{"path":"src/components/index.ts","kind":"import-statement","original":"./components"}],"format":"esm"},"src/translations.ts":{"bytes":1105,"imports":[],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":83284},"dist/lib/browser/index.mjs":{"imports":[{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react-resize-detector","kind":"import-statement","external":true},{"path":"react-virtualized","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"@radix-ui/react-context","kind":"import-statement","external":true},{"path":"date-fns","kind":"import-statement","external":true},{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true}],"exports":["Calendar"],"entryPoint":"src/index.ts","inputs":{"src/components/Calendar/Calendar.tsx":{"bytesInOutput":16768},"src/components/Calendar/context.ts":{"bytesInOutput":136},"src/components/Calendar/util.ts":{"bytesInOutput":2677},"src/components/Calendar/Week.tsx":{"bytesInOutput":12337},"src/components/Calendar/Weekdays.tsx":{"bytesInOutput":1407},"src/components/Calendar/index.ts":{"bytesInOutput":0},"src/components/index.ts":{"bytesInOutput":0},"src/index.ts":{"bytesInOutput":0}},"bytes":33654},"dist/lib/browser/translations.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":582},"dist/lib/browser/translations.mjs":{"imports":[],"exports":["translationKey","translations"],"entryPoint":"src/translations.ts","inputs":{"src/translations.ts":{"bytesInOutput":167}},"bytes":277}}}
1
+ {"inputs":{"src/components/Calendar/context.ts":{"bytes":2909,"imports":[{"path":"@radix-ui/react-context","kind":"import-statement","external":true}],"format":"esm"},"src/components/Calendar/util.ts":{"bytes":16608,"imports":[{"path":"date-fns","kind":"import-statement","external":true}],"format":"esm"},"src/components/Calendar/Weekdays.tsx":{"bytes":6351,"imports":[{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"src/components/Calendar/util.ts","kind":"import-statement","original":"./util"}],"format":"esm"},"src/components/Calendar/Week.tsx":{"bytes":56249,"imports":[{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"src/components/Calendar/context.ts","kind":"import-statement","original":"./context"},{"path":"src/components/Calendar/util.ts","kind":"import-statement","original":"./util"},{"path":"src/components/Calendar/Weekdays.tsx","kind":"import-statement","original":"./Weekdays"}],"format":"esm"},"src/components/Calendar/Calendar.tsx":{"bytes":76893,"imports":[{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react-resize-detector","kind":"import-statement","external":true},{"path":"react-virtualized","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"src/components/Calendar/context.ts","kind":"import-statement","original":"./context"},{"path":"src/components/Calendar/util.ts","kind":"import-statement","original":"./util"},{"path":"src/components/Calendar/Week.tsx","kind":"import-statement","original":"./Week"},{"path":"src/components/Calendar/Weekdays.tsx","kind":"import-statement","original":"./Weekdays"}],"format":"esm"},"src/components/Calendar/index.ts":{"bytes":376,"imports":[{"path":"src/components/Calendar/Calendar.tsx","kind":"import-statement","original":"./Calendar"}],"format":"esm"},"src/components/index.ts":{"bytes":376,"imports":[{"path":"src/components/Calendar/index.ts","kind":"import-statement","original":"./Calendar"}],"format":"esm"},"src/index.ts":{"bytes":382,"imports":[{"path":"src/components/index.ts","kind":"import-statement","original":"./components"}],"format":"esm"},"src/translations.ts":{"bytes":1105,"imports":[],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":83284},"dist/lib/browser/index.mjs":{"imports":[{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react-resize-detector","kind":"import-statement","external":true},{"path":"react-virtualized","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"@radix-ui/react-context","kind":"import-statement","external":true},{"path":"date-fns","kind":"import-statement","external":true},{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"date-fns","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true}],"exports":["Calendar"],"entryPoint":"src/index.ts","inputs":{"src/components/Calendar/Calendar.tsx":{"bytesInOutput":16768},"src/components/Calendar/context.ts":{"bytesInOutput":136},"src/components/Calendar/util.ts":{"bytesInOutput":2677},"src/components/Calendar/Week.tsx":{"bytesInOutput":12337},"src/components/Calendar/Weekdays.tsx":{"bytesInOutput":1407},"src/components/Calendar/index.ts":{"bytesInOutput":0},"src/components/index.ts":{"bytesInOutput":0},"src/index.ts":{"bytesInOutput":0}},"bytes":33654},"dist/lib/browser/translations.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":582},"dist/lib/browser/translations.mjs":{"imports":[],"exports":["translationKey","translations"],"entryPoint":"src/translations.ts","inputs":{"src/translations.ts":{"bytesInOutput":167}},"bytes":277}}}