@dxos/react-ui-calendar 0.8.4-main.ef1bc66f44 → 0.8.4-main.fcfe5033a5
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 -51
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +42 -51
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- 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/translations.d.ts +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -17
- package/src/components/Calendar/Calendar.stories.tsx +14 -40
- package/src/components/Calendar/Calendar.tsx +137 -157
- package/src/translations.ts +1 -1
|
@@ -1,12 +1,12 @@
|
|
|
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";
|
|
8
|
+
import { IconButton, useTranslation } from "@dxos/react-ui";
|
|
9
|
+
import { composable, composableProps, mx } from "@dxos/ui-theme";
|
|
10
10
|
|
|
11
11
|
// src/translations.ts
|
|
12
12
|
var translationKey = "@dxos/react-ui-calendar";
|
|
@@ -14,7 +14,7 @@ var translations = [
|
|
|
14
14
|
{
|
|
15
15
|
"en-US": {
|
|
16
16
|
[translationKey]: {
|
|
17
|
-
"today
|
|
17
|
+
"today.button": "Today"
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -61,16 +61,8 @@ var CalendarRoot = /* @__PURE__ */ forwardRef(({ children, weekStartsOn = 1 }, f
|
|
|
61
61
|
setSelected
|
|
62
62
|
}, children);
|
|
63
63
|
});
|
|
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
64
|
var CALENDAR_TOOLBAR_NAME = "CalendarHeader";
|
|
73
|
-
var CalendarToolbar = ({ classNames }) => {
|
|
65
|
+
var CalendarToolbar = composable(({ classNames, ...props }, forwardedRef) => {
|
|
74
66
|
const { t } = useTranslation(translationKey);
|
|
75
67
|
const { weekStartsOn, event, index, selected } = useCalendarContext(CALENDAR_TOOLBAR_NAME);
|
|
76
68
|
const top = useMemo(() => getDate(start, index ?? 0, 6, weekStartsOn), [
|
|
@@ -89,8 +81,14 @@ var CalendarToolbar = ({ classNames }) => {
|
|
|
89
81
|
today
|
|
90
82
|
]);
|
|
91
83
|
return /* @__PURE__ */ React.createElement("div", {
|
|
92
|
-
|
|
93
|
-
|
|
84
|
+
...composableProps(props, {
|
|
85
|
+
role: "none",
|
|
86
|
+
classNames: [
|
|
87
|
+
"shrink-0 grid! grid-cols-3 items-center bg-toolbar-surface",
|
|
88
|
+
classNames
|
|
89
|
+
]
|
|
90
|
+
}),
|
|
91
|
+
ref: forwardedRef,
|
|
94
92
|
style: {
|
|
95
93
|
width: defaultWidth
|
|
96
94
|
}
|
|
@@ -98,26 +96,31 @@ var CalendarToolbar = ({ classNames }) => {
|
|
|
98
96
|
className: "flex justify-start"
|
|
99
97
|
}, /* @__PURE__ */ React.createElement(IconButton, {
|
|
100
98
|
variant: "ghost",
|
|
101
|
-
size: 5,
|
|
102
99
|
icon: "ph--calendar--regular",
|
|
103
100
|
iconOnly: true,
|
|
104
101
|
classNames: "aspect-square",
|
|
105
|
-
label: t("today
|
|
102
|
+
label: t("today.button"),
|
|
106
103
|
onClick: handleToday
|
|
107
104
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
108
105
|
className: "flex justify-center p-2 text-description"
|
|
109
106
|
}, format(selected ?? top, "MMMM")), /* @__PURE__ */ React.createElement("div", {
|
|
110
107
|
className: "flex justify-end p-2 text-description"
|
|
111
108
|
}, (selected ?? top).getFullYear()));
|
|
112
|
-
};
|
|
109
|
+
});
|
|
113
110
|
CalendarToolbar.displayName = CALENDAR_TOOLBAR_NAME;
|
|
114
111
|
var CALENDAR_GRID_NAME = "CalendarGrid";
|
|
115
|
-
var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
112
|
+
var CalendarGrid = composable(({ classNames, rows, dates = [], onSelect, ...props }, forwardedRef) => {
|
|
116
113
|
const { weekStartsOn, event, setIndex, selected, setSelected } = useCalendarContext(CALENDAR_GRID_NAME);
|
|
117
114
|
const { ref: containerRef, width = 0, height = 0 } = useResizeDetector();
|
|
118
115
|
const maxHeight = rows ? rows * size : void 0;
|
|
119
116
|
const listRef = useRef(null);
|
|
120
117
|
const today = useMemo(() => /* @__PURE__ */ new Date(), []);
|
|
118
|
+
const dateSet = useMemo(() => new Set(dates.map((date) => startOfDay(date).toISOString())), [
|
|
119
|
+
dates
|
|
120
|
+
]);
|
|
121
|
+
const hasDate = useCallback((date) => dateSet.has(startOfDay(date).toISOString()), [
|
|
122
|
+
dateSet
|
|
123
|
+
]);
|
|
121
124
|
const [initialized, setInitialized] = useState(false);
|
|
122
125
|
useEffect(() => {
|
|
123
126
|
const index = differenceInWeeks(today, start);
|
|
@@ -151,9 +154,6 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
151
154
|
return format(day, "EEE");
|
|
152
155
|
});
|
|
153
156
|
}, []);
|
|
154
|
-
const getNumAppointments = useCallback((_date) => {
|
|
155
|
-
return 0;
|
|
156
|
-
}, []);
|
|
157
157
|
const handleDaySelect = useCallback((date) => {
|
|
158
158
|
setSelected((current) => isSameDay(date, current) ? void 0 : date);
|
|
159
159
|
onSelect?.({
|
|
@@ -166,18 +166,15 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
166
166
|
setIndex(Math.round(info.scrollTop / size));
|
|
167
167
|
}, []);
|
|
168
168
|
const rowRenderer = useCallback(({ key, index, style }) => {
|
|
169
|
-
const getBgColor = (date) => date.getMonth() % 2 === 0 && "bg-
|
|
169
|
+
const getBgColor = (date) => date.getMonth() % 2 === 0 && "bg-modal-surface";
|
|
170
170
|
return /* @__PURE__ */ React.createElement("div", {
|
|
171
171
|
key,
|
|
172
172
|
role: "none",
|
|
173
173
|
style,
|
|
174
|
-
className: "
|
|
174
|
+
className: "grid"
|
|
175
175
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
176
176
|
role: "none",
|
|
177
|
-
className:
|
|
178
|
-
}), /* @__PURE__ */ React.createElement("div", {
|
|
179
|
-
role: "none",
|
|
180
|
-
className: "grid grid-cols-7",
|
|
177
|
+
className: "grid grid-cols-7 bg-input-surface",
|
|
181
178
|
style: {
|
|
182
179
|
gridTemplateColumns: `repeat(7, ${size}px)`
|
|
183
180
|
}
|
|
@@ -185,8 +182,7 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
185
182
|
length: 7
|
|
186
183
|
}).map((_, i) => {
|
|
187
184
|
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;
|
|
185
|
+
const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : hasDate(date) ? "border-neutral-700 border-dashed" : void 0;
|
|
190
186
|
return /* @__PURE__ */ React.createElement("div", {
|
|
191
187
|
key: i,
|
|
192
188
|
role: "none",
|
|
@@ -198,30 +194,27 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
198
194
|
className: "absolute top-0 text-xs text-description"
|
|
199
195
|
}, format(date, "MMM")), border && /* @__PURE__ */ React.createElement("div", {
|
|
200
196
|
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
|
|
197
|
+
className: mx("absolute inset-1 border-2 rounded-full", border)
|
|
206
198
|
}));
|
|
207
|
-
}))
|
|
208
|
-
className: mx(getBgColor(getDate(start, index, 6, weekStartsOn)))
|
|
209
|
-
}));
|
|
199
|
+
})));
|
|
210
200
|
}, [
|
|
211
201
|
handleDaySelect,
|
|
212
|
-
|
|
202
|
+
hasDate,
|
|
213
203
|
selected,
|
|
214
204
|
weekStartsOn
|
|
215
205
|
]);
|
|
216
206
|
return /* @__PURE__ */ React.createElement("div", {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
207
|
+
...composableProps(props, {
|
|
208
|
+
role: "none",
|
|
209
|
+
classNames: [
|
|
210
|
+
"flex flex-col h-full w-full justify-center overflow-hidden",
|
|
211
|
+
classNames
|
|
212
|
+
]
|
|
213
|
+
}),
|
|
214
|
+
ref: forwardedRef
|
|
222
215
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
223
216
|
role: "none",
|
|
224
|
-
className: "
|
|
217
|
+
className: "grid w-full grid-cols-7",
|
|
225
218
|
style: {
|
|
226
219
|
width: defaultWidth
|
|
227
220
|
}
|
|
@@ -229,15 +222,14 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
229
222
|
key: i,
|
|
230
223
|
role: "none",
|
|
231
224
|
className: "flex justify-center p-2 text-sm font-thin"
|
|
232
|
-
}, date)))
|
|
225
|
+
}, date))), /* @__PURE__ */ React.createElement("div", {
|
|
233
226
|
role: "none",
|
|
234
|
-
className: "flex flex-col
|
|
227
|
+
className: "flex flex-col h-full w-full justify-center overflow-hidden",
|
|
235
228
|
ref: containerRef
|
|
236
229
|
}, /* @__PURE__ */ React.createElement(List, {
|
|
237
230
|
ref: listRef,
|
|
238
231
|
role: "none",
|
|
239
|
-
|
|
240
|
-
className: "[&>div]:snap-y scrollbar-none outline-none",
|
|
232
|
+
className: "scrollbar-none outline-hidden",
|
|
241
233
|
width,
|
|
242
234
|
height: maxHeight ?? height,
|
|
243
235
|
rowCount: maxRows,
|
|
@@ -247,11 +239,10 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
247
239
|
onScroll: handleScroll,
|
|
248
240
|
onRowsRendered: () => setInitialized(true)
|
|
249
241
|
})));
|
|
250
|
-
};
|
|
242
|
+
});
|
|
251
243
|
CalendarGrid.displayName = CALENDAR_GRID_NAME;
|
|
252
244
|
var Calendar = {
|
|
253
245
|
Root: CalendarRoot,
|
|
254
|
-
Viewport: CalendarViewport,
|
|
255
246
|
Toolbar: CalendarToolbar,
|
|
256
247
|
Grid: CalendarGrid
|
|
257
248
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/Calendar/Calendar.tsx", "../../../src/translations.ts", "../../../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, 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", "
|
|
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';\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 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": "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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", "translations", "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/translations.ts":{"bytes":1196,"imports":[],"format":"esm"},"src/components/Calendar/util.ts":{"bytes":2802,"imports":[],"format":"esm"},"src/components/Calendar/Calendar.tsx":{"bytes":
|
|
1
|
+
{"inputs":{"src/translations.ts":{"bytes":1196,"imports":[],"format":"esm"},"src/components/Calendar/util.ts":{"bytes":2802,"imports":[],"format":"esm"},"src/components/Calendar/Calendar.tsx":{"bytes":29966,"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":"src/translations.ts","kind":"import-statement","original":"../../translations"},{"path":"src/components/Calendar/util.ts","kind":"import-statement","original":"./util"}],"format":"esm"},"src/components/Calendar/index.ts":{"bytes":476,"imports":[{"path":"src/components/Calendar/Calendar.tsx","kind":"import-statement","original":"./Calendar"}],"format":"esm"},"src/components/index.ts":{"bytes":467,"imports":[{"path":"src/components/Calendar/index.ts","kind":"import-statement","original":"./Calendar"}],"format":"esm"},"src/index.ts":{"bytes":462,"imports":[{"path":"src/components/index.ts","kind":"import-statement","original":"./components"}],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18161},"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}],"exports":["Calendar"],"entryPoint":"src/index.ts","inputs":{"src/components/Calendar/Calendar.tsx":{"bytesInOutput":7386},"src/translations.ts":{"bytesInOutput":167},"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":8269}}}
|
|
@@ -2,13 +2,13 @@ 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";
|
|
10
|
+
import { IconButton, useTranslation } from "@dxos/react-ui";
|
|
11
|
+
import { composable, composableProps, mx } from "@dxos/ui-theme";
|
|
12
12
|
|
|
13
13
|
// src/translations.ts
|
|
14
14
|
var translationKey = "@dxos/react-ui-calendar";
|
|
@@ -16,7 +16,7 @@ var translations = [
|
|
|
16
16
|
{
|
|
17
17
|
"en-US": {
|
|
18
18
|
[translationKey]: {
|
|
19
|
-
"today
|
|
19
|
+
"today.button": "Today"
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -63,16 +63,8 @@ var CalendarRoot = /* @__PURE__ */ forwardRef(({ children, weekStartsOn = 1 }, f
|
|
|
63
63
|
setSelected
|
|
64
64
|
}, children);
|
|
65
65
|
});
|
|
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
66
|
var CALENDAR_TOOLBAR_NAME = "CalendarHeader";
|
|
75
|
-
var CalendarToolbar = ({ classNames }) => {
|
|
67
|
+
var CalendarToolbar = composable(({ classNames, ...props }, forwardedRef) => {
|
|
76
68
|
const { t } = useTranslation(translationKey);
|
|
77
69
|
const { weekStartsOn, event, index, selected } = useCalendarContext(CALENDAR_TOOLBAR_NAME);
|
|
78
70
|
const top = useMemo(() => getDate(start, index ?? 0, 6, weekStartsOn), [
|
|
@@ -91,8 +83,14 @@ var CalendarToolbar = ({ classNames }) => {
|
|
|
91
83
|
today
|
|
92
84
|
]);
|
|
93
85
|
return /* @__PURE__ */ React.createElement("div", {
|
|
94
|
-
|
|
95
|
-
|
|
86
|
+
...composableProps(props, {
|
|
87
|
+
role: "none",
|
|
88
|
+
classNames: [
|
|
89
|
+
"shrink-0 grid! grid-cols-3 items-center bg-toolbar-surface",
|
|
90
|
+
classNames
|
|
91
|
+
]
|
|
92
|
+
}),
|
|
93
|
+
ref: forwardedRef,
|
|
96
94
|
style: {
|
|
97
95
|
width: defaultWidth
|
|
98
96
|
}
|
|
@@ -100,26 +98,31 @@ var CalendarToolbar = ({ classNames }) => {
|
|
|
100
98
|
className: "flex justify-start"
|
|
101
99
|
}, /* @__PURE__ */ React.createElement(IconButton, {
|
|
102
100
|
variant: "ghost",
|
|
103
|
-
size: 5,
|
|
104
101
|
icon: "ph--calendar--regular",
|
|
105
102
|
iconOnly: true,
|
|
106
103
|
classNames: "aspect-square",
|
|
107
|
-
label: t("today
|
|
104
|
+
label: t("today.button"),
|
|
108
105
|
onClick: handleToday
|
|
109
106
|
})), /* @__PURE__ */ React.createElement("div", {
|
|
110
107
|
className: "flex justify-center p-2 text-description"
|
|
111
108
|
}, format(selected ?? top, "MMMM")), /* @__PURE__ */ React.createElement("div", {
|
|
112
109
|
className: "flex justify-end p-2 text-description"
|
|
113
110
|
}, (selected ?? top).getFullYear()));
|
|
114
|
-
};
|
|
111
|
+
});
|
|
115
112
|
CalendarToolbar.displayName = CALENDAR_TOOLBAR_NAME;
|
|
116
113
|
var CALENDAR_GRID_NAME = "CalendarGrid";
|
|
117
|
-
var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
114
|
+
var CalendarGrid = composable(({ classNames, rows, dates = [], onSelect, ...props }, forwardedRef) => {
|
|
118
115
|
const { weekStartsOn, event, setIndex, selected, setSelected } = useCalendarContext(CALENDAR_GRID_NAME);
|
|
119
116
|
const { ref: containerRef, width = 0, height = 0 } = useResizeDetector();
|
|
120
117
|
const maxHeight = rows ? rows * size : void 0;
|
|
121
118
|
const listRef = useRef(null);
|
|
122
119
|
const today = useMemo(() => /* @__PURE__ */ new Date(), []);
|
|
120
|
+
const dateSet = useMemo(() => new Set(dates.map((date) => startOfDay(date).toISOString())), [
|
|
121
|
+
dates
|
|
122
|
+
]);
|
|
123
|
+
const hasDate = useCallback((date) => dateSet.has(startOfDay(date).toISOString()), [
|
|
124
|
+
dateSet
|
|
125
|
+
]);
|
|
123
126
|
const [initialized, setInitialized] = useState(false);
|
|
124
127
|
useEffect(() => {
|
|
125
128
|
const index = differenceInWeeks(today, start);
|
|
@@ -153,9 +156,6 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
153
156
|
return format(day, "EEE");
|
|
154
157
|
});
|
|
155
158
|
}, []);
|
|
156
|
-
const getNumAppointments = useCallback((_date) => {
|
|
157
|
-
return 0;
|
|
158
|
-
}, []);
|
|
159
159
|
const handleDaySelect = useCallback((date) => {
|
|
160
160
|
setSelected((current) => isSameDay(date, current) ? void 0 : date);
|
|
161
161
|
onSelect?.({
|
|
@@ -168,18 +168,15 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
168
168
|
setIndex(Math.round(info.scrollTop / size));
|
|
169
169
|
}, []);
|
|
170
170
|
const rowRenderer = useCallback(({ key, index, style }) => {
|
|
171
|
-
const getBgColor = (date) => date.getMonth() % 2 === 0 && "bg-
|
|
171
|
+
const getBgColor = (date) => date.getMonth() % 2 === 0 && "bg-modal-surface";
|
|
172
172
|
return /* @__PURE__ */ React.createElement("div", {
|
|
173
173
|
key,
|
|
174
174
|
role: "none",
|
|
175
175
|
style,
|
|
176
|
-
className: "
|
|
176
|
+
className: "grid"
|
|
177
177
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
178
178
|
role: "none",
|
|
179
|
-
className:
|
|
180
|
-
}), /* @__PURE__ */ React.createElement("div", {
|
|
181
|
-
role: "none",
|
|
182
|
-
className: "grid grid-cols-7",
|
|
179
|
+
className: "grid grid-cols-7 bg-input-surface",
|
|
183
180
|
style: {
|
|
184
181
|
gridTemplateColumns: `repeat(7, ${size}px)`
|
|
185
182
|
}
|
|
@@ -187,8 +184,7 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
187
184
|
length: 7
|
|
188
185
|
}).map((_, i) => {
|
|
189
186
|
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;
|
|
187
|
+
const border = isSameDay(date, selected) ? "border-primary-500" : isSameDay(date, today) ? "border-amber-500" : hasDate(date) ? "border-neutral-700 border-dashed" : void 0;
|
|
192
188
|
return /* @__PURE__ */ React.createElement("div", {
|
|
193
189
|
key: i,
|
|
194
190
|
role: "none",
|
|
@@ -200,30 +196,27 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
200
196
|
className: "absolute top-0 text-xs text-description"
|
|
201
197
|
}, format(date, "MMM")), border && /* @__PURE__ */ React.createElement("div", {
|
|
202
198
|
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
|
|
199
|
+
className: mx("absolute inset-1 border-2 rounded-full", border)
|
|
208
200
|
}));
|
|
209
|
-
}))
|
|
210
|
-
className: mx(getBgColor(getDate(start, index, 6, weekStartsOn)))
|
|
211
|
-
}));
|
|
201
|
+
})));
|
|
212
202
|
}, [
|
|
213
203
|
handleDaySelect,
|
|
214
|
-
|
|
204
|
+
hasDate,
|
|
215
205
|
selected,
|
|
216
206
|
weekStartsOn
|
|
217
207
|
]);
|
|
218
208
|
return /* @__PURE__ */ React.createElement("div", {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
209
|
+
...composableProps(props, {
|
|
210
|
+
role: "none",
|
|
211
|
+
classNames: [
|
|
212
|
+
"flex flex-col h-full w-full justify-center overflow-hidden",
|
|
213
|
+
classNames
|
|
214
|
+
]
|
|
215
|
+
}),
|
|
216
|
+
ref: forwardedRef
|
|
224
217
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
225
218
|
role: "none",
|
|
226
|
-
className: "
|
|
219
|
+
className: "grid w-full grid-cols-7",
|
|
227
220
|
style: {
|
|
228
221
|
width: defaultWidth
|
|
229
222
|
}
|
|
@@ -231,15 +224,14 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
231
224
|
key: i,
|
|
232
225
|
role: "none",
|
|
233
226
|
className: "flex justify-center p-2 text-sm font-thin"
|
|
234
|
-
}, date)))
|
|
227
|
+
}, date))), /* @__PURE__ */ React.createElement("div", {
|
|
235
228
|
role: "none",
|
|
236
|
-
className: "flex flex-col
|
|
229
|
+
className: "flex flex-col h-full w-full justify-center overflow-hidden",
|
|
237
230
|
ref: containerRef
|
|
238
231
|
}, /* @__PURE__ */ React.createElement(List, {
|
|
239
232
|
ref: listRef,
|
|
240
233
|
role: "none",
|
|
241
|
-
|
|
242
|
-
className: "[&>div]:snap-y scrollbar-none outline-none",
|
|
234
|
+
className: "scrollbar-none outline-hidden",
|
|
243
235
|
width,
|
|
244
236
|
height: maxHeight ?? height,
|
|
245
237
|
rowCount: maxRows,
|
|
@@ -249,11 +241,10 @@ var CalendarGrid = ({ classNames, rows, onSelect }) => {
|
|
|
249
241
|
onScroll: handleScroll,
|
|
250
242
|
onRowsRendered: () => setInitialized(true)
|
|
251
243
|
})));
|
|
252
|
-
};
|
|
244
|
+
});
|
|
253
245
|
CalendarGrid.displayName = CALENDAR_GRID_NAME;
|
|
254
246
|
var Calendar = {
|
|
255
247
|
Root: CalendarRoot,
|
|
256
|
-
Viewport: CalendarViewport,
|
|
257
248
|
Toolbar: CalendarToolbar,
|
|
258
249
|
Grid: CalendarGrid
|
|
259
250
|
};
|