@dxos/react-ui-calendar 0.8.4-main.3eb6e50203 → 0.8.4-main.3fbcb4aa9b
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 -69
- 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 -69
- 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 +134 -158
- 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,13 @@ 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
|
-
role: "none",
|
|
173
161
|
style,
|
|
174
|
-
className: "
|
|
162
|
+
className: "grid"
|
|
175
163
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
176
|
-
|
|
177
|
-
className: mx(getBgColor(getDate(start, index, 0, weekStartsOn)))
|
|
178
|
-
}), /* @__PURE__ */ React.createElement("div", {
|
|
179
|
-
role: "none",
|
|
180
|
-
className: "grid grid-cols-7",
|
|
164
|
+
className: "grid grid-cols-7 bg-input-surface",
|
|
181
165
|
style: {
|
|
182
166
|
gridTemplateColumns: `repeat(7, ${size}px)`
|
|
183
167
|
}
|
|
@@ -185,11 +169,9 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
185
169
|
length: 7
|
|
186
170
|
}).map((_, i) => {
|
|
187
171
|
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;
|
|
172
|
+
const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : hasDate(date) ? "border-neutral-700 border-dashed" : void 0;
|
|
190
173
|
return /* @__PURE__ */ React.createElement("div", {
|
|
191
174
|
key: i,
|
|
192
|
-
role: "none",
|
|
193
175
|
className: mx("relative flex justify-center items-center cursor-pointer", getBgColor(date)),
|
|
194
176
|
onClick: () => handleDaySelect(date)
|
|
195
177
|
}, /* @__PURE__ */ React.createElement("span", {
|
|
@@ -197,47 +179,39 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
197
179
|
}, date.getDate()), !border && date.getDate() === 1 && /* @__PURE__ */ React.createElement("span", {
|
|
198
180
|
className: "absolute top-0 text-xs text-description"
|
|
199
181
|
}, format(date, "MMM")), border && /* @__PURE__ */ React.createElement("div", {
|
|
200
|
-
|
|
201
|
-
className: mx("absolute top-0 left-0 is-full bs-full border-2 rounded-full", border)
|
|
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
|
|
182
|
+
className: mx("absolute inset-1 border-2 rounded-full", border)
|
|
206
183
|
}));
|
|
207
|
-
}))
|
|
208
|
-
className: mx(getBgColor(getDate(start, index, 6, weekStartsOn)))
|
|
209
|
-
}));
|
|
184
|
+
})));
|
|
210
185
|
}, [
|
|
211
186
|
handleDaySelect,
|
|
212
|
-
|
|
187
|
+
hasDate,
|
|
213
188
|
selected,
|
|
214
189
|
weekStartsOn
|
|
215
190
|
]);
|
|
216
191
|
return /* @__PURE__ */ React.createElement("div", {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
192
|
+
...composableProps(props, {
|
|
193
|
+
role: "none",
|
|
194
|
+
classNames: [
|
|
195
|
+
"flex flex-col h-full w-full justify-center overflow-hidden",
|
|
196
|
+
classNames
|
|
197
|
+
]
|
|
198
|
+
}),
|
|
199
|
+
ref: forwardedRef
|
|
222
200
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
223
|
-
|
|
224
|
-
className: "flex is-full grid grid-cols-7",
|
|
201
|
+
className: "grid w-full grid-cols-7",
|
|
225
202
|
style: {
|
|
226
203
|
width: defaultWidth
|
|
227
204
|
}
|
|
228
205
|
}, days.map((date, i) => /* @__PURE__ */ React.createElement("div", {
|
|
229
206
|
key: i,
|
|
230
|
-
role: "none",
|
|
231
207
|
className: "flex justify-center p-2 text-sm font-thin"
|
|
232
|
-
}, date)))
|
|
233
|
-
|
|
234
|
-
className: "flex flex-col bs-full is-full justify-center overflow-hidden",
|
|
208
|
+
}, date))), /* @__PURE__ */ React.createElement("div", {
|
|
209
|
+
className: "flex flex-col h-full w-full justify-center overflow-hidden",
|
|
235
210
|
ref: containerRef
|
|
236
211
|
}, /* @__PURE__ */ React.createElement(List, {
|
|
237
212
|
ref: listRef,
|
|
238
213
|
role: "none",
|
|
239
|
-
|
|
240
|
-
className: "[&>div]:snap-y scrollbar-none outline-none",
|
|
214
|
+
className: "scrollbar-none outline-hidden",
|
|
241
215
|
width,
|
|
242
216
|
height: maxHeight ?? height,
|
|
243
217
|
rowCount: maxRows,
|
|
@@ -247,11 +221,10 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
247
221
|
onScroll: handleScroll,
|
|
248
222
|
onRowsRendered: () => setInitialized(true)
|
|
249
223
|
})));
|
|
250
|
-
};
|
|
224
|
+
});
|
|
251
225
|
CalendarGrid.displayName = CALENDAR_GRID_NAME;
|
|
252
226
|
var Calendar = {
|
|
253
227
|
Root: CalendarRoot,
|
|
254
|
-
Viewport: CalendarViewport,
|
|
255
228
|
Toolbar: CalendarToolbar,
|
|
256
229
|
Grid: CalendarGrid
|
|
257
230
|
};
|
|
@@ -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} 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 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 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 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 className='grid w-full grid-cols-7' style={{ width: defaultWidth }}>\n {days.map((date, i) => (\n <div key={i} className='flex justify-center p-2 text-sm font-thin'>\n {date}\n </div>\n ))}\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};\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":29347,"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":17459},"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":7296},"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":7988},"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,13 @@ 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
|
-
role: "none",
|
|
175
163
|
style,
|
|
176
|
-
className: "
|
|
164
|
+
className: "grid"
|
|
177
165
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
178
|
-
|
|
179
|
-
className: mx(getBgColor(getDate(start, index, 0, weekStartsOn)))
|
|
180
|
-
}), /* @__PURE__ */ React.createElement("div", {
|
|
181
|
-
role: "none",
|
|
182
|
-
className: "grid grid-cols-7",
|
|
166
|
+
className: "grid grid-cols-7 bg-input-surface",
|
|
183
167
|
style: {
|
|
184
168
|
gridTemplateColumns: `repeat(7, ${size}px)`
|
|
185
169
|
}
|
|
@@ -187,11 +171,9 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
187
171
|
length: 7
|
|
188
172
|
}).map((_, i) => {
|
|
189
173
|
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;
|
|
174
|
+
const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : hasDate(date) ? "border-neutral-700 border-dashed" : void 0;
|
|
192
175
|
return /* @__PURE__ */ React.createElement("div", {
|
|
193
176
|
key: i,
|
|
194
|
-
role: "none",
|
|
195
177
|
className: mx("relative flex justify-center items-center cursor-pointer", getBgColor(date)),
|
|
196
178
|
onClick: () => handleDaySelect(date)
|
|
197
179
|
}, /* @__PURE__ */ React.createElement("span", {
|
|
@@ -199,47 +181,39 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
199
181
|
}, date.getDate()), !border && date.getDate() === 1 && /* @__PURE__ */ React.createElement("span", {
|
|
200
182
|
className: "absolute top-0 text-xs text-description"
|
|
201
183
|
}, format(date, "MMM")), border && /* @__PURE__ */ React.createElement("div", {
|
|
202
|
-
|
|
203
|
-
className: mx("absolute top-0 left-0 is-full bs-full border-2 rounded-full", border)
|
|
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
|
|
184
|
+
className: mx("absolute inset-1 border-2 rounded-full", border)
|
|
208
185
|
}));
|
|
209
|
-
}))
|
|
210
|
-
className: mx(getBgColor(getDate(start, index, 6, weekStartsOn)))
|
|
211
|
-
}));
|
|
186
|
+
})));
|
|
212
187
|
}, [
|
|
213
188
|
handleDaySelect,
|
|
214
|
-
|
|
189
|
+
hasDate,
|
|
215
190
|
selected,
|
|
216
191
|
weekStartsOn
|
|
217
192
|
]);
|
|
218
193
|
return /* @__PURE__ */ React.createElement("div", {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
194
|
+
...composableProps(props, {
|
|
195
|
+
role: "none",
|
|
196
|
+
classNames: [
|
|
197
|
+
"flex flex-col h-full w-full justify-center overflow-hidden",
|
|
198
|
+
classNames
|
|
199
|
+
]
|
|
200
|
+
}),
|
|
201
|
+
ref: forwardedRef
|
|
224
202
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
225
|
-
|
|
226
|
-
className: "flex is-full grid grid-cols-7",
|
|
203
|
+
className: "grid w-full grid-cols-7",
|
|
227
204
|
style: {
|
|
228
205
|
width: defaultWidth
|
|
229
206
|
}
|
|
230
207
|
}, days.map((date, i) => /* @__PURE__ */ React.createElement("div", {
|
|
231
208
|
key: i,
|
|
232
|
-
role: "none",
|
|
233
209
|
className: "flex justify-center p-2 text-sm font-thin"
|
|
234
|
-
}, date)))
|
|
235
|
-
|
|
236
|
-
className: "flex flex-col bs-full is-full justify-center overflow-hidden",
|
|
210
|
+
}, date))), /* @__PURE__ */ React.createElement("div", {
|
|
211
|
+
className: "flex flex-col h-full w-full justify-center overflow-hidden",
|
|
237
212
|
ref: containerRef
|
|
238
213
|
}, /* @__PURE__ */ React.createElement(List, {
|
|
239
214
|
ref: listRef,
|
|
240
215
|
role: "none",
|
|
241
|
-
|
|
242
|
-
className: "[&>div]:snap-y scrollbar-none outline-none",
|
|
216
|
+
className: "scrollbar-none outline-hidden",
|
|
243
217
|
width,
|
|
244
218
|
height: maxHeight ?? height,
|
|
245
219
|
rowCount: maxRows,
|
|
@@ -249,11 +223,10 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
249
223
|
onScroll: handleScroll,
|
|
250
224
|
onRowsRendered: () => setInitialized(true)
|
|
251
225
|
})));
|
|
252
|
-
};
|
|
226
|
+
});
|
|
253
227
|
CalendarGrid.displayName = CALENDAR_GRID_NAME;
|
|
254
228
|
var Calendar = {
|
|
255
229
|
Root: CalendarRoot,
|
|
256
|
-
Viewport: CalendarViewport,
|
|
257
230
|
Toolbar: CalendarToolbar,
|
|
258
231
|
Grid: CalendarGrid
|
|
259
232
|
};
|