@dxos/react-ui-calendar 0.8.4-main.ef1bc66f44 → 0.8.4-main.fcc0d83b33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +42 -62
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/translations.mjs +16 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +42 -62
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/translations.mjs +18 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/types/src/components/Calendar/Calendar.d.ts +12 -18
- package/dist/types/src/components/Calendar/Calendar.d.ts.map +1 -1
- package/dist/types/src/components/Calendar/Calendar.stories.d.ts +4 -9
- package/dist/types/src/components/Calendar/Calendar.stories.d.ts.map +1 -1
- package/dist/types/src/components/Calendar/util.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -1
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +25 -20
- package/src/components/Calendar/Calendar.stories.tsx +15 -40
- package/src/components/Calendar/Calendar.tsx +138 -157
- package/src/translations.ts +1 -1
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
// src/components/Calendar/Calendar.tsx
|
|
2
2
|
import { createContext } from "@radix-ui/react-context";
|
|
3
|
-
import { addDays, differenceInWeeks, format, startOfWeek } from "date-fns";
|
|
3
|
+
import { addDays, differenceInWeeks, format, startOfDay, startOfWeek } from "date-fns";
|
|
4
4
|
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { useResizeDetector } from "react-resize-detector";
|
|
6
6
|
import { List } from "react-virtualized";
|
|
7
7
|
import { Event } from "@dxos/async";
|
|
8
|
-
import {
|
|
9
|
-
import { mx } from "@dxos/ui-theme";
|
|
10
|
-
|
|
11
|
-
// src/translations.ts
|
|
12
|
-
var translationKey = "@dxos/react-ui-calendar";
|
|
13
|
-
var translations = [
|
|
14
|
-
{
|
|
15
|
-
"en-US": {
|
|
16
|
-
[translationKey]: {
|
|
17
|
-
"today button": "Today"
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
];
|
|
8
|
+
import { IconButton, useTranslation } from "@dxos/react-ui";
|
|
9
|
+
import { composable, composableProps, mx } from "@dxos/ui-theme";
|
|
10
|
+
import { translationKey } from "#translations";
|
|
22
11
|
|
|
23
12
|
// src/components/Calendar/util.ts
|
|
24
13
|
var getDate = (start2, weekNumber, dayOfWeek, weekStartsOn) => {
|
|
@@ -61,16 +50,8 @@ var CalendarRoot = /* @__PURE__ */ forwardRef(({ children, weekStartsOn = 1 }, f
|
|
|
61
50
|
setSelected
|
|
62
51
|
}, children);
|
|
63
52
|
});
|
|
64
|
-
var CALENDAR_VIEWPORT_NAME = "CalendarContent";
|
|
65
|
-
var CalendarViewport = ({ children, classNames }) => {
|
|
66
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
67
|
-
role: "none",
|
|
68
|
-
className: mx("flex flex-col items-center overflow-hidden bg-inputSurface", classNames)
|
|
69
|
-
}, children);
|
|
70
|
-
};
|
|
71
|
-
CalendarViewport.displayName = CALENDAR_VIEWPORT_NAME;
|
|
72
53
|
var CALENDAR_TOOLBAR_NAME = "CalendarHeader";
|
|
73
|
-
var CalendarToolbar = ({ classNames }) => {
|
|
54
|
+
var CalendarToolbar = composable(({ classNames, ...props }, forwardedRef) => {
|
|
74
55
|
const { t } = useTranslation(translationKey);
|
|
75
56
|
const { weekStartsOn, event, index, selected } = useCalendarContext(CALENDAR_TOOLBAR_NAME);
|
|
76
57
|
const top = useMemo(() => getDate(start, index ?? 0, 6, weekStartsOn), [
|
|
@@ -89,8 +70,14 @@ var CalendarToolbar = ({ classNames }) => {
|
|
|
89
70
|
today
|
|
90
71
|
]);
|
|
91
72
|
return /* @__PURE__ */ React.createElement("div", {
|
|
92
|
-
|
|
93
|
-
|
|
73
|
+
...composableProps(props, {
|
|
74
|
+
role: "none",
|
|
75
|
+
classNames: [
|
|
76
|
+
"shrink-0 grid! grid-cols-3 items-center bg-toolbar-surface",
|
|
77
|
+
classNames
|
|
78
|
+
]
|
|
79
|
+
}),
|
|
80
|
+
ref: forwardedRef,
|
|
94
81
|
style: {
|
|
95
82
|
width: defaultWidth
|
|
96
83
|
}
|
|
@@ -98,26 +85,31 @@ var CalendarToolbar = ({ classNames }) => {
|
|
|
98
85
|
className: "flex justify-start"
|
|
99
86
|
}, /* @__PURE__ */ React.createElement(IconButton, {
|
|
100
87
|
variant: "ghost",
|
|
101
|
-
size: 5,
|
|
102
88
|
icon: "ph--calendar--regular",
|
|
103
89
|
iconOnly: true,
|
|
104
90
|
classNames: "aspect-square",
|
|
105
|
-
label: t("today
|
|
91
|
+
label: t("today.button"),
|
|
106
92
|
onClick: handleToday
|
|
107
93
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
108
94
|
className: "flex justify-center p-2 text-description"
|
|
109
95
|
}, format(selected ?? top, "MMMM")), /* @__PURE__ */ React.createElement("div", {
|
|
110
96
|
className: "flex justify-end p-2 text-description"
|
|
111
97
|
}, (selected ?? top).getFullYear()));
|
|
112
|
-
};
|
|
98
|
+
});
|
|
113
99
|
CalendarToolbar.displayName = CALENDAR_TOOLBAR_NAME;
|
|
114
100
|
var CALENDAR_GRID_NAME = "CalendarGrid";
|
|
115
|
-
var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
101
|
+
var CalendarGrid = composable(({ classNames, rows, dates = [], onSelect, ...props }, forwardedRef) => {
|
|
116
102
|
const { weekStartsOn, event, setIndex, selected, setSelected } = useCalendarContext(CALENDAR_GRID_NAME);
|
|
117
103
|
const { ref: containerRef, width = 0, height = 0 } = useResizeDetector();
|
|
118
104
|
const maxHeight = rows ? rows * size : void 0;
|
|
119
105
|
const listRef = useRef(null);
|
|
120
106
|
const today = useMemo(() => /* @__PURE__ */ new Date(), []);
|
|
107
|
+
const dateSet = useMemo(() => new Set(dates.map((date) => startOfDay(date).toISOString())), [
|
|
108
|
+
dates
|
|
109
|
+
]);
|
|
110
|
+
const hasDate = useCallback((date) => dateSet.has(startOfDay(date).toISOString()), [
|
|
111
|
+
dateSet
|
|
112
|
+
]);
|
|
121
113
|
const [initialized, setInitialized] = useState(false);
|
|
122
114
|
useEffect(() => {
|
|
123
115
|
const index = differenceInWeeks(today, start);
|
|
@@ -151,9 +143,6 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
151
143
|
return format(day, "EEE");
|
|
152
144
|
});
|
|
153
145
|
}, []);
|
|
154
|
-
const getNumAppointments = useCallback((_date) => {
|
|
155
|
-
return 0;
|
|
156
|
-
}, []);
|
|
157
146
|
const handleDaySelect = useCallback((date) => {
|
|
158
147
|
setSelected((current) => isSameDay(date, current) ? void 0 : date);
|
|
159
148
|
onSelect?.({
|
|
@@ -166,18 +155,15 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
166
155
|
setIndex(Math.round(info.scrollTop / size));
|
|
167
156
|
}, []);
|
|
168
157
|
const rowRenderer = useCallback(({ key, index, style }) => {
|
|
169
|
-
const getBgColor = (date) => date.getMonth() % 2 === 0 && "bg-
|
|
158
|
+
const getBgColor = (date) => date.getMonth() % 2 === 0 && "bg-modal-surface";
|
|
170
159
|
return /* @__PURE__ */ React.createElement("div", {
|
|
171
160
|
key,
|
|
172
161
|
role: "none",
|
|
173
162
|
style,
|
|
174
|
-
className: "
|
|
163
|
+
className: "grid"
|
|
175
164
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
176
165
|
role: "none",
|
|
177
|
-
className:
|
|
178
|
-
}), /* @__PURE__ */ React.createElement("div", {
|
|
179
|
-
role: "none",
|
|
180
|
-
className: "grid grid-cols-7",
|
|
166
|
+
className: "grid grid-cols-7 bg-input-surface",
|
|
181
167
|
style: {
|
|
182
168
|
gridTemplateColumns: `repeat(7, ${size}px)`
|
|
183
169
|
}
|
|
@@ -185,8 +171,7 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
185
171
|
length: 7
|
|
186
172
|
}).map((_, i) => {
|
|
187
173
|
const date = getDate(start, index, i, weekStartsOn);
|
|
188
|
-
const
|
|
189
|
-
const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : void 0;
|
|
174
|
+
const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : hasDate(date) ? "border-neutral-700 border-dashed" : void 0;
|
|
190
175
|
return /* @__PURE__ */ React.createElement("div", {
|
|
191
176
|
key: i,
|
|
192
177
|
role: "none",
|
|
@@ -198,30 +183,27 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
198
183
|
className: "absolute top-0 text-xs text-description"
|
|
199
184
|
}, format(date, "MMM")), border && /* @__PURE__ */ React.createElement("div", {
|
|
200
185
|
role: "none",
|
|
201
|
-
className: mx("absolute
|
|
202
|
-
}), num > 0 && /* @__PURE__ */ React.createElement(Icon, {
|
|
203
|
-
classNames: "absolute bottom-0",
|
|
204
|
-
icon: num > 3 ? "ph--dots-three--regular" : "ph--dot--regular",
|
|
205
|
-
size: 5
|
|
186
|
+
className: mx("absolute inset-1 border-2 rounded-full", border)
|
|
206
187
|
}));
|
|
207
|
-
}))
|
|
208
|
-
className: mx(getBgColor(getDate(start, index, 6, weekStartsOn)))
|
|
209
|
-
}));
|
|
188
|
+
})));
|
|
210
189
|
}, [
|
|
211
190
|
handleDaySelect,
|
|
212
|
-
|
|
191
|
+
hasDate,
|
|
213
192
|
selected,
|
|
214
193
|
weekStartsOn
|
|
215
194
|
]);
|
|
216
195
|
return /* @__PURE__ */ React.createElement("div", {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
196
|
+
...composableProps(props, {
|
|
197
|
+
role: "none",
|
|
198
|
+
classNames: [
|
|
199
|
+
"flex flex-col h-full w-full justify-center overflow-hidden",
|
|
200
|
+
classNames
|
|
201
|
+
]
|
|
202
|
+
}),
|
|
203
|
+
ref: forwardedRef
|
|
222
204
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
223
205
|
role: "none",
|
|
224
|
-
className: "
|
|
206
|
+
className: "grid w-full grid-cols-7",
|
|
225
207
|
style: {
|
|
226
208
|
width: defaultWidth
|
|
227
209
|
}
|
|
@@ -229,15 +211,14 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
229
211
|
key: i,
|
|
230
212
|
role: "none",
|
|
231
213
|
className: "flex justify-center p-2 text-sm font-thin"
|
|
232
|
-
}, date)))
|
|
214
|
+
}, date))), /* @__PURE__ */ React.createElement("div", {
|
|
233
215
|
role: "none",
|
|
234
|
-
className: "flex flex-col
|
|
216
|
+
className: "flex flex-col h-full w-full justify-center overflow-hidden",
|
|
235
217
|
ref: containerRef
|
|
236
218
|
}, /* @__PURE__ */ React.createElement(List, {
|
|
237
219
|
ref: listRef,
|
|
238
220
|
role: "none",
|
|
239
|
-
|
|
240
|
-
className: "[&>div]:snap-y scrollbar-none outline-none",
|
|
221
|
+
className: "scrollbar-none outline-hidden",
|
|
241
222
|
width,
|
|
242
223
|
height: maxHeight ?? height,
|
|
243
224
|
rowCount: maxRows,
|
|
@@ -247,11 +228,10 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
247
228
|
onScroll: handleScroll,
|
|
248
229
|
onRowsRendered: () => setInitialized(true)
|
|
249
230
|
})));
|
|
250
|
-
};
|
|
231
|
+
});
|
|
251
232
|
CalendarGrid.displayName = CALENDAR_GRID_NAME;
|
|
252
233
|
var Calendar = {
|
|
253
234
|
Root: CalendarRoot,
|
|
254
|
-
Viewport: CalendarViewport,
|
|
255
235
|
Toolbar: CalendarToolbar,
|
|
256
236
|
Grid: CalendarGrid
|
|
257
237
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../src/components/Calendar/Calendar.tsx", "../../../src/
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { createContext } from '@radix-ui/react-context';\nimport { type Day, addDays, differenceInWeeks, format, startOfWeek } from 'date-fns';\nimport React, {\n type Dispatch,\n type PropsWithChildren,\n type SetStateAction,\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 { Icon, IconButton, type ThemedClassName, useTranslation } from '@dxos/react-ui';\nimport { mx } from '@dxos/ui-theme';\n\nimport { translationKey } from '../../translations';\n\nimport { getDate, isSameDay } from './util';\n\nconst maxRows = 50 * 100;\nconst start = new Date('1970-01-01');\nconst size = 48;\nconst defaultWidth = 7 * size;\n\n//\n// Context\n//\n\ntype CalendarEvent = {\n type: 'scroll';\n date: Date;\n};\n\ntype CalendarContextValue = {\n weekStartsOn: Day;\n event: Event<CalendarEvent>;\n index: number | undefined;\n setIndex: Dispatch<SetStateAction<number | undefined>>;\n selected: Date | undefined;\n setSelected: Dispatch<SetStateAction<Date | undefined>>;\n};\n\nconst [CalendarContextProvider, useCalendarContext] = createContext<CalendarContextValue>('Calendar');\n\n//\n// Controller\n//\n\ntype CalendarController = {\n scrollTo: (date: Date) => void;\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<CalendarEvent>(), []);\n const [selected, setSelected] = useState<Date | undefined>();\n const [index, setIndex] = useState<number | undefined>();\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n scrollTo: (date: Date) => {\n event.emit({ type: 'scroll', 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 >\n {children}\n </CalendarContextProvider>\n );\n },\n);\n\n//\n// Viewport\n//\n\nconst CALENDAR_VIEWPORT_NAME = 'CalendarContent';\n\ntype CalendarViewportProps = PropsWithChildren<ThemedClassName>;\n\nconst CalendarViewport = ({ children, classNames }: CalendarViewportProps) => {\n return (\n <div role='none' className={mx('flex flex-col items-center overflow-hidden bg-inputSurface', classNames)}>\n {children}\n </div>\n );\n};\n\nCalendarViewport.displayName = CALENDAR_VIEWPORT_NAME;\n\n//\n// Header\n//\n\nconst CALENDAR_TOOLBAR_NAME = 'CalendarHeader';\n\ntype CalendarToolbarProps = ThemedClassName;\n\nconst CalendarToolbar = ({ classNames }: CalendarToolbarProps) => {\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 role='none'\n className={mx('shink-0 is-full grid grid-cols-3 items-center bg-barSurface', classNames)}\n style={{ width: defaultWidth }}\n >\n <div className='flex justify-start'>\n <IconButton\n variant='ghost'\n size={5}\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// TODO(burdon): Key nav.\n// TODO(burdon): Drag range.\n//\n\nconst CALENDAR_GRID_NAME = 'CalendarGrid';\n\ntype CalendarGridProps = ThemedClassName<{\n rows?: number;\n onSelect?: (event: { date: Date }) => void;\n}>;\n\nconst CalendarGrid = ({ classNames, rows, onSelect }: CalendarGridProps) => {\n const { weekStartsOn, event, setIndex, selected, setSelected } = 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 today = useMemo(() => new Date(), []);\n\n const [initialized, setInitialized] = useState(false);\n useEffect(() => {\n const index = differenceInWeeks(today, start);\n listRef.current?.scrollToRow(index);\n }, [initialized, start, today]);\n\n useEffect(() => {\n return event.on((event) => {\n switch (event.type) {\n case 'scroll': {\n const index = differenceInWeeks(event.date, start);\n listRef.current?.scrollToRow(index);\n break;\n }\n }\n });\n }, [event]);\n\n const days = useMemo(() => {\n const weekStart = startOfWeek(new Date(), { weekStartsOn });\n return Array.from({ length: 7 }, (_, i) => {\n const day = addDays(weekStart, i);\n return format(day, 'EEE'); // Short day name (Mon, Tue, etc.)\n });\n }, []);\n\n // TODO(burdon): Get info by range.\n // TODO(burdon): Border marker for \"all day events?\"\n const getNumAppointments = useCallback((_date: Date) => {\n // return Math.floor(Math.random() * 10);\n return 0;\n }, []);\n\n const handleDaySelect = useCallback(\n (date: Date) => {\n setSelected((current) => (isSameDay(date, current) ? undefined : date));\n onSelect?.({ date });\n },\n [onSelect],\n );\n\n const handleScroll = useCallback<NonNullable<ListProps['onScroll']>>((info) => {\n setIndex(Math.round(info.scrollTop / size));\n }, []);\n\n const rowRenderer = useCallback<ListRowRenderer>(\n ({ key, index, style }) => {\n const getBgColor = (date: Date) => date.getMonth() % 2 === 0 && 'bg-modalSurface';\n return (\n <div key={key} role='none' style={style} className='is-full grid grid-cols-[1fr_max-content_1fr] snap-center'>\n <div role='none' className={mx(getBgColor(getDate(start, index, 0, weekStartsOn)))} />\n <div role='none' className='grid grid-cols-7' style={{ gridTemplateColumns: `repeat(7, ${size}px)` }}>\n {Array.from({ length: 7 }).map((_, i) => {\n const date = getDate(start, index, i, weekStartsOn);\n const num = getNumAppointments(date);\n const border = isSameDay(date, selected)\n ? 'border-primary-500'\n : isSameDay(date, today)\n ? 'border-amber-500'\n : undefined;\n\n return (\n <div\n key={i}\n role='none'\n className={mx('relative flex justify-center items-center cursor-pointer', getBgColor(date))}\n onClick={() => handleDaySelect(date)}\n >\n <span className='text-description'>{date.getDate()}</span>\n {!border && date.getDate() === 1 && (\n <span className='absolute top-0 text-xs text-description'>{format(date, 'MMM')}</span>\n )}\n {border && (\n <div\n role='none'\n className={mx('absolute top-0 left-0 is-full bs-full border-2 rounded-full', border)}\n />\n )}\n {num > 0 && (\n <Icon\n classNames='absolute bottom-0'\n icon={num > 3 ? 'ph--dots-three--regular' : 'ph--dot--regular'}\n size={5}\n />\n )}\n </div>\n );\n })}\n </div>\n <div className={mx(getBgColor(getDate(start, index, 6, weekStartsOn)))} />\n </div>\n );\n },\n [handleDaySelect, getNumAppointments, selected, weekStartsOn],\n );\n\n return (\n <div role='none' className={mx('flex flex-col bs-full is-full justify-center overflow-hidden', classNames)}>\n {/* Day labels */}\n <div role='none' className='flex justify-center bg-groupSurface'>\n <div role='none' className='flex is-full grid grid-cols-7' style={{ width: defaultWidth }}>\n {days.map((date, i) => (\n <div key={i} role='none' className='flex justify-center p-2 text-sm font-thin'>\n {date}\n </div>\n ))}\n </div>\n </div>\n\n {/* Grid */}\n <div role='none' className='flex flex-col bs-full is-full justify-center overflow-hidden' ref={containerRef}>\n <List\n ref={listRef}\n role='none'\n // TODO(burdon): Snap isn't working.\n className='[&>div]:snap-y scrollbar-none outline-none'\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\nCalendarGrid.displayName = CALENDAR_GRID_NAME;\n\n//\n// Calendar\n//\n\nexport const Calendar = {\n Root: CalendarRoot,\n Viewport: CalendarViewport,\n Toolbar: CalendarToolbar,\n Grid: CalendarGrid,\n};\n\nexport type { CalendarController, CalendarRootProps, CalendarViewportProps, CalendarToolbarProps, CalendarGridProps };\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Resource } from '@dxos/react-ui';\n\nexport const translationKey = '@dxos/react-ui-calendar';\n\nexport const translations = [\n {\n 'en-US': {\n [translationKey]: {\n 'today button': 'Today',\n },\n },\n },\n] as const satisfies Resource[];\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Day } from 'date-fns';\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\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"],
|
|
5
|
-
"mappings": ";AAIA,SAASA,qBAAqB;AAC9B,SAAmBC,SAASC,mBAAmBC,QAAQC,mBAAmB;
|
|
6
|
-
"names": ["createContext", "addDays", "differenceInWeeks", "format", "startOfWeek", "React", "forwardRef", "useCallback", "useEffect", "useImperativeHandle", "useMemo", "useRef", "useState", "useResizeDetector", "List", "Event", "
|
|
3
|
+
"sources": ["../../../src/components/Calendar/Calendar.tsx", "../../../src/components/Calendar/util.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { createContext } from '@radix-ui/react-context';\nimport { type Day, addDays, differenceInWeeks, format, startOfDay, startOfWeek } from 'date-fns';\nimport React, {\n type Dispatch,\n type PropsWithChildren,\n type SetStateAction,\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, mx } from '@dxos/ui-theme';\n\nimport { translationKey } from '#translations';\n\nimport { getDate, isSameDay } from './util';\n\nconst maxRows = 50 * 100;\nconst start = new Date('1970-01-01');\nconst size = 48;\nconst defaultWidth = 7 * size;\n\n//\n// Context\n//\n\ntype CalendarEvent = {\n type: 'scroll';\n date: Date;\n};\n\ntype CalendarContextValue = {\n weekStartsOn: Day;\n event: Event<CalendarEvent>;\n index: number | undefined;\n setIndex: Dispatch<SetStateAction<number | undefined>>;\n selected: Date | undefined;\n setSelected: Dispatch<SetStateAction<Date | undefined>>;\n};\n\nconst [CalendarContextProvider, useCalendarContext] = createContext<CalendarContextValue>('Calendar');\n\n//\n// Controller\n//\n\ntype CalendarController = {\n scrollTo: (date: Date) => void;\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<CalendarEvent>(), []);\n const [selected, setSelected] = useState<Date | undefined>();\n const [index, setIndex] = useState<number | undefined>();\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n scrollTo: (date: Date) => {\n event.emit({ type: 'scroll', 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 >\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 style={{ width: defaultWidth }}\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// TODO(burdon): Key nav.\n// TODO(burdon): Drag range.\n//\n\nconst CALENDAR_GRID_NAME = 'CalendarGrid';\n\ntype CalendarGridProps = {\n rows?: number;\n /** Dates to highlight on the grid. Each date that appears in this array receives a border indicator. */\n dates?: Date[];\n onSelect?: (event: { date: Date }) => void;\n};\n\nconst CalendarGrid = composable<HTMLDivElement, CalendarGridProps>(\n ({ classNames, rows, dates = [], onSelect, ...props }, forwardedRef) => {\n const { weekStartsOn, event, setIndex, selected, setSelected } = 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 today = useMemo(() => new Date(), []);\n\n // Build a set of ISO date strings (YYYY-MM-DD) for O(1) per-cell lookup.\n const dateSet = useMemo(() => new Set(dates.map((date) => startOfDay(date).toISOString())), [dates]);\n\n const hasDate = useCallback((date: Date) => dateSet.has(startOfDay(date).toISOString()), [dateSet]);\n\n const [initialized, setInitialized] = useState(false);\n useEffect(() => {\n const index = differenceInWeeks(today, start);\n listRef.current?.scrollToRow(index);\n }, [initialized, start, today]);\n\n useEffect(() => {\n return event.on((event) => {\n switch (event.type) {\n case 'scroll': {\n const index = differenceInWeeks(event.date, start);\n listRef.current?.scrollToRow(index);\n break;\n }\n }\n });\n }, [event]);\n\n const days = useMemo(() => {\n const weekStart = startOfWeek(new Date(), { weekStartsOn });\n return Array.from({ length: 7 }, (_, i) => {\n const day = addDays(weekStart, i);\n return format(day, 'EEE'); // Short day name (Mon, Tue, etc.)\n });\n }, []);\n\n // TODO(burdon): Get info by range.\n\n const handleDaySelect = useCallback(\n (date: Date) => {\n setSelected((current) => (isSameDay(date, current) ? undefined : date));\n onSelect?.({ date });\n },\n [onSelect],\n );\n\n const handleScroll = useCallback<NonNullable<ListProps['onScroll']>>((info) => {\n setIndex(Math.round(info.scrollTop / size));\n }, []);\n\n const rowRenderer = useCallback<ListRowRenderer>(\n ({ key, index, style }) => {\n const getBgColor = (date: Date) => date.getMonth() % 2 === 0 && 'bg-modal-surface';\n return (\n <div key={key} role='none' style={style} className='grid'>\n <div\n role='none'\n className='grid grid-cols-7 bg-input-surface'\n style={{ gridTemplateColumns: `repeat(7, ${size}px)` }}\n >\n {Array.from({ length: 7 }).map((_, i) => {\n const date = getDate(start, index, i, weekStartsOn);\n const border = isSameDay(date, selected)\n ? 'border-primary-500'\n : isSameDay(date, today)\n ? 'border-amber-500'\n : hasDate(date)\n ? 'border-neutral-700 border-dashed'\n : undefined;\n\n return (\n <div\n key={i}\n role='none'\n className={mx('relative flex justify-center items-center cursor-pointer', getBgColor(date))}\n onClick={() => handleDaySelect(date)}\n >\n <span className='text-description'>{date.getDate()}</span>\n {!border && date.getDate() === 1 && (\n <span className='absolute top-0 text-xs text-description'>{format(date, 'MMM')}</span>\n )}\n {border && <div role='none' className={mx('absolute inset-1 border-2 rounded-full', border)} />}\n </div>\n );\n })}\n </div>\n </div>\n );\n },\n [handleDaySelect, hasDate, 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', classNames],\n })}\n ref={forwardedRef}\n >\n {/* Day of week labels */}\n <div role='none' className='grid w-full grid-cols-7' style={{ width: defaultWidth }}>\n {days.map((date, i) => (\n <div key={i} role='none' className='flex justify-center p-2 text-sm font-thin'>\n {date}\n </div>\n ))}\n </div>\n\n {/* Grid */}\n <div role='none' 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};\n\nexport type { CalendarController, CalendarRootProps, CalendarToolbarProps, CalendarGridProps };\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Day } from 'date-fns';\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\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"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["createContext", "addDays", "differenceInWeeks", "format", "startOfDay", "startOfWeek", "React", "forwardRef", "useCallback", "useEffect", "useImperativeHandle", "useMemo", "useRef", "useState", "useResizeDetector", "List", "Event", "IconButton", "useTranslation", "composable", "composableProps", "mx", "translationKey", "getDate", "start", "weekNumber", "dayOfWeek", "weekStartsOn", "result", "Date", "startDayOfWeek", "getDay", "adjustedStartDay", "setDate", "isSameDay", "date1", "date2", "getFullYear", "getMonth", "maxRows", "start", "Date", "size", "defaultWidth", "CalendarContextProvider", "useCalendarContext", "createContext", "CalendarRoot", "forwardRef", "children", "weekStartsOn", "forwardedRef", "event", "useMemo", "Event", "selected", "setSelected", "useState", "index", "setIndex", "useImperativeHandle", "scrollTo", "date", "emit", "type", "CALENDAR_TOOLBAR_NAME", "CalendarToolbar", "composable", "classNames", "props", "t", "useTranslation", "translationKey", "top", "getDate", "today", "handleToday", "useCallback", "div", "composableProps", "role", "ref", "style", "width", "className", "IconButton", "variant", "icon", "iconOnly", "label", "onClick", "format", "getFullYear", "displayName", "CALENDAR_GRID_NAME", "CalendarGrid", "rows", "dates", "onSelect", "containerRef", "height", "useResizeDetector", "maxHeight", "undefined", "listRef", "useRef", "dateSet", "Set", "map", "startOfDay", "toISOString", "hasDate", "has", "initialized", "setInitialized", "useEffect", "differenceInWeeks", "current", "scrollToRow", "on", "days", "weekStart", "startOfWeek", "Array", "from", "length", "_", "i", "day", "addDays", "handleDaySelect", "isSameDay", "handleScroll", "info", "Math", "round", "scrollTop", "rowRenderer", "key", "getBgColor", "getMonth", "gridTemplateColumns", "border", "mx", "span", "List", "rowCount", "rowHeight", "scrollToAlignment", "onScroll", "onRowsRendered", "Calendar", "Root", "Toolbar", "Grid"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"src/
|
|
1
|
+
{"inputs":{"src/components/Calendar/util.ts":{"bytes":2699,"imports":[],"format":"esm"},"src/components/Calendar/Calendar.tsx":{"bytes":29849,"imports":[{"path":"@radix-ui/react-context","kind":"import-statement","external":true},{"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/ui-theme","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"src/components/Calendar/util.ts","kind":"import-statement","original":"./util"}],"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":17699},"dist/lib/browser/index.mjs":{"imports":[{"path":"@radix-ui/react-context","kind":"import-statement","external":true},{"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/ui-theme","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true}],"exports":["Calendar"],"entryPoint":"src/index.ts","inputs":{"src/components/Calendar/Calendar.tsx":{"bytesInOutput":7434},"src/components/Calendar/util.ts":{"bytesInOutput":517},"src/components/Calendar/index.ts":{"bytesInOutput":0},"src/components/index.ts":{"bytesInOutput":0},"src/index.ts":{"bytesInOutput":0}},"bytes":8126},"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}}}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/translations.ts
|
|
2
|
+
var translationKey = "@dxos/react-ui-calendar";
|
|
3
|
+
var translations = [
|
|
4
|
+
{
|
|
5
|
+
"en-US": {
|
|
6
|
+
[translationKey]: {
|
|
7
|
+
"today.button": "Today"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
];
|
|
12
|
+
export {
|
|
13
|
+
translationKey,
|
|
14
|
+
translations
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=translations.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/translations.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Resource } from '@dxos/react-ui';\n\nexport const translationKey = '@dxos/react-ui-calendar';\n\nexport const translations = [\n {\n 'en-US': {\n [translationKey]: {\n 'today.button': 'Today',\n },\n },\n },\n] as const satisfies Resource[];\n"],
|
|
5
|
+
"mappings": ";AAMO,IAAMA,iBAAiB;AAEvB,IAAMC,eAAe;EAC1B;IACE,SAAS;MACP,CAACD,cAAAA,GAAiB;QAChB,gBAAgB;MAClB;IACF;EACF;;",
|
|
6
|
+
"names": ["translationKey", "translations"]
|
|
7
|
+
}
|
|
@@ -2,25 +2,14 @@ import { createRequire } from 'node:module';const require = createRequire(import
|
|
|
2
2
|
|
|
3
3
|
// src/components/Calendar/Calendar.tsx
|
|
4
4
|
import { createContext } from "@radix-ui/react-context";
|
|
5
|
-
import { addDays, differenceInWeeks, format, startOfWeek } from "date-fns";
|
|
5
|
+
import { addDays, differenceInWeeks, format, startOfDay, startOfWeek } from "date-fns";
|
|
6
6
|
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
7
7
|
import { useResizeDetector } from "react-resize-detector";
|
|
8
8
|
import { List } from "react-virtualized";
|
|
9
9
|
import { Event } from "@dxos/async";
|
|
10
|
-
import {
|
|
11
|
-
import { mx } from "@dxos/ui-theme";
|
|
12
|
-
|
|
13
|
-
// src/translations.ts
|
|
14
|
-
var translationKey = "@dxos/react-ui-calendar";
|
|
15
|
-
var translations = [
|
|
16
|
-
{
|
|
17
|
-
"en-US": {
|
|
18
|
-
[translationKey]: {
|
|
19
|
-
"today button": "Today"
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
];
|
|
10
|
+
import { IconButton, useTranslation } from "@dxos/react-ui";
|
|
11
|
+
import { composable, composableProps, mx } from "@dxos/ui-theme";
|
|
12
|
+
import { translationKey } from "#translations";
|
|
24
13
|
|
|
25
14
|
// src/components/Calendar/util.ts
|
|
26
15
|
var getDate = (start2, weekNumber, dayOfWeek, weekStartsOn) => {
|
|
@@ -63,16 +52,8 @@ var CalendarRoot = /* @__PURE__ */ forwardRef(({ children, weekStartsOn = 1 }, f
|
|
|
63
52
|
setSelected
|
|
64
53
|
}, children);
|
|
65
54
|
});
|
|
66
|
-
var CALENDAR_VIEWPORT_NAME = "CalendarContent";
|
|
67
|
-
var CalendarViewport = ({ children, classNames }) => {
|
|
68
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
69
|
-
role: "none",
|
|
70
|
-
className: mx("flex flex-col items-center overflow-hidden bg-inputSurface", classNames)
|
|
71
|
-
}, children);
|
|
72
|
-
};
|
|
73
|
-
CalendarViewport.displayName = CALENDAR_VIEWPORT_NAME;
|
|
74
55
|
var CALENDAR_TOOLBAR_NAME = "CalendarHeader";
|
|
75
|
-
var CalendarToolbar = ({ classNames }) => {
|
|
56
|
+
var CalendarToolbar = composable(({ classNames, ...props }, forwardedRef) => {
|
|
76
57
|
const { t } = useTranslation(translationKey);
|
|
77
58
|
const { weekStartsOn, event, index, selected } = useCalendarContext(CALENDAR_TOOLBAR_NAME);
|
|
78
59
|
const top = useMemo(() => getDate(start, index ?? 0, 6, weekStartsOn), [
|
|
@@ -91,8 +72,14 @@ var CalendarToolbar = ({ classNames }) => {
|
|
|
91
72
|
today
|
|
92
73
|
]);
|
|
93
74
|
return /* @__PURE__ */ React.createElement("div", {
|
|
94
|
-
|
|
95
|
-
|
|
75
|
+
...composableProps(props, {
|
|
76
|
+
role: "none",
|
|
77
|
+
classNames: [
|
|
78
|
+
"shrink-0 grid! grid-cols-3 items-center bg-toolbar-surface",
|
|
79
|
+
classNames
|
|
80
|
+
]
|
|
81
|
+
}),
|
|
82
|
+
ref: forwardedRef,
|
|
96
83
|
style: {
|
|
97
84
|
width: defaultWidth
|
|
98
85
|
}
|
|
@@ -100,26 +87,31 @@ var CalendarToolbar = ({ classNames }) => {
|
|
|
100
87
|
className: "flex justify-start"
|
|
101
88
|
}, /* @__PURE__ */ React.createElement(IconButton, {
|
|
102
89
|
variant: "ghost",
|
|
103
|
-
size: 5,
|
|
104
90
|
icon: "ph--calendar--regular",
|
|
105
91
|
iconOnly: true,
|
|
106
92
|
classNames: "aspect-square",
|
|
107
|
-
label: t("today
|
|
93
|
+
label: t("today.button"),
|
|
108
94
|
onClick: handleToday
|
|
109
95
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
110
96
|
className: "flex justify-center p-2 text-description"
|
|
111
97
|
}, format(selected ?? top, "MMMM")), /* @__PURE__ */ React.createElement("div", {
|
|
112
98
|
className: "flex justify-end p-2 text-description"
|
|
113
99
|
}, (selected ?? top).getFullYear()));
|
|
114
|
-
};
|
|
100
|
+
});
|
|
115
101
|
CalendarToolbar.displayName = CALENDAR_TOOLBAR_NAME;
|
|
116
102
|
var CALENDAR_GRID_NAME = "CalendarGrid";
|
|
117
|
-
var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
103
|
+
var CalendarGrid = composable(({ classNames, rows, dates = [], onSelect, ...props }, forwardedRef) => {
|
|
118
104
|
const { weekStartsOn, event, setIndex, selected, setSelected } = useCalendarContext(CALENDAR_GRID_NAME);
|
|
119
105
|
const { ref: containerRef, width = 0, height = 0 } = useResizeDetector();
|
|
120
106
|
const maxHeight = rows ? rows * size : void 0;
|
|
121
107
|
const listRef = useRef(null);
|
|
122
108
|
const today = useMemo(() => /* @__PURE__ */ new Date(), []);
|
|
109
|
+
const dateSet = useMemo(() => new Set(dates.map((date) => startOfDay(date).toISOString())), [
|
|
110
|
+
dates
|
|
111
|
+
]);
|
|
112
|
+
const hasDate = useCallback((date) => dateSet.has(startOfDay(date).toISOString()), [
|
|
113
|
+
dateSet
|
|
114
|
+
]);
|
|
123
115
|
const [initialized, setInitialized] = useState(false);
|
|
124
116
|
useEffect(() => {
|
|
125
117
|
const index = differenceInWeeks(today, start);
|
|
@@ -153,9 +145,6 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
153
145
|
return format(day, "EEE");
|
|
154
146
|
});
|
|
155
147
|
}, []);
|
|
156
|
-
const getNumAppointments = useCallback((_date) => {
|
|
157
|
-
return 0;
|
|
158
|
-
}, []);
|
|
159
148
|
const handleDaySelect = useCallback((date) => {
|
|
160
149
|
setSelected((current) => isSameDay(date, current) ? void 0 : date);
|
|
161
150
|
onSelect?.({
|
|
@@ -168,18 +157,15 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
168
157
|
setIndex(Math.round(info.scrollTop / size));
|
|
169
158
|
}, []);
|
|
170
159
|
const rowRenderer = useCallback(({ key, index, style }) => {
|
|
171
|
-
const getBgColor = (date) => date.getMonth() % 2 === 0 && "bg-
|
|
160
|
+
const getBgColor = (date) => date.getMonth() % 2 === 0 && "bg-modal-surface";
|
|
172
161
|
return /* @__PURE__ */ React.createElement("div", {
|
|
173
162
|
key,
|
|
174
163
|
role: "none",
|
|
175
164
|
style,
|
|
176
|
-
className: "
|
|
165
|
+
className: "grid"
|
|
177
166
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
178
167
|
role: "none",
|
|
179
|
-
className:
|
|
180
|
-
}), /* @__PURE__ */ React.createElement("div", {
|
|
181
|
-
role: "none",
|
|
182
|
-
className: "grid grid-cols-7",
|
|
168
|
+
className: "grid grid-cols-7 bg-input-surface",
|
|
183
169
|
style: {
|
|
184
170
|
gridTemplateColumns: `repeat(7, ${size}px)`
|
|
185
171
|
}
|
|
@@ -187,8 +173,7 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
187
173
|
length: 7
|
|
188
174
|
}).map((_, i) => {
|
|
189
175
|
const date = getDate(start, index, i, weekStartsOn);
|
|
190
|
-
const
|
|
191
|
-
const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : void 0;
|
|
176
|
+
const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : hasDate(date) ? "border-neutral-700 border-dashed" : void 0;
|
|
192
177
|
return /* @__PURE__ */ React.createElement("div", {
|
|
193
178
|
key: i,
|
|
194
179
|
role: "none",
|
|
@@ -200,30 +185,27 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
200
185
|
className: "absolute top-0 text-xs text-description"
|
|
201
186
|
}, format(date, "MMM")), border && /* @__PURE__ */ React.createElement("div", {
|
|
202
187
|
role: "none",
|
|
203
|
-
className: mx("absolute
|
|
204
|
-
}), num > 0 && /* @__PURE__ */ React.createElement(Icon, {
|
|
205
|
-
classNames: "absolute bottom-0",
|
|
206
|
-
icon: num > 3 ? "ph--dots-three--regular" : "ph--dot--regular",
|
|
207
|
-
size: 5
|
|
188
|
+
className: mx("absolute inset-1 border-2 rounded-full", border)
|
|
208
189
|
}));
|
|
209
|
-
}))
|
|
210
|
-
className: mx(getBgColor(getDate(start, index, 6, weekStartsOn)))
|
|
211
|
-
}));
|
|
190
|
+
})));
|
|
212
191
|
}, [
|
|
213
192
|
handleDaySelect,
|
|
214
|
-
|
|
193
|
+
hasDate,
|
|
215
194
|
selected,
|
|
216
195
|
weekStartsOn
|
|
217
196
|
]);
|
|
218
197
|
return /* @__PURE__ */ React.createElement("div", {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
198
|
+
...composableProps(props, {
|
|
199
|
+
role: "none",
|
|
200
|
+
classNames: [
|
|
201
|
+
"flex flex-col h-full w-full justify-center overflow-hidden",
|
|
202
|
+
classNames
|
|
203
|
+
]
|
|
204
|
+
}),
|
|
205
|
+
ref: forwardedRef
|
|
224
206
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
225
207
|
role: "none",
|
|
226
|
-
className: "
|
|
208
|
+
className: "grid w-full grid-cols-7",
|
|
227
209
|
style: {
|
|
228
210
|
width: defaultWidth
|
|
229
211
|
}
|
|
@@ -231,15 +213,14 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
231
213
|
key: i,
|
|
232
214
|
role: "none",
|
|
233
215
|
className: "flex justify-center p-2 text-sm font-thin"
|
|
234
|
-
}, date)))
|
|
216
|
+
}, date))), /* @__PURE__ */ React.createElement("div", {
|
|
235
217
|
role: "none",
|
|
236
|
-
className: "flex flex-col
|
|
218
|
+
className: "flex flex-col h-full w-full justify-center overflow-hidden",
|
|
237
219
|
ref: containerRef
|
|
238
220
|
}, /* @__PURE__ */ React.createElement(List, {
|
|
239
221
|
ref: listRef,
|
|
240
222
|
role: "none",
|
|
241
|
-
|
|
242
|
-
className: "[&>div]:snap-y scrollbar-none outline-none",
|
|
223
|
+
className: "scrollbar-none outline-hidden",
|
|
243
224
|
width,
|
|
244
225
|
height: maxHeight ?? height,
|
|
245
226
|
rowCount: maxRows,
|
|
@@ -249,11 +230,10 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
249
230
|
onScroll: handleScroll,
|
|
250
231
|
onRowsRendered: () => setInitialized(true)
|
|
251
232
|
})));
|
|
252
|
-
};
|
|
233
|
+
});
|
|
253
234
|
CalendarGrid.displayName = CALENDAR_GRID_NAME;
|
|
254
235
|
var Calendar = {
|
|
255
236
|
Root: CalendarRoot,
|
|
256
|
-
Viewport: CalendarViewport,
|
|
257
237
|
Toolbar: CalendarToolbar,
|
|
258
238
|
Grid: CalendarGrid
|
|
259
239
|
};
|