diff --git a/public/index.html b/public/index.html
index 90cace4..65fbf91 100644
--- a/public/index.html
+++ b/public/index.html
@@ -26,7 +26,11 @@
href="https://fonts.googleapis.com/css2?family=Cal+Sans&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap"
rel="stylesheet"
/>
-
Twake Calendar
+
+ React App
diff --git a/src/components/Calendar/Calendar.css b/src/components/Calendar/Calendar.css
index eed1f96..22c0ded 100644
--- a/src/components/Calendar/Calendar.css
+++ b/src/components/Calendar/Calendar.css
@@ -15,7 +15,6 @@
flex-grow: 1;
height: 100%;
overflow: hidden;
- padding: 0 25px 0 10px;
}
.calendarListHeader {
@@ -42,18 +41,6 @@
border: dashed 1px #ffffff;
}
-.fc .fc-timegrid-slot {
- height: auto;
- min-height: 54px !important;
-}
-
-.fc .fc-timegrid-slot-label {
- display: flex;
- align-items: flex-start;
- padding-top: 2px;
- height: 100%;
-}
-
.fc-event-main span,
.fc-daygrid-event div {
display: block !important;
diff --git a/src/components/Calendar/Calendar.tsx b/src/components/Calendar/Calendar.tsx
index 5ec75ee..794d436 100644
--- a/src/components/Calendar/Calendar.tsx
+++ b/src/components/Calendar/Calendar.tsx
@@ -320,7 +320,7 @@ export default function CalendarApp() {
}}
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
initialView="timeGridWeek"
- firstDay={1}
+ firstDay={0}
editable={true}
selectable={true}
timeZone="local"
@@ -361,8 +361,8 @@ export default function CalendarApp() {
)}
weekNumbers
weekNumberFormat={{ week: "long" }}
- slotDuration={"00:30:00"}
- slotLabelInterval={"00:30:00"}
+ slotDuration={"01:00:00"}
+ slotLabelInterval={"01:00:00"}
scrollTime={new Date(Date.now() - 2 * 60 * 60 * 1000)
.toTimeString()
.slice(0, 5)}
@@ -402,6 +402,159 @@ export default function CalendarApp() {
);
}}
+ dayHeaderDidMount={(arg) => {
+ // Add click handler to day headers in week view
+ if (arg.view.type === "timeGridWeek") {
+ const headerEl = arg.el;
+
+ const handleDayHeaderClick = () => {
+ // Switch to day view and navigate to the clicked date
+ calendarRef.current?.changeView("timeGridDay", arg.date);
+ setSelectedDate(new Date(arg.date));
+ setSelectedMiniDate(new Date(arg.date));
+ };
+
+ headerEl.addEventListener("click", handleDayHeaderClick);
+
+ // Store the handler for cleanup
+ (headerEl as any).__dayHeaderClickHandler = handleDayHeaderClick;
+ }
+ }}
+ dayHeaderWillUnmount={(arg) => {
+ // Clean up event listeners to prevent memory leaks
+ const headerEl = arg.el;
+ if ((headerEl as any).__dayHeaderClickHandler) {
+ headerEl.removeEventListener(
+ "click",
+ (headerEl as any).__dayHeaderClickHandler
+ );
+ delete (headerEl as any).__dayHeaderClickHandler;
+ }
+ }}
+ viewDidMount={(arg) => {
+ // Add global hover effect for week and day views
+ if (
+ arg.view.type === "timeGridWeek" ||
+ arg.view.type === "timeGridDay"
+ ) {
+ const calendarEl = document.querySelector(".fc") as HTMLElement;
+ if (calendarEl) {
+ const handleMouseMove = (e: MouseEvent) => {
+ // Find the timegrid container
+ const timegridEl =
+ calendarEl.querySelector(".fc-timegrid-body");
+ if (!timegridEl) return;
+
+ // Check if mouse is over all-day events area (fc-scrollgrid-sync-table)
+ const allDayTable = calendarEl.querySelector(
+ ".fc-scrollgrid-sync-table"
+ );
+ if (allDayTable) {
+ const allDayRect = allDayTable.getBoundingClientRect();
+ if (
+ e.clientY >= allDayRect.top &&
+ e.clientY <= allDayRect.bottom
+ ) {
+ // Mouse is over all-day events area, don't show highlight
+ timegridEl
+ .querySelectorAll(".hour-highlight")
+ .forEach((el: Element) => el.remove());
+ return;
+ }
+ }
+
+ // Check if mouse is over time slot labels (left side with hours)
+ const target = e.target as HTMLElement;
+ if (target && target.closest(".fc-timegrid-slot-label")) {
+ // Mouse is over time slot labels, don't show highlight
+ timegridEl
+ .querySelectorAll(".hour-highlight")
+ .forEach((el: Element) => el.remove());
+ return;
+ }
+
+ // Get all day columns
+ const dayColumns =
+ timegridEl.querySelectorAll(".fc-timegrid-col");
+ if (dayColumns.length === 0) return;
+
+ // Remove previous highlights
+ timegridEl
+ .querySelectorAll(".hour-highlight")
+ .forEach((el: Element) => el.remove());
+
+ // Find which day column the mouse is over
+ let targetColumn: Element | null = null;
+ for (const column of dayColumns) {
+ const rect = column.getBoundingClientRect();
+ if (e.clientX >= rect.left && e.clientX <= rect.right) {
+ targetColumn = column;
+ break;
+ }
+ }
+
+ if (targetColumn) {
+ const rect = targetColumn.getBoundingClientRect();
+ const relativeY = e.clientY - rect.top;
+ const hourHeight = rect.height / 24; // Assuming 24 hours
+ const hourIndex = Math.floor(relativeY / hourHeight);
+
+ // Only show highlight if mouse is actually over the timegrid area (not all-day events)
+ if (relativeY >= 0 && relativeY <= rect.height) {
+ // Create highlight for the specific hour in the specific day
+ const highlight = document.createElement("div");
+ highlight.className = "hour-highlight";
+ highlight.style.top = `${hourIndex * hourHeight}px`;
+ highlight.style.height = `${hourHeight}px`;
+
+ (targetColumn as HTMLElement).style.position = "relative";
+ targetColumn.appendChild(highlight);
+ }
+ }
+ };
+
+ const handleMouseLeave = () => {
+ // Remove all hour highlights when mouse leaves calendar
+ const timegridEl =
+ calendarEl.querySelector(".fc-timegrid-body");
+ if (timegridEl) {
+ timegridEl
+ .querySelectorAll(".hour-highlight")
+ .forEach((el: Element) => el.remove());
+ }
+ };
+
+ calendarEl.addEventListener("mousemove", handleMouseMove);
+ calendarEl.addEventListener("mouseleave", handleMouseLeave);
+
+ // Store handlers for cleanup
+ (calendarEl as any).__calendarMouseMoveHandler =
+ handleMouseMove;
+ (calendarEl as any).__calendarMouseLeaveHandler =
+ handleMouseLeave;
+ }
+ }
+ }}
+ viewWillUnmount={(arg) => {
+ // Clean up event listeners to prevent memory leaks
+ const calendarEl = document.querySelector(".fc") as HTMLElement;
+ if (calendarEl) {
+ if ((calendarEl as any).__calendarMouseMoveHandler) {
+ calendarEl.removeEventListener(
+ "mousemove",
+ (calendarEl as any).__calendarMouseMoveHandler
+ );
+ delete (calendarEl as any).__calendarMouseMoveHandler;
+ }
+ if ((calendarEl as any).__calendarMouseLeaveHandler) {
+ calendarEl.removeEventListener(
+ "mouseleave",
+ (calendarEl as any).__calendarMouseLeaveHandler
+ );
+ delete (calendarEl as any).__calendarMouseLeaveHandler;
+ }
+ }
+ }}
eventClick={(info) => {
info.jsEvent.preventDefault(); // don't let the browser navigate
diff --git a/src/components/Calendar/CustomCalendar.css b/src/components/Calendar/CustomCalendar.css
index d892e1c..c950534 100644
--- a/src/components/Calendar/CustomCalendar.css
+++ b/src/components/Calendar/CustomCalendar.css
@@ -5,8 +5,11 @@
.fc-theme-standard td,
.fc-theme-standard th {
border: 1px solid #b8c1cc;
+}
+th.fc-col-header-cell.fc-day {
border-left: 0;
}
+
a.fc-timegrid-axis-cushion {
color: #243b55;
text-align: center;
@@ -24,11 +27,12 @@ a.fc-timegrid-axis-cushion {
font-weight: 500;
line-height: 20px;
letter-spacing: 0.25px;
+ font-family: "Inter", sans-serif;
}
.fc-daygrid-day-top .fc-daygrid-day-number,
span.fc-daygrid-day-number {
color: #243b55;
- margin: 0 8px 0 0;
+ margin: 0 6px 0 0;
font-family: Roboto;
font-size: 28px;
font-style: normal;
@@ -44,11 +48,12 @@ span.fc-daygrid-day-number:after {
content: "";
z-index: -1;
border-radius: 50%;
- width: 48px;
- height: 48px;
+ width: 45px;
+ height: 45px;
position: absolute;
- left: 0;
- top: -1px;
+ top: 1px;
+ left: 1px;
+ transition: background-color 0.3s ease;
}
span.fc-daygrid-day-number.current-date {
color: #fff;
@@ -65,10 +70,16 @@ th.fc-timegrid-axis.fc-scrollgrid-shrink {
border-right: 1px solid #b8c1cc;
}
.fc .fc-timegrid-axis-cushion {
- min-width: 70px;
- max-width: 70px;
+ min-width: 80px;
+ max-width: 80px;
padding: 0;
text-align: left;
+ font-family: Roboto;
+ font-size: 12.507px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 18.76px;
+ padding: 0 0 0 10px;
}
.fc .fc-timegrid-divider {
display: none;
@@ -85,6 +96,7 @@ th.fc-timegrid-axis.fc-scrollgrid-shrink {
}
th.fc-col-header-cell.fc-day {
border-right: 0;
+ border-bottom: 0;
}
.fc .fc-scrollgrid {
border: 0;
@@ -106,3 +118,88 @@ th.fc-col-header-cell.fc-day {
top: 9.5px;
right: -15px;
}
+.fc .fc-timegrid-col.fc-day-today,
+.fc .fc-daygrid-day.fc-day-today {
+ background-color: #fff;
+}
+.fc .fc-daygrid-body-unbalanced .fc-daygrid-day-events {
+ min-height: 36px;
+}
+.fc .fc-daygrid-body-natural .fc-daygrid-day-events {
+ margin-bottom: 0;
+}
+.fc .fc-timegrid-now-indicator-line {
+ z-index: 5;
+ border: 0 solid #f67e35;
+ border-top-width: 2px;
+ position: absolute;
+ left: 0;
+ right: 0;
+}
+.fc .fc-timegrid-now-indicator-line::after {
+ content: "";
+ z-index: 5;
+ background-color: #f67e35;
+ border-radius: 50%;
+ width: 10px;
+ height: 10px;
+ position: absolute;
+ top: -6px;
+ left: -5px;
+}
+.fc .fc-timegrid-now-indicator-arrow {
+ display: none;
+}
+.fc-day-today .fc-timegrid-now-indicator-container {
+ overflow: unset;
+}
+.fc .fc-timegrid-slot {
+ height: auto;
+ min-height: 40px !important;
+}
+.fc .fc-timegrid-slot-label {
+ display: flex;
+ align-items: flex-start;
+ padding-top: 2px;
+ height: 100%;
+}
+tr:has(> td.fc-timegrid-divider.fc-cell-shaded) {
+ display: none;
+}
+.fc .fc-scrollgrid-section td {
+ border-bottom: 0;
+}
+.fc .fc-scrollgrid-section td .fc-daygrid-day-frame {
+ border-bottom: 1px solid #b8c1cc;
+}
+th.fc-col-header-cell.fc-day {
+ cursor: pointer;
+}
+th.fc-col-header-cell.fc-day:hover
+ .fc-daygrid-day-number:not(.current-date)::after {
+ background-color: #b8c1cc56;
+}
+
+/* Hover effects for month view using CSS */
+.fc-daygrid-day:hover {
+ background-color: #b8c1cc1a !important;
+ transition: background-color 0.3s ease;
+}
+
+/* Hover effects for week and day views using JavaScript */
+.hoverable-day-cell {
+ transition: background-color 0.3s ease;
+}
+
+.hour-highlight {
+ transition: background-color 0.3s ease;
+ background-color: #b8c1cc1a !important;
+ position: absolute;
+ left: 0;
+ right: 0;
+ pointer-events: none;
+ z-index: 1;
+}
+.fc-timegrid-slot-label {
+ background-color: #fff !important;
+}