react-yearly-calendar-grid 0.1.0

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/index.js ADDED
@@ -0,0 +1,1069 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __defProps = Object.defineProperties;
9
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
10
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
13
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
14
+ var __spreadValues = (a, b) => {
15
+ for (var prop in b || (b = {}))
16
+ if (__hasOwnProp.call(b, prop))
17
+ __defNormalProp(a, prop, b[prop]);
18
+ if (__getOwnPropSymbols)
19
+ for (var prop of __getOwnPropSymbols(b)) {
20
+ if (__propIsEnum.call(b, prop))
21
+ __defNormalProp(a, prop, b[prop]);
22
+ }
23
+ return a;
24
+ };
25
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
26
+
27
+ // src/YearlyCalendar/styles.ts
28
+ var defaultTheme = {
29
+ // 背景色
30
+ bgWhite: "#ffffff",
31
+ bgGray50: "#f9fafb",
32
+ bgGray100: "#f3f4f6",
33
+ bgSunday: "#fef2f2",
34
+ bgSaturday: "#eff6ff",
35
+ bgDropHighlight: "rgba(191, 219, 254, 0.5)",
36
+ // ボーダー
37
+ borderDefault: "#d1d5db",
38
+ borderDropHighlight: "#3b82f6",
39
+ // テキスト
40
+ textPrimary: "#111827",
41
+ textSecondary: "#4b5563",
42
+ textMuted: "#6b7280",
43
+ // UI要素
44
+ tooltipBg: "#111827",
45
+ tooltipText: "#ffffff",
46
+ dialogOverlay: "rgba(0, 0, 0, 0.3)",
47
+ buttonPrimary: "#2563eb",
48
+ buttonPrimaryHover: "#1d4ed8",
49
+ floatingDateBg: "#2563eb",
50
+ resizeFloatingBg: "#9333ea"
51
+ };
52
+ function mergeTheme(custom) {
53
+ if (!custom) return defaultTheme;
54
+ return __spreadValues(__spreadValues({}, defaultTheme), custom);
55
+ }
56
+ function createStyles(theme) {
57
+ return {
58
+ // コンテナ
59
+ container: {
60
+ width: "100%",
61
+ height: "100%",
62
+ position: "relative",
63
+ display: "flex",
64
+ flexDirection: "column"
65
+ },
66
+ // フレックスコンテナ
67
+ flexContainer: {
68
+ display: "flex",
69
+ width: "100%",
70
+ flex: 1
71
+ },
72
+ // 日付列(左側固定)
73
+ dayColumn: {
74
+ position: "sticky",
75
+ left: 0,
76
+ zIndex: 20,
77
+ backgroundColor: theme.bgGray100,
78
+ flexShrink: 0,
79
+ display: "flex",
80
+ flexDirection: "column"
81
+ },
82
+ // 日付ヘッダーセル
83
+ dayHeaderCell: {
84
+ height: 24,
85
+ border: `1px solid ${theme.borderDefault}`,
86
+ backgroundColor: theme.bgGray100,
87
+ padding: "0 4px",
88
+ display: "flex",
89
+ alignItems: "center",
90
+ justifyContent: "center",
91
+ fontWeight: 500,
92
+ fontSize: 12,
93
+ width: 32
94
+ },
95
+ // 日付セル
96
+ dayCell: {
97
+ border: `1px solid ${theme.borderDefault}`,
98
+ backgroundColor: theme.bgGray50,
99
+ padding: "0 4px",
100
+ display: "flex",
101
+ alignItems: "center",
102
+ justifyContent: "center",
103
+ fontWeight: 500,
104
+ fontSize: 12,
105
+ width: 32
106
+ },
107
+ // 月列
108
+ monthColumn: {
109
+ position: "relative",
110
+ flex: 1,
111
+ minWidth: 0
112
+ },
113
+ // 月ヘッダー
114
+ monthHeader: {
115
+ height: 24,
116
+ border: `1px solid ${theme.borderDefault}`,
117
+ backgroundColor: theme.bgGray100,
118
+ padding: "0 4px",
119
+ display: "flex",
120
+ alignItems: "center",
121
+ justifyContent: "center",
122
+ fontWeight: 500,
123
+ fontSize: 12
124
+ },
125
+ // カレンダーセル(基本)
126
+ calendarCell: {
127
+ border: `1px solid ${theme.borderDefault}`,
128
+ position: "relative"
129
+ },
130
+ // イベントバーコンテナ
131
+ eventBarContainer: {
132
+ position: "absolute",
133
+ top: 24,
134
+ left: 0,
135
+ right: "20%",
136
+ padding: "0 2px",
137
+ pointerEvents: "none",
138
+ zIndex: 10
139
+ },
140
+ // イベントバー
141
+ eventBar: {
142
+ position: "absolute",
143
+ cursor: "grab",
144
+ transition: "filter 0.15s, z-index 0.15s",
145
+ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)"
146
+ },
147
+ // ドロップインジケーター
148
+ dropIndicator: {
149
+ position: "absolute",
150
+ left: 0,
151
+ right: 0,
152
+ pointerEvents: "none",
153
+ zIndex: 50
154
+ },
155
+ dropIndicatorInner: {
156
+ height: "100%",
157
+ border: `2px solid ${theme.borderDropHighlight}`,
158
+ backgroundColor: theme.bgDropHighlight,
159
+ borderRadius: 4
160
+ },
161
+ // フローティング日付表示
162
+ floatingDate: {
163
+ position: "fixed",
164
+ zIndex: 100,
165
+ pointerEvents: "none",
166
+ transform: "translateX(-50%)"
167
+ },
168
+ floatingDateContent: {
169
+ backgroundColor: theme.floatingDateBg,
170
+ color: theme.tooltipText,
171
+ fontSize: 14,
172
+ fontWeight: 700,
173
+ borderRadius: 8,
174
+ padding: "8px 12px",
175
+ boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)"
176
+ },
177
+ floatingDateSubtext: {
178
+ fontSize: 12,
179
+ fontWeight: 400,
180
+ opacity: 0.8
181
+ },
182
+ // リサイズ用フローティング表示
183
+ resizeFloatingContent: {
184
+ backgroundColor: theme.resizeFloatingBg,
185
+ color: theme.tooltipText,
186
+ fontSize: 14,
187
+ fontWeight: 700,
188
+ borderRadius: 8,
189
+ padding: "8px 12px",
190
+ boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)"
191
+ },
192
+ // ツールチップ
193
+ tooltip: {
194
+ position: "fixed",
195
+ zIndex: 200,
196
+ pointerEvents: "none"
197
+ },
198
+ tooltipContent: {
199
+ backgroundColor: theme.tooltipBg,
200
+ color: theme.tooltipText,
201
+ fontSize: 12,
202
+ borderRadius: 4,
203
+ padding: "6px 8px",
204
+ whiteSpace: "nowrap",
205
+ boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)"
206
+ },
207
+ tooltipTitle: {
208
+ fontWeight: 500
209
+ },
210
+ tooltipDate: {
211
+ color: "rgba(255, 255, 255, 0.7)",
212
+ fontSize: 10,
213
+ marginTop: 2
214
+ },
215
+ // 確認ダイアログ
216
+ dialogOverlay: {
217
+ position: "fixed",
218
+ inset: 0,
219
+ zIndex: 100,
220
+ display: "flex",
221
+ alignItems: "center",
222
+ justifyContent: "center",
223
+ backgroundColor: theme.dialogOverlay
224
+ },
225
+ dialogContent: {
226
+ backgroundColor: theme.bgWhite,
227
+ borderRadius: 8,
228
+ boxShadow: "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
229
+ padding: 16,
230
+ maxWidth: 384,
231
+ margin: "0 16px"
232
+ },
233
+ dialogTitle: {
234
+ fontWeight: 700,
235
+ color: theme.textPrimary,
236
+ marginBottom: 8
237
+ },
238
+ dialogBody: {
239
+ fontSize: 14,
240
+ color: theme.textSecondary,
241
+ marginBottom: 16
242
+ },
243
+ dialogEventTitle: {
244
+ fontWeight: 500,
245
+ color: theme.textPrimary
246
+ },
247
+ dialogDateLabel: {
248
+ color: theme.textMuted
249
+ },
250
+ dialogButtons: {
251
+ display: "flex",
252
+ gap: 8,
253
+ justifyContent: "flex-end"
254
+ },
255
+ dialogCancelButton: {
256
+ padding: "8px 16px",
257
+ fontSize: 14,
258
+ color: theme.textSecondary,
259
+ backgroundColor: "transparent",
260
+ border: "none",
261
+ borderRadius: 4,
262
+ cursor: "pointer"
263
+ },
264
+ dialogConfirmButton: {
265
+ padding: "8px 16px",
266
+ fontSize: 14,
267
+ color: theme.tooltipText,
268
+ backgroundColor: theme.buttonPrimary,
269
+ border: "none",
270
+ borderRadius: 4,
271
+ cursor: "pointer"
272
+ },
273
+ // リサイズハンドル
274
+ resizeHandle: {
275
+ position: "absolute",
276
+ left: 0,
277
+ right: 0,
278
+ height: 8,
279
+ cursor: "ns-resize",
280
+ opacity: 0,
281
+ backgroundColor: "rgba(255, 255, 255, 0.3)"
282
+ },
283
+ resizeHandleTop: {
284
+ top: 0,
285
+ borderTopLeftRadius: 4,
286
+ borderTopRightRadius: 4
287
+ },
288
+ resizeHandleBottom: {
289
+ bottom: 0,
290
+ borderBottomLeftRadius: 4,
291
+ borderBottomRightRadius: 4
292
+ },
293
+ // インジケーター(▲▼)
294
+ indicator: {
295
+ position: "absolute",
296
+ left: "50%",
297
+ transform: "translateX(-50%)",
298
+ fontSize: 8,
299
+ color: "rgba(255, 255, 255, 0.8)"
300
+ },
301
+ indicatorTop: {
302
+ top: -6
303
+ },
304
+ indicatorBottom: {
305
+ bottom: -6
306
+ },
307
+ // 縦書きタイトル
308
+ verticalTitle: {
309
+ position: "absolute",
310
+ inset: 0,
311
+ display: "flex",
312
+ alignItems: "center",
313
+ justifyContent: "center",
314
+ fontSize: 10,
315
+ color: "#ffffff",
316
+ fontWeight: 500,
317
+ overflow: "hidden",
318
+ writingMode: "vertical-rl"
319
+ },
320
+ // 横書きタイトル
321
+ horizontalTitle: {
322
+ position: "absolute",
323
+ inset: 0,
324
+ display: "flex",
325
+ alignItems: "center",
326
+ justifyContent: "center",
327
+ fontSize: 9,
328
+ color: "#ffffff",
329
+ fontWeight: 500,
330
+ overflow: "hidden",
331
+ padding: "0 2px"
332
+ },
333
+ truncate: {
334
+ overflow: "hidden",
335
+ textOverflow: "ellipsis",
336
+ whiteSpace: "nowrap"
337
+ }
338
+ };
339
+ }
340
+ function getCellBackgroundColor(theme, isValidDay, isSunday2, isSaturday2, isHovered) {
341
+ if (!isValidDay) return theme.bgGray100;
342
+ if (isHovered) return theme.bgGray50;
343
+ if (isSunday2) return theme.bgSunday;
344
+ if (isSaturday2) return theme.bgSaturday;
345
+ return theme.bgWhite;
346
+ }
347
+ var MONTHS = [
348
+ "1\u6708",
349
+ "2\u6708",
350
+ "3\u6708",
351
+ "4\u6708",
352
+ "5\u6708",
353
+ "6\u6708",
354
+ "7\u6708",
355
+ "8\u6708",
356
+ "9\u6708",
357
+ "10\u6708",
358
+ "11\u6708",
359
+ "12\u6708"
360
+ ];
361
+ var DAYS = Array.from({ length: 31 }, (_, i) => i + 1);
362
+ var MIN_ROW_HEIGHT = 20;
363
+ var HEADER_HEIGHT = 24;
364
+ function getDaysInMonth(year, month) {
365
+ return new Date(year, month + 1, 0).getDate();
366
+ }
367
+ function isSunday(year, month, day) {
368
+ return new Date(year, month, day).getDay() === 0;
369
+ }
370
+ function isSaturday(year, month, day) {
371
+ return new Date(year, month, day).getDay() === 6;
372
+ }
373
+ function getMonthSpans(events, year, month) {
374
+ const spans = [];
375
+ const daysInMonth = getDaysInMonth(year, month);
376
+ events.forEach((event) => {
377
+ const startDate = event.date;
378
+ const endDate = event.endDate || event.date;
379
+ const monthStart = new Date(year, month, 1);
380
+ const monthEnd = new Date(year, month, daysInMonth, 23, 59, 59);
381
+ if (startDate > monthEnd || endDate < monthStart) {
382
+ return;
383
+ }
384
+ const spanStart = startDate >= monthStart ? startDate.getDate() : 1;
385
+ const spanEnd = endDate <= monthEnd ? endDate.getDate() : daysInMonth;
386
+ spans.push({
387
+ event,
388
+ startDay: spanStart,
389
+ endDay: spanEnd,
390
+ isStart: startDate.getFullYear() === year && startDate.getMonth() === month,
391
+ isEnd: endDate.getFullYear() === year && endDate.getMonth() === month
392
+ });
393
+ });
394
+ return spans;
395
+ }
396
+ function calculateLanes(spans) {
397
+ const laneMap = /* @__PURE__ */ new Map();
398
+ const lanes = [];
399
+ const sorted = [...spans].sort((a, b) => a.startDay - b.startDay);
400
+ sorted.forEach((span) => {
401
+ let laneIndex = lanes.findIndex((lane) => lane.endDay < span.startDay);
402
+ if (laneIndex === -1) {
403
+ laneIndex = lanes.length;
404
+ lanes.push({ endDay: span.endDay });
405
+ } else {
406
+ lanes[laneIndex].endDay = span.endDay;
407
+ }
408
+ laneMap.set(span.event.id, laneIndex);
409
+ });
410
+ return laneMap;
411
+ }
412
+ function YearlyCalendar({
413
+ year,
414
+ events = [],
415
+ onDateClick,
416
+ onEventClick,
417
+ onEventMove,
418
+ categoryColors = {},
419
+ theme: customTheme
420
+ }) {
421
+ const theme = react.useMemo(() => mergeTheme(customTheme), [customTheme]);
422
+ const styles = react.useMemo(() => createStyles(theme), [theme]);
423
+ const containerRef = react.useRef(null);
424
+ const [rowHeight, setRowHeight] = react.useState(MIN_ROW_HEIGHT);
425
+ react.useEffect(() => {
426
+ const updateRowHeight = () => {
427
+ if (containerRef.current) {
428
+ const containerHeight = containerRef.current.clientHeight;
429
+ const calculatedHeight = Math.floor(
430
+ (containerHeight - HEADER_HEIGHT) / 31
431
+ );
432
+ setRowHeight(Math.max(calculatedHeight, MIN_ROW_HEIGHT));
433
+ }
434
+ };
435
+ updateRowHeight();
436
+ window.addEventListener("resize", updateRowHeight);
437
+ return () => window.removeEventListener("resize", updateRowHeight);
438
+ }, []);
439
+ const [draggingEvent, setDraggingEvent] = react.useState(
440
+ null
441
+ );
442
+ const [dragOffset, setDragOffset] = react.useState(0);
443
+ const [dragOverCell, setDragOverCell] = react.useState(null);
444
+ const [dragPosition, setDragPosition] = react.useState(null);
445
+ const [dragGrabOffsetY, setDragGrabOffsetY] = react.useState(0);
446
+ const [dragCardHeight, setDragCardHeight] = react.useState(0);
447
+ const [pendingMove, setPendingMove] = react.useState(null);
448
+ const [hoveredEvent, setHoveredEvent] = react.useState(null);
449
+ const [resizingEvent, setResizingEvent] = react.useState(null);
450
+ const [hoveredCell, setHoveredCell] = react.useState(null);
451
+ const [hoveredEventBar, setHoveredEventBar] = react.useState(null);
452
+ const [hoveredResizeHandle, setHoveredResizeHandle] = react.useState(null);
453
+ const [hoveredCancelButton, setHoveredCancelButton] = react.useState(false);
454
+ const [hoveredConfirmButton, setHoveredConfirmButton] = react.useState(false);
455
+ const monthSpans = react.useMemo(() => {
456
+ const result = [];
457
+ for (let month = 0; month < 12; month++) {
458
+ result.push(getMonthSpans(events, year, month));
459
+ }
460
+ return result;
461
+ }, [events, year]);
462
+ const laneMaps = react.useMemo(() => {
463
+ return monthSpans.map((spans) => calculateLanes(spans));
464
+ }, [monthSpans]);
465
+ const maxLanes = react.useMemo(() => {
466
+ return laneMaps.map((laneMap) => {
467
+ let max = 0;
468
+ laneMap.forEach((lane) => {
469
+ max = Math.max(max, lane + 1);
470
+ });
471
+ return Math.max(max, 1);
472
+ });
473
+ }, [laneMaps]);
474
+ const resizePreviewSpans = react.useMemo(() => {
475
+ if (!resizingEvent || !dragOverCell) return null;
476
+ const { event, edge } = resizingEvent;
477
+ const dropDate = new Date(year, dragOverCell.month, dragOverCell.day);
478
+ let newStartDate = new Date(event.date);
479
+ let newEndDate = new Date(event.endDate || event.date);
480
+ if (edge === "start") {
481
+ if (dropDate <= newEndDate) {
482
+ newStartDate = dropDate;
483
+ }
484
+ } else {
485
+ if (dropDate >= newStartDate) {
486
+ newEndDate = dropDate;
487
+ }
488
+ }
489
+ const spans = /* @__PURE__ */ new Map();
490
+ for (let month = 0; month < 12; month++) {
491
+ const daysInMonth = getDaysInMonth(year, month);
492
+ const monthStart = new Date(year, month, 1);
493
+ const monthEnd = new Date(year, month, daysInMonth, 23, 59, 59);
494
+ if (newStartDate > monthEnd || newEndDate < monthStart) continue;
495
+ const spanStart = newStartDate >= monthStart ? newStartDate.getDate() : 1;
496
+ const spanEnd = newEndDate <= monthEnd ? newEndDate.getDate() : daysInMonth;
497
+ spans.set(month, {
498
+ event,
499
+ startDay: spanStart,
500
+ endDay: spanEnd,
501
+ isStart: newStartDate.getFullYear() === year && newStartDate.getMonth() === month,
502
+ isEnd: newEndDate.getFullYear() === year && newEndDate.getMonth() === month
503
+ });
504
+ }
505
+ return spans;
506
+ }, [resizingEvent, dragOverCell, year]);
507
+ const getEventColor = (event) => {
508
+ if (event.color) return event.color;
509
+ if (event.category && categoryColors[event.category]) {
510
+ return categoryColors[event.category];
511
+ }
512
+ return "#3b82f6";
513
+ };
514
+ const eventsByDate = react.useMemo(() => {
515
+ const map = /* @__PURE__ */ new Map();
516
+ events.forEach((event) => {
517
+ const startDate = event.date;
518
+ const endDate = event.endDate || event.date;
519
+ const current = new Date(startDate);
520
+ while (current <= endDate) {
521
+ const dateKey = `${current.getFullYear()}-${current.getMonth()}-${current.getDate()}`;
522
+ const existing = map.get(dateKey) || [];
523
+ map.set(dateKey, [...existing, event]);
524
+ current.setDate(current.getDate() + 1);
525
+ }
526
+ });
527
+ return map;
528
+ }, [events]);
529
+ const getEventsForDate = (month, day) => {
530
+ const dateKey = `${year}-${month}-${day}`;
531
+ return eventsByDate.get(dateKey) || [];
532
+ };
533
+ const handleCellClick = (month, day) => {
534
+ if (!onDateClick) return;
535
+ const daysInMonth = getDaysInMonth(year, month);
536
+ if (day > daysInMonth) return;
537
+ const date = new Date(year, month, day);
538
+ const cellEvents = getEventsForDate(month, day);
539
+ onDateClick(date, cellEvents);
540
+ };
541
+ const handleDragStart = react.useCallback(
542
+ (e, event, monthIndex, spanStartDay) => {
543
+ e.dataTransfer.effectAllowed = "move";
544
+ e.dataTransfer.setData("text/plain", event.id);
545
+ const rect = e.currentTarget.getBoundingClientRect();
546
+ const clickY = e.clientY - rect.top;
547
+ const dayOffset = Math.floor(clickY / rowHeight);
548
+ const grabDay = spanStartDay + dayOffset;
549
+ setDragGrabOffsetY(clickY);
550
+ setDragCardHeight(rect.height);
551
+ const grabDate = new Date(year, monthIndex, grabDay);
552
+ const startDate = event.date;
553
+ const offsetDays = Math.floor(
554
+ (grabDate.getTime() - startDate.getTime()) / (1e3 * 60 * 60 * 24)
555
+ );
556
+ setDragOffset(offsetDays);
557
+ requestAnimationFrame(() => {
558
+ setDraggingEvent(event);
559
+ });
560
+ },
561
+ [year, rowHeight]
562
+ );
563
+ const handleDragOver = react.useCallback(
564
+ (e, month, day) => {
565
+ e.preventDefault();
566
+ e.dataTransfer.dropEffect = "move";
567
+ if (draggingEvent && dragGrabOffsetY > 0) {
568
+ const dayOffset = Math.floor(dragGrabOffsetY / rowHeight);
569
+ let cardTopDay = day - dayOffset;
570
+ let cardTopMonth = month;
571
+ while (cardTopDay < 1 && cardTopMonth > 0) {
572
+ cardTopMonth--;
573
+ cardTopDay += getDaysInMonth(year, cardTopMonth);
574
+ }
575
+ let daysInMonth = getDaysInMonth(year, cardTopMonth);
576
+ while (cardTopDay > daysInMonth && cardTopMonth < 11) {
577
+ cardTopDay -= daysInMonth;
578
+ cardTopMonth++;
579
+ daysInMonth = getDaysInMonth(year, cardTopMonth);
580
+ }
581
+ cardTopDay = Math.max(
582
+ 1,
583
+ Math.min(cardTopDay, getDaysInMonth(year, cardTopMonth))
584
+ );
585
+ setDragOverCell({ month: cardTopMonth, day: cardTopDay });
586
+ } else {
587
+ setDragOverCell({ month, day });
588
+ }
589
+ setDragPosition({ x: e.clientX, y: e.clientY });
590
+ },
591
+ [draggingEvent, dragGrabOffsetY, year, rowHeight]
592
+ );
593
+ const handleDragLeave = react.useCallback(() => {
594
+ setDragOverCell(null);
595
+ }, []);
596
+ const handleDrop = react.useCallback(
597
+ (e) => {
598
+ e.preventDefault();
599
+ if (draggingEvent && onEventMove && dragOverCell) {
600
+ const newStartDate = new Date(
601
+ year,
602
+ dragOverCell.month,
603
+ dragOverCell.day
604
+ );
605
+ let newEndDate;
606
+ if (draggingEvent.endDate) {
607
+ const duration = draggingEvent.endDate.getTime() - draggingEvent.date.getTime();
608
+ newEndDate = new Date(newStartDate.getTime() + duration);
609
+ }
610
+ onEventMove(draggingEvent, newStartDate, newEndDate);
611
+ }
612
+ setDragOverCell(null);
613
+ setDragPosition(null);
614
+ setDraggingEvent(null);
615
+ setDragOffset(0);
616
+ },
617
+ [draggingEvent, dragOverCell, year, onEventMove]
618
+ );
619
+ const handleDragEnd = react.useCallback(() => {
620
+ setDraggingEvent(null);
621
+ setDragOverCell(null);
622
+ setDragPosition(null);
623
+ setDragGrabOffsetY(0);
624
+ setDragCardHeight(0);
625
+ }, []);
626
+ const handleConfirmMove = react.useCallback(() => {
627
+ if (pendingMove && onEventMove) {
628
+ onEventMove(
629
+ pendingMove.event,
630
+ pendingMove.newStartDate,
631
+ pendingMove.newEndDate
632
+ );
633
+ }
634
+ setPendingMove(null);
635
+ }, [pendingMove, onEventMove]);
636
+ const handleCancelMove = react.useCallback(() => {
637
+ setPendingMove(null);
638
+ }, []);
639
+ const handleResizeStart = react.useCallback(
640
+ (e, event, edge) => {
641
+ e.stopPropagation();
642
+ e.dataTransfer.effectAllowed = "move";
643
+ e.dataTransfer.setData("text/plain", `resize-${event.id}`);
644
+ requestAnimationFrame(() => {
645
+ setResizingEvent({ event, edge });
646
+ });
647
+ },
648
+ []
649
+ );
650
+ const handleResizeDrop = react.useCallback(
651
+ (month, day) => {
652
+ if (!resizingEvent || !onEventMove) return;
653
+ const { event, edge } = resizingEvent;
654
+ const dropDate = new Date(year, month, day);
655
+ let newStartDate = event.date;
656
+ let newEndDate = event.endDate || event.date;
657
+ if (edge === "start") {
658
+ if (dropDate <= newEndDate) {
659
+ newStartDate = dropDate;
660
+ }
661
+ } else {
662
+ if (dropDate >= newStartDate) {
663
+ newEndDate = dropDate;
664
+ }
665
+ }
666
+ const finalEndDate = newStartDate.getTime() === newEndDate.getTime() ? void 0 : newEndDate;
667
+ onEventMove(event, newStartDate, finalEndDate);
668
+ setResizingEvent(null);
669
+ setDragOverCell(null);
670
+ setDragPosition(null);
671
+ },
672
+ [resizingEvent, year, onEventMove]
673
+ );
674
+ const handleResizeEnd = react.useCallback(() => {
675
+ setResizingEvent(null);
676
+ setDragOverCell(null);
677
+ setDragPosition(null);
678
+ }, []);
679
+ react.useEffect(() => {
680
+ const handleGlobalMouseUp = () => {
681
+ setResizingEvent(null);
682
+ setDraggingEvent(null);
683
+ setDragOverCell(null);
684
+ setDragPosition(null);
685
+ setDragGrabOffsetY(0);
686
+ setDragCardHeight(0);
687
+ setDragOffset(0);
688
+ };
689
+ window.addEventListener("mouseup", handleGlobalMouseUp);
690
+ window.addEventListener("dragend", handleGlobalMouseUp);
691
+ return () => {
692
+ window.removeEventListener("mouseup", handleGlobalMouseUp);
693
+ window.removeEventListener("dragend", handleGlobalMouseUp);
694
+ };
695
+ }, []);
696
+ const getWeekdayName = (month, day) => {
697
+ const date = new Date(year, month, day);
698
+ return ["\u65E5", "\u6708", "\u706B", "\u6C34", "\u6728", "\u91D1", "\u571F"][date.getDay()];
699
+ };
700
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: styles.container, children: [
701
+ draggingEvent && dragOverCell && dragPosition && (() => {
702
+ const cardTop = dragPosition.y - dragGrabOffsetY;
703
+ const cardBottom = cardTop + dragCardHeight;
704
+ const showBelow = cardTop < 80;
705
+ return /* @__PURE__ */ jsxRuntime.jsx(
706
+ "div",
707
+ {
708
+ style: __spreadProps(__spreadValues({}, styles.floatingDate), {
709
+ left: dragPosition.x,
710
+ top: showBelow ? cardBottom + 8 : cardTop - 8,
711
+ transform: showBelow ? "translateX(-50%)" : "translateX(-50%) translateY(-100%)"
712
+ }),
713
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.floatingDateContent, children: [
714
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
715
+ dragOverCell.month + 1,
716
+ "\u6708",
717
+ dragOverCell.day,
718
+ "\u65E5(",
719
+ getWeekdayName(dragOverCell.month, dragOverCell.day),
720
+ ")"
721
+ ] }),
722
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.floatingDateSubtext, children: draggingEvent.title })
723
+ ] })
724
+ }
725
+ );
726
+ })(),
727
+ resizingEvent && dragOverCell && dragPosition && /* @__PURE__ */ jsxRuntime.jsx(
728
+ "div",
729
+ {
730
+ style: __spreadProps(__spreadValues({}, styles.floatingDate), {
731
+ left: dragPosition.x,
732
+ top: dragPosition.y - 50
733
+ }),
734
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.resizeFloatingContent, children: [
735
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
736
+ resizingEvent.edge === "start" ? "\u958B\u59CB\u65E5\uFF1A" : "\u7D42\u4E86\u65E5\uFF1A",
737
+ dragOverCell.month + 1,
738
+ "\u6708",
739
+ dragOverCell.day,
740
+ "\u65E5(",
741
+ getWeekdayName(dragOverCell.month, dragOverCell.day),
742
+ ")"
743
+ ] }),
744
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.floatingDateSubtext, children: resizingEvent.event.title })
745
+ ] })
746
+ }
747
+ ),
748
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.flexContainer, children: [
749
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.dayColumn, children: [
750
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.dayHeaderCell, children: "\u65E5" }),
751
+ DAYS.map((day) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: __spreadProps(__spreadValues({}, styles.dayCell), { height: rowHeight }), children: day }, day))
752
+ ] }),
753
+ MONTHS.map((monthName, monthIndex) => {
754
+ const daysInMonth = getDaysInMonth(year, monthIndex);
755
+ const spans = monthSpans[monthIndex];
756
+ const laneMap = laneMaps[monthIndex];
757
+ const laneCount = maxLanes[monthIndex];
758
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.monthColumn, children: [
759
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.monthHeader, children: monthName }),
760
+ DAYS.map((day) => {
761
+ const isValidDay = day <= daysInMonth;
762
+ const sunday = isValidDay && isSunday(year, monthIndex, day);
763
+ const saturday = isValidDay && isSaturday(year, monthIndex, day);
764
+ const isHovered = (hoveredCell == null ? void 0 : hoveredCell.month) === monthIndex && (hoveredCell == null ? void 0 : hoveredCell.day) === day;
765
+ return /* @__PURE__ */ jsxRuntime.jsx(
766
+ "div",
767
+ {
768
+ style: __spreadProps(__spreadValues({}, styles.calendarCell), {
769
+ height: rowHeight,
770
+ backgroundColor: getCellBackgroundColor(
771
+ theme,
772
+ isValidDay,
773
+ sunday,
774
+ saturday,
775
+ isHovered && isValidDay
776
+ ),
777
+ cursor: isValidDay ? "pointer" : "default"
778
+ }),
779
+ onClick: () => isValidDay && handleCellClick(monthIndex, day),
780
+ onMouseEnter: () => isValidDay && setHoveredCell({ month: monthIndex, day }),
781
+ onMouseLeave: () => setHoveredCell(null),
782
+ onDragOver: (e) => isValidDay && handleDragOver(e, monthIndex, day),
783
+ onDragLeave: handleDragLeave,
784
+ onDrop: (e) => {
785
+ if (!isValidDay) return;
786
+ if (resizingEvent) {
787
+ handleResizeDrop(monthIndex, day);
788
+ } else {
789
+ handleDrop(e);
790
+ }
791
+ }
792
+ },
793
+ day
794
+ );
795
+ }),
796
+ (draggingEvent || resizingEvent) && (dragOverCell == null ? void 0 : dragOverCell.month) === monthIndex && dragOverCell.day <= daysInMonth && /* @__PURE__ */ jsxRuntime.jsx(
797
+ "div",
798
+ {
799
+ style: __spreadProps(__spreadValues({}, styles.dropIndicator), {
800
+ top: 24 + (dragOverCell.day - 1) * rowHeight,
801
+ height: rowHeight
802
+ }),
803
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.dropIndicatorInner })
804
+ }
805
+ ),
806
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.eventBarContainer, children: [
807
+ spans.map((span) => {
808
+ const laneIndex = laneMap.get(span.event.id) || 0;
809
+ const laneWidth = 100 / laneCount;
810
+ const left = laneIndex * laneWidth;
811
+ const isDraggingThis = (draggingEvent == null ? void 0 : draggingEvent.id) === span.event.id;
812
+ const isResizingThis = (resizingEvent == null ? void 0 : resizingEvent.event.id) === span.event.id;
813
+ let displaySpan = span;
814
+ if (isResizingThis && resizePreviewSpans) {
815
+ const previewSpan = resizePreviewSpans.get(monthIndex);
816
+ if (previewSpan) {
817
+ displaySpan = previewSpan;
818
+ } else {
819
+ return null;
820
+ }
821
+ }
822
+ const displayStartDay = displaySpan.startDay;
823
+ const displayEndDay = displaySpan.endDay;
824
+ const top = (displayStartDay - 1) * rowHeight + 2;
825
+ const height = (displayEndDay - displayStartDay + 1) * rowHeight - 4;
826
+ const spanDays = displayEndDay - displayStartDay + 1;
827
+ const isInteractive = !draggingEvent && !resizingEvent;
828
+ const isHovered = hoveredEventBar === span.event.id;
829
+ return /* @__PURE__ */ jsxRuntime.jsxs(
830
+ "div",
831
+ {
832
+ style: __spreadProps(__spreadValues({}, styles.eventBar), {
833
+ top,
834
+ left: `${left}%`,
835
+ width: `calc(${laneWidth}% - 4px)`,
836
+ height,
837
+ backgroundColor: getEventColor(span.event),
838
+ borderRadius: `${span.isStart ? 4 : 0}px ${span.isStart ? 4 : 0}px ${span.isEnd ? 4 : 0}px ${span.isEnd ? 4 : 0}px`,
839
+ opacity: isDraggingThis ? 0.3 : 1,
840
+ border: isResizingThis ? "2px dashed rgba(255, 255, 255, 0.8)" : "none",
841
+ zIndex: isResizingThis ? 30 : isHovered ? 50 : 10,
842
+ filter: isHovered ? "brightness(1.1)" : "none",
843
+ pointerEvents: isInteractive ? "auto" : "none"
844
+ }),
845
+ draggable: true,
846
+ onDragStart: (e) => handleDragStart(
847
+ e,
848
+ span.event,
849
+ monthIndex,
850
+ span.startDay
851
+ ),
852
+ onDragEnd: handleDragEnd,
853
+ onMouseEnter: (e) => {
854
+ setHoveredEventBar(span.event.id);
855
+ const rect = e.currentTarget.getBoundingClientRect();
856
+ setHoveredEvent({
857
+ event: span.event,
858
+ x: rect.right + 8,
859
+ y: rect.top
860
+ });
861
+ },
862
+ onMouseLeave: () => {
863
+ setHoveredEventBar(null);
864
+ setHoveredEvent(null);
865
+ },
866
+ onClick: (e) => {
867
+ e.stopPropagation();
868
+ onEventClick == null ? void 0 : onEventClick(span.event);
869
+ },
870
+ children: [
871
+ !span.isStart && /* @__PURE__ */ jsxRuntime.jsx("div", { style: __spreadValues(__spreadValues({}, styles.indicator), styles.indicatorTop), children: "\u25B2" }),
872
+ span.isStart && spanDays >= 2 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.verticalTitle, children: /* @__PURE__ */ jsxRuntime.jsx(
873
+ "span",
874
+ {
875
+ style: __spreadProps(__spreadValues({}, styles.truncate), {
876
+ maxHeight: "100%",
877
+ padding: "0 2px"
878
+ }),
879
+ children: span.event.title
880
+ }
881
+ ) }),
882
+ span.isStart && spanDays === 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.horizontalTitle, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.truncate, children: span.event.title }) }),
883
+ !span.isEnd && /* @__PURE__ */ jsxRuntime.jsx("div", { style: __spreadValues(__spreadValues({}, styles.indicator), styles.indicatorBottom), children: "\u25BC" }),
884
+ span.event.endDate && isInteractive && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
885
+ span.isStart && /* @__PURE__ */ jsxRuntime.jsx(
886
+ "div",
887
+ {
888
+ style: __spreadProps(__spreadValues(__spreadValues({}, styles.resizeHandle), styles.resizeHandleTop), {
889
+ opacity: (hoveredResizeHandle == null ? void 0 : hoveredResizeHandle.eventId) === span.event.id && (hoveredResizeHandle == null ? void 0 : hoveredResizeHandle.edge) === "start" ? 1 : 0
890
+ }),
891
+ draggable: true,
892
+ onDragStart: (e) => {
893
+ e.stopPropagation();
894
+ handleResizeStart(e, span.event, "start");
895
+ },
896
+ onDragEnd: handleResizeEnd,
897
+ onMouseEnter: () => setHoveredResizeHandle({
898
+ eventId: span.event.id,
899
+ edge: "start"
900
+ }),
901
+ onMouseLeave: () => setHoveredResizeHandle(null)
902
+ }
903
+ ),
904
+ span.isEnd && /* @__PURE__ */ jsxRuntime.jsx(
905
+ "div",
906
+ {
907
+ style: __spreadProps(__spreadValues(__spreadValues({}, styles.resizeHandle), styles.resizeHandleBottom), {
908
+ opacity: (hoveredResizeHandle == null ? void 0 : hoveredResizeHandle.eventId) === span.event.id && (hoveredResizeHandle == null ? void 0 : hoveredResizeHandle.edge) === "end" ? 1 : 0
909
+ }),
910
+ draggable: true,
911
+ onDragStart: (e) => {
912
+ e.stopPropagation();
913
+ handleResizeStart(e, span.event, "end");
914
+ },
915
+ onDragEnd: handleResizeEnd,
916
+ onMouseEnter: () => setHoveredResizeHandle({
917
+ eventId: span.event.id,
918
+ edge: "end"
919
+ }),
920
+ onMouseLeave: () => setHoveredResizeHandle(null)
921
+ }
922
+ )
923
+ ] })
924
+ ]
925
+ },
926
+ span.event.id
927
+ );
928
+ }),
929
+ resizingEvent && resizePreviewSpans && resizePreviewSpans.has(monthIndex) && !spans.some((s) => s.event.id === resizingEvent.event.id) && (() => {
930
+ const previewSpan = resizePreviewSpans.get(monthIndex);
931
+ let originalLaneIndex = 0;
932
+ for (let m = 0; m < 12; m++) {
933
+ const lane = laneMaps[m].get(resizingEvent.event.id);
934
+ if (lane !== void 0) {
935
+ originalLaneIndex = lane;
936
+ break;
937
+ }
938
+ }
939
+ const previewLaneCount = maxLanes[monthIndex];
940
+ const laneWidth = 100 / previewLaneCount;
941
+ const left = Math.min(originalLaneIndex, previewLaneCount - 1) * laneWidth;
942
+ const top = (previewSpan.startDay - 1) * rowHeight + 2;
943
+ const height = (previewSpan.endDay - previewSpan.startDay + 1) * rowHeight - 4;
944
+ return /* @__PURE__ */ jsxRuntime.jsxs(
945
+ "div",
946
+ {
947
+ style: __spreadProps(__spreadValues({}, styles.eventBar), {
948
+ top,
949
+ left: `${left}%`,
950
+ width: `calc(${laneWidth}% - 4px)`,
951
+ height,
952
+ backgroundColor: getEventColor(resizingEvent.event),
953
+ borderRadius: `${previewSpan.isStart ? 4 : 0}px ${previewSpan.isStart ? 4 : 0}px ${previewSpan.isEnd ? 4 : 0}px ${previewSpan.isEnd ? 4 : 0}px`,
954
+ border: "2px dashed rgba(255, 255, 255, 0.8)",
955
+ opacity: 0.7,
956
+ zIndex: 30
957
+ }),
958
+ children: [
959
+ !previewSpan.isStart && /* @__PURE__ */ jsxRuntime.jsx("div", { style: __spreadValues(__spreadValues({}, styles.indicator), styles.indicatorTop), children: "\u25B2" }),
960
+ !previewSpan.isEnd && /* @__PURE__ */ jsxRuntime.jsx("div", { style: __spreadValues(__spreadValues({}, styles.indicator), styles.indicatorBottom), children: "\u25BC" })
961
+ ]
962
+ },
963
+ `preview-${resizingEvent.event.id}`
964
+ );
965
+ })()
966
+ ] })
967
+ ] }, monthIndex);
968
+ })
969
+ ] }),
970
+ hoveredEvent && !draggingEvent && !resizingEvent && /* @__PURE__ */ jsxRuntime.jsx(
971
+ "div",
972
+ {
973
+ style: __spreadProps(__spreadValues({}, styles.tooltip), {
974
+ left: hoveredEvent.x,
975
+ top: hoveredEvent.y
976
+ }),
977
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.tooltipContent, children: [
978
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.tooltipTitle, children: hoveredEvent.event.title }),
979
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.tooltipDate, children: [
980
+ hoveredEvent.event.date.toLocaleDateString("ja-JP", {
981
+ month: "short",
982
+ day: "numeric",
983
+ weekday: "short"
984
+ }),
985
+ hoveredEvent.event.endDate && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
986
+ " \u301C ",
987
+ hoveredEvent.event.endDate.toLocaleDateString("ja-JP", {
988
+ month: "short",
989
+ day: "numeric",
990
+ weekday: "short"
991
+ })
992
+ ] })
993
+ ] })
994
+ ] })
995
+ }
996
+ ),
997
+ pendingMove && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.dialogOverlay, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.dialogContent, children: [
998
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { style: styles.dialogTitle, children: pendingMove.isResize ? "\u671F\u9593\u3092\u5909\u66F4\u3057\u307E\u3059\u304B\uFF1F" : "\u4E88\u5B9A\u3092\u79FB\u52D5\u3057\u307E\u3059\u304B\uFF1F" }),
999
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.dialogBody, children: [
1000
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.dialogEventTitle, children: pendingMove.event.title }),
1001
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 4 }, children: [
1002
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.dialogDateLabel, children: "\u5909\u66F4\u524D\uFF1A" }),
1003
+ pendingMove.event.date.toLocaleDateString("ja-JP", {
1004
+ month: "long",
1005
+ day: "numeric",
1006
+ weekday: "short"
1007
+ }),
1008
+ pendingMove.event.endDate && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1009
+ " \u301C ",
1010
+ pendingMove.event.endDate.toLocaleDateString("ja-JP", {
1011
+ month: "long",
1012
+ day: "numeric",
1013
+ weekday: "short"
1014
+ })
1015
+ ] })
1016
+ ] }),
1017
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 4 }, children: [
1018
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.dialogDateLabel, children: "\u5909\u66F4\u5F8C\uFF1A" }),
1019
+ pendingMove.newStartDate.toLocaleDateString("ja-JP", {
1020
+ month: "long",
1021
+ day: "numeric",
1022
+ weekday: "short"
1023
+ }),
1024
+ pendingMove.newEndDate && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1025
+ " \u301C ",
1026
+ pendingMove.newEndDate.toLocaleDateString("ja-JP", {
1027
+ month: "long",
1028
+ day: "numeric",
1029
+ weekday: "short"
1030
+ })
1031
+ ] })
1032
+ ] })
1033
+ ] }),
1034
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.dialogButtons, children: [
1035
+ /* @__PURE__ */ jsxRuntime.jsx(
1036
+ "button",
1037
+ {
1038
+ onClick: handleCancelMove,
1039
+ style: __spreadProps(__spreadValues({}, styles.dialogCancelButton), {
1040
+ backgroundColor: hoveredCancelButton ? theme.bgGray100 : "transparent"
1041
+ }),
1042
+ onMouseEnter: () => setHoveredCancelButton(true),
1043
+ onMouseLeave: () => setHoveredCancelButton(false),
1044
+ children: "\u30AD\u30E3\u30F3\u30BB\u30EB"
1045
+ }
1046
+ ),
1047
+ /* @__PURE__ */ jsxRuntime.jsx(
1048
+ "button",
1049
+ {
1050
+ onClick: handleConfirmMove,
1051
+ style: __spreadProps(__spreadValues({}, styles.dialogConfirmButton), {
1052
+ backgroundColor: hoveredConfirmButton ? theme.buttonPrimaryHover : theme.buttonPrimary
1053
+ }),
1054
+ onMouseEnter: () => setHoveredConfirmButton(true),
1055
+ onMouseLeave: () => setHoveredConfirmButton(false),
1056
+ children: pendingMove.isResize ? "\u5909\u66F4\u3059\u308B" : "\u79FB\u52D5\u3059\u308B"
1057
+ }
1058
+ )
1059
+ ] })
1060
+ ] }) })
1061
+ ] });
1062
+ }
1063
+
1064
+ exports.YearlyCalendar = YearlyCalendar;
1065
+ exports.createStyles = createStyles;
1066
+ exports.defaultTheme = defaultTheme;
1067
+ exports.mergeTheme = mergeTheme;
1068
+ //# sourceMappingURL=index.js.map
1069
+ //# sourceMappingURL=index.js.map