UNPKG

ar-design

Version:

AR Design is a (react | nextjs) ui library.

174 lines (173 loc) 8.59 kB
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;