@monkey-mini-app/ui 0.1.1 → 0.1.4
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/README.md +12 -6
- package/dist/globals.css +1 -1
- package/dist/index.js +12 -2
- package/dist/manifest.json +6 -6
- package/dist/runtime.js +9 -0
- package/dist/sdk.js +158 -0
- package/dist/src/blocks/activity-feed.tsx +7 -0
- package/dist/src/blocks/app-shell.tsx +1 -0
- package/dist/src/blocks/attachment-gallery.tsx +7 -0
- package/dist/src/blocks/bar-chart.tsx +11 -0
- package/dist/src/blocks/comment-thread.tsx +7 -0
- package/dist/src/blocks/dashboard-shell.tsx +109 -0
- package/dist/src/blocks/description-list.tsx +7 -0
- package/dist/src/blocks/detail-panel.tsx +1 -0
- package/dist/src/blocks/donut-chart.tsx +7 -0
- package/dist/src/blocks/env-badge.tsx +7 -0
- package/dist/src/blocks/env-table.tsx +316 -0
- package/dist/src/blocks/file-tree.tsx +7 -0
- package/dist/src/blocks/filter-bar.tsx +1 -0
- package/dist/src/blocks/form-sheet.tsx +160 -0
- package/dist/src/blocks/gauge.tsx +7 -0
- package/dist/src/blocks/list-detail.tsx +121 -0
- package/dist/src/blocks/notification-center.tsx +7 -0
- package/dist/src/blocks/page-header.tsx +1 -0
- package/dist/src/blocks/progress-ring.tsx +7 -0
- package/dist/src/blocks/radar-chart.tsx +7 -0
- package/dist/src/blocks/request-inspector.tsx +7 -0
- package/dist/src/blocks/reveal.tsx +56 -0
- package/dist/src/blocks/run-timeline.tsx +7 -0
- package/dist/src/blocks/scrollspy.tsx +7 -0
- package/dist/src/blocks/settings-split.tsx +154 -0
- package/dist/src/blocks/severity-chip.tsx +7 -0
- package/dist/src/blocks/sparkline.tsx +7 -0
- package/dist/src/blocks/stat-card.tsx +1 -0
- package/dist/src/blocks/status-badge.tsx +1 -0
- package/dist/src/blocks/table-page.tsx +83 -0
- package/dist/src/blocks/terminal.tsx +21 -3
- package/dist/src/blocks/test-step-list.tsx +7 -0
- package/dist/src/blocks/trend-card.tsx +7 -0
- package/dist/src/blocks/wizard-shell.tsx +132 -0
- package/dist/src/cdn-modules.d.ts +5 -0
- package/dist/src/components/accordion.tsx +7 -0
- package/dist/src/components/alert-dialog.tsx +7 -0
- package/dist/src/components/alert.tsx +7 -0
- package/dist/src/components/aspect-ratio.tsx +4 -0
- package/dist/src/components/attachment.tsx +4 -0
- package/dist/src/components/avatar.tsx +4 -0
- package/dist/src/components/badge.tsx +7 -0
- package/dist/src/components/breadcrumb.tsx +4 -0
- package/dist/src/components/bubble.tsx +4 -0
- package/dist/src/components/button-group.tsx +6 -0
- package/dist/src/components/button.tsx +7 -0
- package/dist/src/components/calendar.tsx +4 -0
- package/dist/src/components/card.tsx +8 -1
- package/dist/src/components/chart.tsx +7 -0
- package/dist/src/components/checkbox.tsx +5 -0
- package/dist/src/components/collapsible.tsx +4 -0
- package/dist/src/components/combobox.tsx +4 -0
- package/dist/src/components/command.tsx +6 -0
- package/dist/src/components/context-menu.tsx +4 -0
- package/dist/src/components/dialog.tsx +21 -0
- package/dist/src/components/drawer.tsx +4 -0
- package/dist/src/components/dropdown-menu.tsx +7 -0
- package/dist/src/components/empty.tsx +7 -0
- package/dist/src/components/field.tsx +6 -0
- package/dist/src/components/hover-card.tsx +4 -0
- package/dist/src/components/input-group.tsx +4 -0
- package/dist/src/components/input.tsx +7 -0
- package/dist/src/components/item.tsx +4 -0
- package/dist/src/components/kbd.tsx +4 -0
- package/dist/src/components/label.tsx +7 -0
- package/dist/src/components/marker.tsx +4 -0
- package/dist/src/components/menubar.tsx +4 -0
- package/dist/src/components/message-scroller.tsx +4 -0
- package/dist/src/components/message.tsx +4 -0
- package/dist/src/components/native-select.tsx +4 -0
- package/dist/src/components/navigation-menu.tsx +4 -0
- package/dist/src/components/pagination.tsx +4 -0
- package/dist/src/components/popover.tsx +7 -0
- package/dist/src/components/progress.tsx +7 -0
- package/dist/src/components/questionnaire.tsx +4 -0
- package/dist/src/components/radio-group.tsx +4 -0
- package/dist/src/components/resizable.tsx +7 -0
- package/dist/src/components/scroll-area.tsx +4 -0
- package/dist/src/components/select.tsx +7 -0
- package/dist/src/components/separator.tsx +5 -0
- package/dist/src/components/sheet.tsx +22 -0
- package/dist/src/components/sidebar.tsx +4 -0
- package/dist/src/components/skeleton.tsx +7 -0
- package/dist/src/components/slider.tsx +4 -0
- package/dist/src/components/spinner.tsx +6 -0
- package/dist/src/components/switch.tsx +7 -0
- package/dist/src/components/table.tsx +7 -0
- package/dist/src/components/tabs.tsx +7 -0
- package/dist/src/components/textarea.tsx +7 -0
- package/dist/src/components/toast.tsx +7 -0
- package/dist/src/components/toggle-group.tsx +4 -0
- package/dist/src/components/toggle.tsx +4 -0
- package/dist/src/components/tooltip.tsx +7 -0
- package/dist/src/composites/autocomplete.tsx +7 -0
- package/dist/src/composites/cascader.tsx +7 -0
- package/dist/src/composites/color-picker.tsx +7 -0
- package/dist/src/composites/confirm-dialog.tsx +7 -0
- package/dist/src/composites/copyable.tsx +9 -1
- package/dist/src/composites/currency-input.tsx +7 -0
- package/dist/src/composites/date-picker.tsx +7 -0
- package/dist/src/composites/date-range-picker.tsx +4 -0
- package/dist/src/composites/date-time-picker.tsx +7 -0
- package/dist/src/composites/date-time-range-picker.tsx +7 -0
- package/dist/src/composites/duration-input.tsx +7 -0
- package/dist/src/composites/mini-calendar.tsx +7 -0
- package/dist/src/composites/number-field.tsx +7 -0
- package/dist/src/composites/password-field.tsx +7 -0
- package/dist/src/composites/phone-input.tsx +7 -0
- package/dist/src/composites/rating.tsx +7 -0
- package/dist/src/composites/relative-date-picker.tsx +7 -0
- package/dist/src/composites/search-input.tsx +7 -0
- package/dist/src/composites/slider-range.tsx +7 -0
- package/dist/src/composites/tag-input.tsx +7 -0
- package/dist/src/composites/time-picker.tsx +11 -1
- package/dist/src/composites/time-range-picker.tsx +7 -0
- package/dist/src/composites/timezone-select.tsx +7 -0
- package/dist/src/composites/transfer.tsx +7 -0
- package/dist/src/composites/user-picker.tsx +7 -0
- package/dist/src/hooks/use-html-dark.ts +37 -0
- package/dist/src/hooks/use-mobile.ts +3 -0
- package/dist/src/i18n/en.ts +109 -2
- package/dist/src/i18n/zh.ts +99 -2
- package/dist/src/index.ts +147 -134
- package/dist/src/lib/clipboard.ts +10 -0
- package/dist/src/lib/illustrations.tsx +2 -2
- package/dist/src/products/code-block.tsx +35 -18
- package/dist/src/products/code-editor.tsx +156 -24
- package/dist/src/products/commit-graph.tsx +644 -0
- package/dist/src/products/data-grid.tsx +2 -1
- package/dist/src/products/diff-viewer.tsx +104 -8
- package/dist/src/products/event-calendar.tsx +28 -823
- package/dist/src/products/file-dropzone.tsx +7 -0
- package/dist/src/products/full-calendar/animations.ts +38 -0
- package/dist/src/products/full-calendar/calendar-body.tsx +77 -0
- package/dist/src/products/full-calendar/constants.ts +10 -0
- package/dist/src/products/full-calendar/contexts/calendar-context.tsx +255 -0
- package/dist/src/products/full-calendar/contexts/dnd-context.tsx +157 -0
- package/dist/src/products/full-calendar/dialogs/add-edit-event-dialog.tsx +187 -0
- package/dist/src/products/full-calendar/dialogs/delete-event-dialog.tsx +39 -0
- package/dist/src/products/full-calendar/dialogs/event-details-dialog.tsx +100 -0
- package/dist/src/products/full-calendar/dialogs/events-list-dialog.tsx +87 -0
- package/dist/src/products/full-calendar/dnd/draggable-event.tsx +45 -0
- package/dist/src/products/full-calendar/dnd/droppable-area.tsx +45 -0
- package/dist/src/products/full-calendar/dnd/resizable-event.tsx +179 -0
- package/dist/src/products/full-calendar/header/calendar-header.tsx +65 -0
- package/dist/src/products/full-calendar/header/date-navigator.tsx +110 -0
- package/dist/src/products/full-calendar/header/filter.tsx +58 -0
- package/dist/src/products/full-calendar/header/today-button.tsx +45 -0
- package/dist/src/products/full-calendar/header/user-select.tsx +64 -0
- package/dist/src/products/full-calendar/header/view-tabs.tsx +36 -0
- package/dist/src/products/full-calendar/helpers.ts +453 -0
- package/dist/src/products/full-calendar/hooks/use-time-range-create.ts +152 -0
- package/dist/src/products/full-calendar/hooks.ts +70 -0
- package/dist/src/products/full-calendar/interfaces.ts +23 -0
- package/dist/src/products/full-calendar/settings/settings.tsx +80 -0
- package/dist/src/products/full-calendar/shims/avatar-group.tsx +16 -0
- package/dist/src/products/full-calendar/shims/day-picker.tsx +1 -0
- package/dist/src/products/full-calendar/shims/motion.tsx +97 -0
- package/dist/src/products/full-calendar/shims/next-themes.ts +6 -0
- package/dist/src/products/full-calendar/shims/re-resizable.tsx +25 -0
- package/dist/src/products/full-calendar/shims/sonner.ts +4 -0
- package/dist/src/products/full-calendar/types.ts +8 -0
- package/dist/src/products/full-calendar/views/agenda-view/agenda-events.tsx +140 -0
- package/dist/src/products/full-calendar/views/month-view/calendar-month-view.tsx +69 -0
- package/dist/src/products/full-calendar/views/month-view/day-cell.tsx +201 -0
- package/dist/src/products/full-calendar/views/month-view/event-bullet.tsx +40 -0
- package/dist/src/products/full-calendar/views/month-view/month-event-badge.tsx +143 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/calendar-day-view.tsx +295 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/calendar-time-line.tsx +35 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/calendar-week-view.tsx +253 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/day-view-multi-day-events-row.tsx +73 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/event-block.tsx +114 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/render-grouped-events.tsx +49 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/week-view-multi-day-events-row.tsx +143 -0
- package/dist/src/products/full-calendar/views/year-view/calendar-year-view.tsx +161 -0
- package/dist/src/products/gantt.tsx +7 -0
- package/dist/src/products/jira-wiki.tsx +1 -0
- package/dist/src/products/jql-input.tsx +340 -37
- package/dist/src/products/json-viewer.tsx +7 -0
- package/dist/src/products/kanban-issue-panel.tsx +7 -0
- package/dist/src/products/kanban.tsx +1 -0
- package/dist/src/products/log-viewer.tsx +390 -18
- package/dist/src/products/markdown-editor.tsx +21 -6
- package/dist/src/products/markdown.tsx +7 -0
- package/dist/src/products/rich-text-editor.tsx +65 -59
- package/dist/src/products/sortable-list.tsx +7 -0
- package/dist/src/products/stepper.tsx +219 -70
- package/dist/src/products/timeline.tsx +7 -0
- package/dist/src/products/tree-view.tsx +7 -0
- package/dist/src/sdk/app-error-boundary.tsx +240 -0
- package/dist/src/sdk/app-id.ts +7 -0
- package/dist/src/sdk/use-app.ts +126 -0
- package/dist/src/styles/globals.css +1 -0
- package/dist/vendors/lodash.js +20 -0
- package/dist/vendors/motion.js +12 -0
- package/package.json +20 -19
- package/dist/src/products/jql-language.ts +0 -70
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import {addDays, format, isSameDay, parseISO, startOfWeek} from "date-fns";
|
|
2
|
+
import {motion} from "../../shims/motion";
|
|
3
|
+
import {ScrollArea} from "@monkey-mini-app/ui/components/scroll-area";
|
|
4
|
+
import {
|
|
5
|
+
fadeIn,
|
|
6
|
+
staggerContainer,
|
|
7
|
+
transition,
|
|
8
|
+
} from "../../animations";
|
|
9
|
+
import {useCalendar} from "../../contexts/calendar-context";
|
|
10
|
+
import {AddEditEventDialog} from "../../dialogs/add-edit-event-dialog";
|
|
11
|
+
import {DroppableArea} from "../../dnd/droppable-area";
|
|
12
|
+
import {groupEvents, HOUR_HEIGHT_PX} from "../../helpers";
|
|
13
|
+
import {useTimeRangeCreate} from "../../hooks/use-time-range-create";
|
|
14
|
+
import type {IEvent} from "../../interfaces";
|
|
15
|
+
import {CalendarTimeline} from "./calendar-time-line";
|
|
16
|
+
import {RenderGroupedEvents} from "./render-grouped-events";
|
|
17
|
+
import {
|
|
18
|
+
WeekViewMultiDayEventsRow
|
|
19
|
+
} from "./week-view-multi-day-events-row";
|
|
20
|
+
|
|
21
|
+
interface IProps {
|
|
22
|
+
singleDayEvents: IEvent[];
|
|
23
|
+
multiDayEvents: IEvent[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function CalendarWeekView({singleDayEvents, multiDayEvents}: IProps) {
|
|
27
|
+
const {selectedDate, use24HourFormat} = useCalendar();
|
|
28
|
+
const {
|
|
29
|
+
selection,
|
|
30
|
+
draft,
|
|
31
|
+
clearDraft,
|
|
32
|
+
overlayStyle,
|
|
33
|
+
onPointerDown,
|
|
34
|
+
onPointerMove,
|
|
35
|
+
onPointerUp,
|
|
36
|
+
} = useTimeRangeCreate();
|
|
37
|
+
|
|
38
|
+
const weekStart = startOfWeek(selectedDate);
|
|
39
|
+
const weekDays = Array.from({length: 7}, (_, i) => addDays(weekStart, i));
|
|
40
|
+
const hours = Array.from({length: 24}, (_, i) => i);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<motion.div
|
|
44
|
+
data-testid="calendar-week-grid"
|
|
45
|
+
initial="initial"
|
|
46
|
+
animate="animate"
|
|
47
|
+
exit="exit"
|
|
48
|
+
variants={fadeIn}
|
|
49
|
+
transition={transition}
|
|
50
|
+
>
|
|
51
|
+
<motion.div
|
|
52
|
+
className="flex flex-col items-center justify-center border-b p-4 text-sm sm:hidden"
|
|
53
|
+
initial={{opacity: 0, y: -20}}
|
|
54
|
+
animate={{opacity: 1, y: 0}}
|
|
55
|
+
transition={transition}
|
|
56
|
+
>
|
|
57
|
+
<p>Weekly view is not recommended on smaller devices.</p>
|
|
58
|
+
<p>Please switch to a desktop device or use the daily view instead.</p>
|
|
59
|
+
</motion.div>
|
|
60
|
+
|
|
61
|
+
<motion.div
|
|
62
|
+
className="flex-col sm:flex"
|
|
63
|
+
variants={staggerContainer}
|
|
64
|
+
>
|
|
65
|
+
<div>
|
|
66
|
+
<WeekViewMultiDayEventsRow
|
|
67
|
+
selectedDate={selectedDate}
|
|
68
|
+
multiDayEvents={multiDayEvents}
|
|
69
|
+
/>
|
|
70
|
+
|
|
71
|
+
{/* Week header */}
|
|
72
|
+
<motion.div
|
|
73
|
+
className="relative z-20 flex border-b"
|
|
74
|
+
initial={{opacity: 0, y: -20}}
|
|
75
|
+
animate={{opacity: 1, y: 0}}
|
|
76
|
+
transition={transition}
|
|
77
|
+
>
|
|
78
|
+
{/* Time column header - responsive width */}
|
|
79
|
+
<div className="w-18"></div>
|
|
80
|
+
<div className="grid flex-1 grid-cols-7 border-l">
|
|
81
|
+
{weekDays.map((day, index) => (
|
|
82
|
+
<motion.span
|
|
83
|
+
key={day.toISOString()}
|
|
84
|
+
className="py-1 sm:py-2 text-center text-xs font-medium text-t-quaternary"
|
|
85
|
+
initial={{opacity: 0, y: -10}}
|
|
86
|
+
animate={{opacity: 1, y: 0}}
|
|
87
|
+
transition={{delay: index * 0.05, ...transition}}
|
|
88
|
+
>
|
|
89
|
+
{/* Mobile: Show only day abbreviation and number */}
|
|
90
|
+
<span className="block sm:hidden">
|
|
91
|
+
{format(day, "EEE").charAt(0)}
|
|
92
|
+
<span className="block font-semibold text-t-secondary text-xs">
|
|
93
|
+
{format(day, "d")}
|
|
94
|
+
</span>
|
|
95
|
+
</span>
|
|
96
|
+
{/* Desktop: Show full format */}
|
|
97
|
+
<span className="hidden sm:inline">
|
|
98
|
+
{format(day, "EE")}{" "}
|
|
99
|
+
<span className="ml-1 font-semibold text-t-secondary">
|
|
100
|
+
{format(day, "d")}
|
|
101
|
+
</span>
|
|
102
|
+
</span>
|
|
103
|
+
</motion.span>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
</motion.div>
|
|
107
|
+
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<ScrollArea className="h-[736px]">
|
|
111
|
+
<div className="flex">
|
|
112
|
+
{/* Hours column */}
|
|
113
|
+
<motion.div className="relative w-18" variants={staggerContainer}>
|
|
114
|
+
{hours.map((hour, index) => (
|
|
115
|
+
<motion.div
|
|
116
|
+
key={hour}
|
|
117
|
+
className="relative"
|
|
118
|
+
style={{height: `${HOUR_HEIGHT_PX}px`}}
|
|
119
|
+
initial={{opacity: 0, x: -20}}
|
|
120
|
+
animate={{opacity: 1, x: 0}}
|
|
121
|
+
transition={{delay: index * 0.02, ...transition}}
|
|
122
|
+
>
|
|
123
|
+
<div className="absolute -top-3 right-2 flex h-6 items-center">
|
|
124
|
+
{index !== 0 && (
|
|
125
|
+
<span className="text-xs text-t-quaternary">
|
|
126
|
+
{format(
|
|
127
|
+
new Date().setHours(hour, 0, 0, 0),
|
|
128
|
+
use24HourFormat ? "HH:00" : "h a",
|
|
129
|
+
)}
|
|
130
|
+
</span>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
</motion.div>
|
|
134
|
+
))}
|
|
135
|
+
</motion.div>
|
|
136
|
+
|
|
137
|
+
{/* Week grid */}
|
|
138
|
+
<motion.div
|
|
139
|
+
className="relative flex-1 border-l"
|
|
140
|
+
variants={staggerContainer}
|
|
141
|
+
>
|
|
142
|
+
<div className="grid grid-cols-7 divide-x">
|
|
143
|
+
{weekDays.map((day, dayIndex) => {
|
|
144
|
+
const dayEvents = singleDayEvents.filter(
|
|
145
|
+
(event) =>
|
|
146
|
+
isSameDay(parseISO(event.startDate), day) ||
|
|
147
|
+
isSameDay(parseISO(event.endDate), day),
|
|
148
|
+
);
|
|
149
|
+
const groupedEvents = groupEvents(dayEvents);
|
|
150
|
+
|
|
151
|
+
const showOverlay =
|
|
152
|
+
selection != null &&
|
|
153
|
+
isSameDay(selection.day, day) &&
|
|
154
|
+
overlayStyle != null;
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<motion.div
|
|
158
|
+
key={day.toISOString()}
|
|
159
|
+
className="relative touch-none select-none"
|
|
160
|
+
data-testid="calendar-day-column"
|
|
161
|
+
data-day={day.toISOString()}
|
|
162
|
+
initial={{opacity: 0}}
|
|
163
|
+
animate={{opacity: 1}}
|
|
164
|
+
transition={{delay: dayIndex * 0.1, ...transition}}
|
|
165
|
+
onPointerDown={(e) => onPointerDown(e, day, e.currentTarget)}
|
|
166
|
+
onPointerMove={onPointerMove}
|
|
167
|
+
onPointerUp={onPointerUp}
|
|
168
|
+
onPointerCancel={onPointerUp}
|
|
169
|
+
>
|
|
170
|
+
{hours.map((hour, index) => (
|
|
171
|
+
<motion.div
|
|
172
|
+
key={hour}
|
|
173
|
+
className="relative"
|
|
174
|
+
style={{height: `${HOUR_HEIGHT_PX}px`}}
|
|
175
|
+
initial={{opacity: 0}}
|
|
176
|
+
animate={{opacity: 1}}
|
|
177
|
+
transition={{delay: index * 0.01, ...transition}}
|
|
178
|
+
>
|
|
179
|
+
{index !== 0 && (
|
|
180
|
+
<div
|
|
181
|
+
className="pointer-events-none absolute inset-x-0 top-0 border-b"></div>
|
|
182
|
+
)}
|
|
183
|
+
|
|
184
|
+
<DroppableArea
|
|
185
|
+
date={day}
|
|
186
|
+
hour={hour}
|
|
187
|
+
minute={0}
|
|
188
|
+
className="absolute inset-x-0 top-0 h-[48px]"
|
|
189
|
+
>
|
|
190
|
+
<div className="absolute inset-0" />
|
|
191
|
+
</DroppableArea>
|
|
192
|
+
|
|
193
|
+
<div
|
|
194
|
+
className="pointer-events-none absolute inset-x-0 top-1/2 border-b border-dashed border-b-tertiary"></div>
|
|
195
|
+
|
|
196
|
+
<DroppableArea
|
|
197
|
+
date={day}
|
|
198
|
+
hour={hour}
|
|
199
|
+
minute={30}
|
|
200
|
+
className="absolute inset-x-0 bottom-0 h-[48px]"
|
|
201
|
+
>
|
|
202
|
+
<div className="absolute inset-0" />
|
|
203
|
+
</DroppableArea>
|
|
204
|
+
</motion.div>
|
|
205
|
+
))}
|
|
206
|
+
|
|
207
|
+
{showOverlay ? (
|
|
208
|
+
<div
|
|
209
|
+
className="pointer-events-none absolute inset-x-0.5 z-10 rounded-md border border-primary/40 bg-primary/15"
|
|
210
|
+
data-testid="calendar-selection-overlay"
|
|
211
|
+
style={overlayStyle}
|
|
212
|
+
/>
|
|
213
|
+
) : null}
|
|
214
|
+
|
|
215
|
+
<div className="pointer-events-auto relative z-20">
|
|
216
|
+
<RenderGroupedEvents
|
|
217
|
+
groupedEvents={groupedEvents}
|
|
218
|
+
day={day}
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
221
|
+
</motion.div>
|
|
222
|
+
);
|
|
223
|
+
})}
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<CalendarTimeline/>
|
|
227
|
+
</motion.div>
|
|
228
|
+
|
|
229
|
+
{draft ? (
|
|
230
|
+
<AddEditEventDialog
|
|
231
|
+
open
|
|
232
|
+
onOpenChange={(open) => {
|
|
233
|
+
if (!open) clearDraft();
|
|
234
|
+
}}
|
|
235
|
+
startDate={draft.start}
|
|
236
|
+
startTime={{
|
|
237
|
+
hour: draft.start.getHours(),
|
|
238
|
+
minute: draft.start.getMinutes(),
|
|
239
|
+
}}
|
|
240
|
+
endTime={{
|
|
241
|
+
hour: draft.end.getHours(),
|
|
242
|
+
minute: draft.end.getMinutes(),
|
|
243
|
+
}}
|
|
244
|
+
/>
|
|
245
|
+
) : null}
|
|
246
|
+
</div>
|
|
247
|
+
</ScrollArea>
|
|
248
|
+
</motion.div>
|
|
249
|
+
</motion.div>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
|
package/dist/src/products/full-calendar/views/week-and-day-view/day-view-multi-day-events-row.tsx
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
differenceInDays,
|
|
3
|
+
endOfDay,
|
|
4
|
+
isWithinInterval,
|
|
5
|
+
parseISO,
|
|
6
|
+
startOfDay,
|
|
7
|
+
} from "date-fns";
|
|
8
|
+
import type { IEvent } from "../../interfaces";
|
|
9
|
+
import { MonthEventBadge } from "../month-view/month-event-badge";
|
|
10
|
+
|
|
11
|
+
interface IProps {
|
|
12
|
+
selectedDate: Date;
|
|
13
|
+
multiDayEvents: IEvent[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function DayViewMultiDayEventsRow({
|
|
17
|
+
selectedDate,
|
|
18
|
+
multiDayEvents,
|
|
19
|
+
}: IProps) {
|
|
20
|
+
const dayStart = startOfDay(selectedDate);
|
|
21
|
+
const dayEnd = endOfDay(selectedDate);
|
|
22
|
+
|
|
23
|
+
const multiDayEventsInDay = multiDayEvents
|
|
24
|
+
.filter((event) => {
|
|
25
|
+
const eventStart = parseISO(event.startDate);
|
|
26
|
+
const eventEnd = parseISO(event.endDate);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
isWithinInterval(dayStart, { start: eventStart, end: eventEnd }) ||
|
|
30
|
+
isWithinInterval(dayEnd, { start: eventStart, end: eventEnd }) ||
|
|
31
|
+
(eventStart <= dayStart && eventEnd >= dayEnd)
|
|
32
|
+
);
|
|
33
|
+
})
|
|
34
|
+
.sort((a, b) => {
|
|
35
|
+
const durationA = differenceInDays(
|
|
36
|
+
parseISO(a.endDate),
|
|
37
|
+
parseISO(a.startDate),
|
|
38
|
+
);
|
|
39
|
+
const durationB = differenceInDays(
|
|
40
|
+
parseISO(b.endDate),
|
|
41
|
+
parseISO(b.startDate),
|
|
42
|
+
);
|
|
43
|
+
return durationB - durationA;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (multiDayEventsInDay.length === 0) return null;
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className="flex border-b">
|
|
50
|
+
<div className="w-18"></div>
|
|
51
|
+
<div className="flex flex-1 flex-col gap-1 border-l py-1">
|
|
52
|
+
{multiDayEventsInDay.map((event) => {
|
|
53
|
+
const eventStart = startOfDay(parseISO(event.startDate));
|
|
54
|
+
const eventEnd = startOfDay(parseISO(event.endDate));
|
|
55
|
+
const currentDate = startOfDay(selectedDate);
|
|
56
|
+
|
|
57
|
+
const eventTotalDays = differenceInDays(eventEnd, eventStart) + 1;
|
|
58
|
+
const eventCurrentDay = differenceInDays(currentDate, eventStart) + 1;
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<MonthEventBadge
|
|
62
|
+
key={event.id}
|
|
63
|
+
event={event}
|
|
64
|
+
cellDate={selectedDate}
|
|
65
|
+
eventCurrentDay={eventCurrentDay}
|
|
66
|
+
eventTotalDays={eventTotalDays}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
})}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { VariantProps } from "class-variance-authority";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { differenceInMinutes, parseISO } from "date-fns";
|
|
4
|
+
import type { HTMLAttributes } from "react";
|
|
5
|
+
import { cn } from "@monkey-mini-app/ui/lib/utils";
|
|
6
|
+
import { useCalendar } from "../../contexts/calendar-context";
|
|
7
|
+
import { EventDetailsDialog } from "../../dialogs/event-details-dialog";
|
|
8
|
+
import { DraggableEvent } from "../../dnd/draggable-event";
|
|
9
|
+
import { ResizableEvent } from "../../dnd/resizable-event";
|
|
10
|
+
import { formatTime } from "../../helpers";
|
|
11
|
+
import type { IEvent } from "../../interfaces";
|
|
12
|
+
|
|
13
|
+
const calendarWeekEventCardVariants = cva(
|
|
14
|
+
"flex select-none flex-col gap-0.5 truncate whitespace-nowrap rounded-md border px-2 py-1.5 text-xs focus-visible:outline-offset-2",
|
|
15
|
+
{
|
|
16
|
+
variants: {
|
|
17
|
+
color: {
|
|
18
|
+
// Colored variants
|
|
19
|
+
blue: "border-blue-200 bg-blue-100/50 text-blue-700 hover:bg-blue-100 dark:border-blue-800 dark:bg-blue-950/50 dark:text-blue-300 dark:hover:bg-blue-950",
|
|
20
|
+
green:
|
|
21
|
+
"border-green-200 bg-green-100/50 text-green-700 hover:bg-green-100 dark:border-green-800 dark:bg-green-950/50 dark:text-green-300 dark:hover:bg-green-950",
|
|
22
|
+
red: "border-red-200 bg-red-100/50 text-red-700 hover:bg-red-100 dark:border-red-800 dark:bg-red-950/50 dark:text-red-300 dark:hover:bg-red-950",
|
|
23
|
+
yellow:
|
|
24
|
+
"border-yellow-200 bg-yellow-100/50 text-yellow-700 hover:bg-yellow-100 dark:border-yellow-800 dark:bg-yellow-950/50 dark:text-yellow-300 dark:hover:bg-yellow-950",
|
|
25
|
+
purple:
|
|
26
|
+
"border-purple-200 bg-purple-100/50 text-purple-700 hover:bg-purple-100 dark:border-purple-800 dark:bg-purple-950/50 dark:text-purple-300 dark:hover:bg-purple-950",
|
|
27
|
+
orange:
|
|
28
|
+
"border-orange-200 bg-orange-100/50 text-orange-700 hover:bg-orange-100 dark:border-orange-800 dark:bg-orange-950/50 dark:text-orange-300 dark:hover:bg-orange-950",
|
|
29
|
+
|
|
30
|
+
// Dot variants
|
|
31
|
+
"blue-dot":
|
|
32
|
+
"border-border bg-card text-foreground hover:bg-accent [&_svg]:fill-blue-600 dark:[&_svg]:fill-blue-500",
|
|
33
|
+
"green-dot":
|
|
34
|
+
"border-border bg-card text-foreground hover:bg-accent [&_svg]:fill-green-600 dark:[&_svg]:fill-green-500",
|
|
35
|
+
"red-dot":
|
|
36
|
+
"border-border bg-card text-foreground hover:bg-accent [&_svg]:fill-red-600 dark:[&_svg]:fill-red-500",
|
|
37
|
+
"orange-dot":
|
|
38
|
+
"border-border bg-card text-foreground hover:bg-accent [&_svg]:fill-orange-600 dark:[&_svg]:fill-orange-500",
|
|
39
|
+
"purple-dot":
|
|
40
|
+
"border-border bg-card text-foreground hover:bg-accent [&_svg]:fill-purple-600 dark:[&_svg]:fill-purple-500",
|
|
41
|
+
"yellow-dot":
|
|
42
|
+
"border-border bg-card text-foreground hover:bg-accent [&_svg]:fill-yellow-600 dark:[&_svg]:fill-yellow-500",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
defaultVariants: {
|
|
46
|
+
color: "blue-dot",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
interface IProps
|
|
52
|
+
extends
|
|
53
|
+
HTMLAttributes<HTMLDivElement>,
|
|
54
|
+
Omit<VariantProps<typeof calendarWeekEventCardVariants>, "color"> {
|
|
55
|
+
event: IEvent;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function EventBlock({ event, className }: IProps) {
|
|
59
|
+
const { badgeVariant, use24HourFormat } = useCalendar();
|
|
60
|
+
|
|
61
|
+
const start = parseISO(event.startDate);
|
|
62
|
+
const end = parseISO(event.endDate);
|
|
63
|
+
const durationInMinutes = differenceInMinutes(end, start);
|
|
64
|
+
const heightInPixels = (durationInMinutes / 60) * 96 - 8;
|
|
65
|
+
|
|
66
|
+
const color = (
|
|
67
|
+
badgeVariant === "dot" ? `${event.color}-dot` : event.color
|
|
68
|
+
) as VariantProps<typeof calendarWeekEventCardVariants>["color"];
|
|
69
|
+
|
|
70
|
+
const calendarWeekEventCardClasses = cn(
|
|
71
|
+
calendarWeekEventCardVariants({ color, className }),
|
|
72
|
+
durationInMinutes < 35 && "py-0 justify-center",
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<ResizableEvent event={event}>
|
|
77
|
+
<DraggableEvent event={event}>
|
|
78
|
+
<EventDetailsDialog event={event}>
|
|
79
|
+
<button
|
|
80
|
+
type="button"
|
|
81
|
+
data-calendar-event="1"
|
|
82
|
+
data-testid={`calendar-event-${event.id}`}
|
|
83
|
+
className={calendarWeekEventCardClasses}
|
|
84
|
+
style={{ height: `${heightInPixels}px` }}
|
|
85
|
+
>
|
|
86
|
+
<div className="flex items-center gap-1.5 truncate">
|
|
87
|
+
{badgeVariant === "dot" && (
|
|
88
|
+
<svg
|
|
89
|
+
width="8"
|
|
90
|
+
height="8"
|
|
91
|
+
viewBox="0 0 8 8"
|
|
92
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
93
|
+
className="shrink-0"
|
|
94
|
+
aria-hidden="true"
|
|
95
|
+
>
|
|
96
|
+
<circle cx="4" cy="4" r="4" />
|
|
97
|
+
</svg>
|
|
98
|
+
)}
|
|
99
|
+
|
|
100
|
+
<p className="truncate font-semibold">{event.title}</p>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
{durationInMinutes > 25 && (
|
|
104
|
+
<p>
|
|
105
|
+
{formatTime(start, use24HourFormat)} -{" "}
|
|
106
|
+
{formatTime(end, use24HourFormat)}
|
|
107
|
+
</p>
|
|
108
|
+
)}
|
|
109
|
+
</button>
|
|
110
|
+
</EventDetailsDialog>
|
|
111
|
+
</DraggableEvent>
|
|
112
|
+
</ResizableEvent>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { areIntervalsOverlapping, parseISO } from "date-fns";
|
|
2
|
+
import { getEventBlockStyle } from "../../helpers";
|
|
3
|
+
import type { IEvent } from "../../interfaces";
|
|
4
|
+
import { EventBlock } from "./event-block";
|
|
5
|
+
|
|
6
|
+
interface RenderGroupedEventsProps {
|
|
7
|
+
groupedEvents: IEvent[][];
|
|
8
|
+
day: Date;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function RenderGroupedEvents({
|
|
12
|
+
groupedEvents,
|
|
13
|
+
day,
|
|
14
|
+
}: RenderGroupedEventsProps) {
|
|
15
|
+
return groupedEvents.map((group, groupIndex) =>
|
|
16
|
+
group.map((event) => {
|
|
17
|
+
let style = getEventBlockStyle(
|
|
18
|
+
event,
|
|
19
|
+
day,
|
|
20
|
+
groupIndex,
|
|
21
|
+
groupedEvents.length,
|
|
22
|
+
);
|
|
23
|
+
const hasOverlap = groupedEvents.some(
|
|
24
|
+
(otherGroup, otherIndex) =>
|
|
25
|
+
otherIndex !== groupIndex &&
|
|
26
|
+
otherGroup.some((otherEvent) =>
|
|
27
|
+
areIntervalsOverlapping(
|
|
28
|
+
{
|
|
29
|
+
start: parseISO(event.startDate),
|
|
30
|
+
end: parseISO(event.endDate),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
start: parseISO(otherEvent.startDate),
|
|
34
|
+
end: parseISO(otherEvent.endDate),
|
|
35
|
+
},
|
|
36
|
+
),
|
|
37
|
+
),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (!hasOverlap) style = { ...style, width: "100%", left: "0%" };
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div key={event.id} className="absolute p-1" style={style}>
|
|
44
|
+
<EventBlock event={event} />
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
}
|
package/dist/src/products/full-calendar/views/week-and-day-view/week-view-multi-day-events-row.tsx
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addDays,
|
|
3
|
+
differenceInDays,
|
|
4
|
+
endOfWeek,
|
|
5
|
+
isAfter,
|
|
6
|
+
isBefore,
|
|
7
|
+
parseISO,
|
|
8
|
+
startOfDay,
|
|
9
|
+
startOfWeek,
|
|
10
|
+
} from "date-fns";
|
|
11
|
+
import { useMemo } from "react";
|
|
12
|
+
import type { IEvent } from "../../interfaces";
|
|
13
|
+
import { MonthEventBadge } from "../month-view/month-event-badge";
|
|
14
|
+
|
|
15
|
+
interface IProps {
|
|
16
|
+
selectedDate: Date;
|
|
17
|
+
multiDayEvents: IEvent[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function WeekViewMultiDayEventsRow({
|
|
21
|
+
selectedDate,
|
|
22
|
+
multiDayEvents,
|
|
23
|
+
}: IProps) {
|
|
24
|
+
const weekStart = startOfWeek(selectedDate);
|
|
25
|
+
const weekEnd = endOfWeek(selectedDate);
|
|
26
|
+
const weekDays = Array.from({ length: 7 }, (_, i) => addDays(weekStart, i));
|
|
27
|
+
|
|
28
|
+
const processedEvents = useMemo(() => {
|
|
29
|
+
return multiDayEvents
|
|
30
|
+
.map((event) => {
|
|
31
|
+
const start = parseISO(event.startDate);
|
|
32
|
+
const end = parseISO(event.endDate);
|
|
33
|
+
const adjustedStart = isBefore(start, weekStart) ? weekStart : start;
|
|
34
|
+
const adjustedEnd = isAfter(end, weekEnd) ? weekEnd : end;
|
|
35
|
+
const startIndex = differenceInDays(adjustedStart, weekStart);
|
|
36
|
+
const endIndex = differenceInDays(adjustedEnd, weekStart);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
...event,
|
|
40
|
+
adjustedStart,
|
|
41
|
+
adjustedEnd,
|
|
42
|
+
startIndex,
|
|
43
|
+
endIndex,
|
|
44
|
+
};
|
|
45
|
+
})
|
|
46
|
+
.sort((a, b) => {
|
|
47
|
+
const startDiff = a.adjustedStart.getTime() - b.adjustedStart.getTime();
|
|
48
|
+
if (startDiff !== 0) return startDiff;
|
|
49
|
+
return b.endIndex - b.startIndex - (a.endIndex - a.startIndex);
|
|
50
|
+
});
|
|
51
|
+
}, [multiDayEvents, weekStart, weekEnd]);
|
|
52
|
+
|
|
53
|
+
const eventRows = useMemo(() => {
|
|
54
|
+
const rows: (typeof processedEvents)[] = [];
|
|
55
|
+
|
|
56
|
+
processedEvents.forEach((event) => {
|
|
57
|
+
let rowIndex = rows.findIndex((row) =>
|
|
58
|
+
row.every(
|
|
59
|
+
(e) => e.endIndex < event.startIndex || e.startIndex > event.endIndex,
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (rowIndex === -1) {
|
|
64
|
+
rowIndex = rows.length;
|
|
65
|
+
rows.push([]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
rows[rowIndex].push(event);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return rows;
|
|
72
|
+
}, [processedEvents]);
|
|
73
|
+
|
|
74
|
+
const hasEventsInWeek = useMemo(() => {
|
|
75
|
+
return multiDayEvents.some((event) => {
|
|
76
|
+
const start = parseISO(event.startDate);
|
|
77
|
+
const end = parseISO(event.endDate);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
// Event starts within the week
|
|
81
|
+
(start >= weekStart && start <= weekEnd) ||
|
|
82
|
+
// Event ends within the week
|
|
83
|
+
(end >= weekStart && end <= weekEnd) ||
|
|
84
|
+
// Event spans the entire week
|
|
85
|
+
(start <= weekStart && end >= weekEnd)
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
}, [multiDayEvents, weekStart, weekEnd]);
|
|
89
|
+
|
|
90
|
+
if (!hasEventsInWeek) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div className="overflow-hidden flex" data-testid="calendar-all-day">
|
|
96
|
+
<div className="w-18 border-b"></div>
|
|
97
|
+
<div className="grid flex-1 grid-cols-7 divide-x border-b border-l">
|
|
98
|
+
{weekDays.map((day, dayIndex) => (
|
|
99
|
+
<div
|
|
100
|
+
key={day.toISOString()}
|
|
101
|
+
className="flex h-full flex-col gap-1 py-1"
|
|
102
|
+
>
|
|
103
|
+
{eventRows.map((row, rowIndex) => {
|
|
104
|
+
const event = row.find(
|
|
105
|
+
(e) => e.startIndex <= dayIndex && e.endIndex >= dayIndex,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (!event) {
|
|
109
|
+
return (
|
|
110
|
+
<div key={`${rowIndex}-${dayIndex.toString()}`} className="h-6.5" />
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let position: "first" | "middle" | "last" | "none" = "none";
|
|
115
|
+
|
|
116
|
+
if (
|
|
117
|
+
dayIndex === event.startIndex &&
|
|
118
|
+
dayIndex === event.endIndex
|
|
119
|
+
) {
|
|
120
|
+
position = "none";
|
|
121
|
+
} else if (dayIndex === event.startIndex) {
|
|
122
|
+
position = "first";
|
|
123
|
+
} else if (dayIndex === event.endIndex) {
|
|
124
|
+
position = "last";
|
|
125
|
+
} else {
|
|
126
|
+
position = "middle";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<MonthEventBadge
|
|
131
|
+
key={`${event.id}-${dayIndex}`}
|
|
132
|
+
event={event}
|
|
133
|
+
cellDate={startOfDay(day)}
|
|
134
|
+
position={position}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
})}
|
|
138
|
+
</div>
|
|
139
|
+
))}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
}
|