ar-design
Version:
AR Design is a (react | nextjs) ui library.
174 lines (173 loc) • 8.59 kB
JavaScript
import React, { useEffect, useMemo, useState } from "react";
import ReactDOM from "react-dom";
const Week = function ({ trackedBy, data, renderItem, states, config }) {
// states
const [mouseCoordinate, setMouseCoordinate] = useState({
x: 0,
y: 0,
isRightHalf: false,
isBottomHalf: false,
});
const [activeTooltip, setActiveTooltip] = useState(null);
// variables
const startHour = 0;
const endHour = 24;
const hours = endHour - startHour;
// const cellHeight = 60;
// methods
const weekDays = useMemo(() => getWeekDays(states.currentDate.get, config?.weekStartsOn ?? 1), [states.currentDate.get, config?.weekStartsOn]);
// useEffects
useEffect(() => {
const handleMouseMove = (event) => {
setMouseCoordinate({
x: event.clientX,
y: event.clientY,
isRightHalf: event.clientX > window.innerWidth / 2,
isBottomHalf: event.clientY > window.innerHeight / 2,
});
};
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}, []);
return (React.createElement(React.Fragment, null,
React.createElement("div", { className: "ar-calendar-week-view" },
React.createElement("div", { className: "head" }, weekDays.map((day) => (React.createElement("div", { key: day.toISOString(), className: "item" },
React.createElement("span", { className: "day-name" }, day.toLocaleString(config?.locale ?? "tr", { weekday: "short" }).toUpperCase()),
React.createElement("span", { className: "date" }, day.getDate()))))),
React.createElement("div", { className: "body" },
React.createElement("div", { className: "clocks" }, Array.from({ length: hours }, (_, index) => (React.createElement("div", { key: index },
React.createElement("span", null,
String(startHour + index).padStart(2, "0"),
":00"))))),
React.createElement("div", { role: "grid", className: "grid" }, Array.from({ length: hours }).map((_, rowIndex) => (React.createElement("div", { key: rowIndex, className: "row" }, weekDays.map((_, colIndex) => (React.createElement("div", { key: colIndex, className: "cell" }))))))),
React.createElement("div", { className: "events-layer" }, weekDays.map((day, dayIndex) => {
const dayStart = new Date(day).setHours(0, 0, 0, 0);
const dayEnd = new Date(day).setHours(23, 59, 59, 999);
// 1. Bu güne ait etkinlikleri filtrele ve sırala.
const dayEvents = data
.filter((event) => {
return event.start.getTime() <= dayEnd && event.end.getTime() >= dayStart;
})
.sort((a, b) => a.start.getTime() - b.start.getTime());
// 2. Çakışmaları hesapla (Görsel yerleşim için kritik adım).
const positionedEvents = computeEventLayout(dayEvents, dayStart, dayEnd);
return positionedEvents.map(({ event, layout, originalIndex }) => {
const uniqueValue = event[trackedBy];
const eventColor = getColor(uniqueValue);
return (React.createElement("div", { key: `${originalIndex}-${dayIndex}`, onMouseEnter: () => setActiveTooltip({ content: renderItem(event, originalIndex), id: originalIndex }), onMouseLeave: () => setActiveTooltip(null), className: "event-box", style: {
backgroundColor: eventColor.bg,
position: "absolute",
top: `${layout.top}px`,
height: `${layout.height}px`,
// Dinamik genişlik ve sol mesafe hesaplama.
left: `calc(${(100 / 7) * dayIndex}% + ${(layout.column * (100 / 7)) / layout.totalColumns}%)`,
width: `${100 / 7 / layout.totalColumns}%`,
border: `1px solid ${eventColor.border}`,
borderRadius: "var(--border-radius-sm)",
zIndex: 10,
} }, layout.height > 20 && renderItem(event, originalIndex)));
});
})))),
activeTooltip &&
ReactDOM.createPortal(React.createElement("div", { className: "ar-calendar-tooltip", style: {
top: mouseCoordinate.y,
left: mouseCoordinate.x,
transform: `translate(${mouseCoordinate.isRightHalf ? "-110%" : "10%"}, ${mouseCoordinate.isBottomHalf ? "-110%" : "10%"})`,
} }, activeTooltip.content), document.body)));
};
/**
* Etkinliklerin çakışma durumuna göre konumlarını hesaplayan yardımcı fonksiyon
*/
function computeEventLayout(events, dayStart, dayEnd) {
const cellHeight = 60;
const results = [];
// Gruplandırma (Aynı anda çakışan etkinlik kümeleri)
let clusters = [];
let lastEventEnd = 0;
events.forEach((event, idx) => {
const start = Math.max(event.start.getTime(), dayStart);
const end = Math.min(event.end.getTime(), dayEnd);
if (start >= lastEventEnd) {
clusters.push([]); // Yeni bir küme başlat
}
const lastCluster = clusters[clusters.length - 1];
lastCluster.push({ event, idx, start, end });
lastEventEnd = Math.max(lastEventEnd, end);
});
// Her küme içindeki kolonları hesapla
clusters.forEach((cluster) => {
const columns = [];
cluster.forEach((item) => {
let placed = false;
for (let i = 0; i < columns.length; i++) {
// Eğer bu kolondaki son etkinlikle çakışmıyorsa buraya koy
const lastInColumn = columns[i][columns[i].length - 1];
if (item.start >= lastInColumn.end) {
columns[i].push(item);
placed = true;
break;
}
}
if (!placed) {
columns.push([item]); // Yeni kolon aç
}
});
// Sonuçları formatla
cluster.forEach((item) => {
const colIndex = columns.findIndex((col) => col.includes(item));
const startMinutes = new Date(item.start).getHours() * 60 + new Date(item.start).getMinutes();
const duration = (item.end - item.start) / 60000;
results.push({
event: item.event,
originalIndex: item.idx,
layout: {
top: (startMinutes / 60) * cellHeight,
height: (duration / 60) * cellHeight,
column: colIndex,
totalColumns: columns.length,
},
});
});
});
return results;
}
const getWeekRange = (date, weekStartsOn = 1) => {
const current = new Date(date);
const currentDay = current.getDay();
const diff = (currentDay - weekStartsOn + 7) % 7;
const start = new Date(current);
start.setDate(current.getDate() - diff);
start.setHours(0, 0, 0, 0);
const end = new Date(start);
end.setDate(start.getDate() + 6);
return { start, end };
};
const getWeekDays = (date, weekStartsOn = 1) => {
const { start } = getWeekRange(date, weekStartsOn);
return Array.from({ length: 7 }, (_, i) => {
const d = new Date(start);
d.setDate(start.getDate() + i);
return d;
});
};
const getColor = (id) => {
const colors = [
{ bg: "#3174ad", border: "#2a6293" }, // Mavi
{ bg: "#4caf50", border: "#388e3c" }, // Yeşil
{ bg: "#ff9800", border: "#f57c00" }, // Turuncu
{ bg: "#9c27b0", border: "#7b1fa2" }, // Mor
{ bg: "#e91e63", border: "#c2185b" }, // Pembe
{ bg: "#00bcd4", border: "#0097a7" }, // Turkuaz
];
// Eğer id string ise karakter kodlarının toplamını alarak tutarlı bir index üretiriz
let hash = 0;
const identifier = String(id);
for (let i = 0; i < identifier.length; i++) {
hash = identifier.charCodeAt(i) + ((hash << 5) - hash);
}
const index = Math.abs(hash);
return colors[index % colors.length];
};
export default Week;