UNPKG

ar-design

Version:

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

366 lines (365 loc) 19.6 kB
"use client"; import React, { useEffect, useRef, useState } from "react"; import "../../../assets/css/components/form/date-picker/date-picker.css"; import Input from "../input"; import Button from "../button"; import Alert from "../../feedback/alert"; import ReactDOM from "react-dom"; import DATE from "./DATE"; const weekdays = ["Pzt", "Sal", "Çar", "Per", "Cum", "Cmt", "Paz"]; const months = [ { value: 0, text: "Ocak" }, { value: 1, text: "Şubat" }, { value: 2, text: "Mart" }, { value: 3, text: "Nisan" }, { value: 4, text: "Mayıs" }, { value: 5, text: "Haziran" }, { value: 6, text: "Temmuz" }, { value: 7, text: "Ağustos" }, { value: 8, text: "Eylül" }, { value: 9, text: "Ekim" }, { value: 10, text: "Kasım" }, { value: 11, text: "Aralık" }, ]; const DatePicker = ({ variant, color, onChange, config, validation, ...attributes }) => { // refs const _arCalendar = useRef(null); const _arClock = useRef(null); const _placeholder = useRef(null); const _calendarHeader = useRef(null); const _calendarFooter = useRef(null); const _clockHeader = useRef(null); const _clockFooter = useRef(null); const _currentDate = useRef(new Date()).current; const _beginDate = useRef(null); // refs -> Geçerli Tarih ve Saat Bilgileri. const _year = useRef(_currentDate.getFullYear()); const _month = useRef(_currentDate.getMonth()); const _day = useRef(_currentDate.getDate()); const _hours = useRef(_currentDate.getHours()); const _minutes = useRef(_currentDate.getMinutes()); // refs -> List Elements const _hoursListElement = useRef(null); const _hoursLiElements = useRef([]); const _minutesListElement = useRef(null); const _minutesLiElements = useRef([]); // states const [calendarIsOpen, setCalendarIsOpen] = useState(false); const [calendarDays, setCalendarDays] = useState([]); const [years, setYears] = useState([]); const [hours, setHours] = useState(); const [minutes, setMinutes] = useState(); const [dateChanged, setDateChanged] = useState(false); const [timeChanged, setTimeChanged] = useState(false); // states => Seçilmiş Tarihler const [selectedYear, setSelectedYear] = useState(_currentDate.getFullYear()); const [selectedMonth, setSelectedMonth] = useState(_currentDate.getMonth()); const [selectedDay, setSelectedDay] = useState(_currentDate.getDate()); const [selectedHours, setSelectedHours] = useState(_currentDate.getHours()); const [selectedMinutes, setSelectedMinutes] = useState(_currentDate.getMinutes()); // methods const handleClickOutSide = (event) => { const target = event.target; if (target === _beginDate.current) return; if (_arCalendar.current && !_arCalendar.current.contains(target)) closeCalendar(); }; const handleKeys = (event) => { const key = event.key; if (key === "Escape") { event.stopPropagation(); closeCalendar(); } }; const handlePosition = () => { if (_arCalendar.current && _beginDate.current) { const arCalendarRect = _arCalendar.current.getBoundingClientRect(); const InpuRect = _beginDate.current?.getBoundingClientRect(); if (InpuRect) { const screenCenterX = window.innerWidth / 2; const screenCenterY = window.innerHeight / 2; const sx = window.scrollX || document.documentElement.scrollLeft || document.body.scrollLeft; const sy = window.scrollY || document.documentElement.scrollTop || document.body.scrollTop; const _isClock = InpuRect.left > screenCenterX ? -Math.abs(config?.isClock ? 7.5 * 16.75 : 0) : 0; _arCalendar.current.style.visibility = "visible"; _arCalendar.current.style.opacity = "1"; _arCalendar.current.style.top = `${(InpuRect.top > screenCenterY ? InpuRect.top - arCalendarRect.height : InpuRect.top + InpuRect.height) + sy}px`; _arCalendar.current.style.left = `${(InpuRect.left > screenCenterX ? InpuRect.right - arCalendarRect.width : InpuRect.left) + sx + _isClock}px`; } } }; const handleHeight = () => { if (_arCalendar.current && _arClock.current) { const calendar = _arCalendar.current?.getBoundingClientRect()?.height; _arClock.current.style.maxHeight = `${calendar}px`; } if (_calendarHeader.current && _clockHeader.current) { const calendarHeaderH = _calendarHeader.current?.getBoundingClientRect()?.height; _clockHeader.current.style.minHeight = `${calendarHeaderH}px`; } if (_calendarFooter && _clockFooter.current) { const calendarFooterH = _calendarFooter.current?.getBoundingClientRect()?.height; _clockFooter.current.style.minHeight = `${calendarFooterH}px`; } }; const handleOk = (isShutdownOn = true) => { // Stateler güncelleniyor... setSelectedYear(_year.current); setSelectedMonth(_month.current); setSelectedDay(_day.current); setSelectedHours(_hours.current); setSelectedMinutes(_minutes.current); const inputDate = new Date(Date.UTC(_year.current, _month.current, _day.current, !config?.isClock ? 0 : _hours.current, !config?.isClock ? 0 : _minutes.current, 0)); onChange(inputDate.toISOString()); isShutdownOn && setCalendarIsOpen(false); }; const setNowButton = () => { const now = new Date(); // Stateler güncelleniyor... setSelectedYear(now.getFullYear()); setSelectedMonth(now.getMonth()); setSelectedDay(now.getDate()); setSelectedHours(now.getHours()); setSelectedMinutes(now.getMinutes()); _year.current = now.getFullYear(); _month.current = now.getMonth(); _day.current = now.getDate(); _hours.current = now.getHours(); _minutes.current = now.getMinutes(); // Takvim kapatılıyor... setCalendarIsOpen(false); // Değer gönderiliyor... const inputDate = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), !config?.isClock ? 0 : now.getHours(), !config?.isClock ? 0 : now.getMinutes(), 0)); onChange(inputDate.toISOString()); }; const okayButton = () => { return (React.createElement(Button, { variant: "borderless", color: "green", onClick: () => handleOk() }, "Tamam")); }; const closeCalendar = () => { const { year, month, day, hours, minutes } = DATE.Parse(String(attributes.value), config?.isClock); _year.current = attributes.value ? year : selectedYear; _month.current = attributes.value ? month - 1 : selectedMonth; _day.current = attributes.value ? day : selectedDay; _hours.current = attributes.value ? hours : selectedHours; _minutes.current = attributes.value ? minutes : selectedMinutes; setCalendarIsOpen(false); }; // useEffects useEffect(() => { if (calendarIsOpen) { setTimeout(() => { handleHeight(); handlePosition(); }, 0); const days = []; const firstDayOfMonth = new Date(_year.current, _month.current, 1); const lastDayOfMonth = new Date(_year.current, _month.current + 1, 0); const startingDay = firstDayOfMonth.getDay() === 0 ? 7 : firstDayOfMonth.getDay(); const endingDay = lastDayOfMonth.getDay() === 0 ? 7 : lastDayOfMonth.getDay(); for (let i = 1; i < startingDay; i++) { days.push(React.createElement("span", { key: `prev-${i}`, className: "empty-day" })); } for (let i = firstDayOfMonth.getDate(); i <= lastDayOfMonth.getDate(); i++) { const isSelected = !isNaN(firstDayOfMonth.getTime()) ? firstDayOfMonth.getFullYear() === _year.current && firstDayOfMonth.getMonth() === _month.current && i === _day.current : _currentDate.getFullYear() === _year.current && _currentDate.getMonth() === _month.current && i === _day.current; days.push(React.createElement("span", { key: `current-${i}`, className: isSelected ? "selection-day" : "", onClick: (event) => { event.preventDefault(); _day.current = i; setSelectedDay(i); setDateChanged(!dateChanged); handleOk(false); } }, React.createElement("span", null, i))); } for (let i = endingDay; i < 7; i++) { days.push(React.createElement("span", { key: `next-${i}`, className: "empty-day" })); } setCalendarDays(days); // window.addEventListener("blur", () => closeCalendar()); document.addEventListener("click", handleClickOutSide); document.addEventListener("keydown", handleKeys); } return () => { // window.removeEventListener("blur", () => closeCalendar()); document.removeEventListener("click", handleClickOutSide); document.removeEventListener("keydown", handleKeys); }; }, [dateChanged, calendarIsOpen]); useEffect(() => { const generateList = (count, current, setFunc) => { const items = Array.from({ length: count }, (_, i) => (React.createElement("li", { ref: (element) => { if (!element) return; count === 24 ? (_hoursLiElements.current[i] = element) : (_minutesLiElements.current[i] = element); }, key: i, ...(current === i ? { className: "selection-time" } : {}), onClick: () => { if (count === 24) { setTimeChanged((prev) => !prev); _hours.current = i; setSelectedHours(i); } else { setTimeChanged((prev) => !prev); _minutes.current = i; setSelectedMinutes(i); } handleOk(false); } }, React.createElement("span", null, React.createElement("span", null, i.toString().padStart(2, "0")))))); setFunc(items); }; // Listeler oluşturuyor... generateList(24, _hours.current, setHours); generateList(60, _minutes.current, setMinutes); if (!config?.isClock) return; if (calendarIsOpen) handleHeight(); // Seçim sonrasında en yukarı getirme işlemi için aşağıda yer alan kodlar yazılmıştır const hourLiElement = _hoursLiElements.current[_hours.current]; const minuteLiElement = _minutesLiElements.current[_minutes.current]; if (hourLiElement) { _hoursListElement.current?.scrollTo({ top: hourLiElement.offsetTop - _hoursListElement.current.offsetTop - 8, behavior: "smooth", }); } if (minuteLiElement) { _minutesListElement.current?.scrollTo({ top: minuteLiElement.offsetTop - _minutesListElement.current.offsetTop - 8, behavior: "smooth", }); } }, [timeChanged, calendarIsOpen, config?.isClock]); useEffect(() => { if (isNaN(_year.current)) return; const years = []; // Son 20 yıl for (let i = _year.current - 20; i <= _year.current; i++) { years.push({ value: i, text: `${i}` }); } // Önümüzdeki 20 yıl for (let i = _year.current + 1; i <= _year.current + 20; i++) { years.push({ value: i, text: `${i}` }); } setYears(years); }, [selectedYear]); return (React.createElement("div", { className: "ar-date-picker" }, attributes.placeholder && attributes.placeholder.length > 0 && (React.createElement("label", { ref: _placeholder }, validation ? "* " : "", attributes.placeholder)), React.createElement("div", { className: "wrapper", style: { clipPath: `polygon( -15px 0, 10px -5px, 10px 5px, calc(${_placeholder.current?.getBoundingClientRect().width}px + 7px) 5px, calc(${_placeholder.current?.getBoundingClientRect().width}px + 7px) -5px, 100% -70px, calc(100% + 5px) calc(100% + 5px), -5px calc(100% + 5px) )`, } }, React.createElement(Input, { ref: _beginDate, variant: variant, color: color, ...attributes, value: DATE.ParseValue(String(attributes.value), config?.isClock), type: config?.isClock ? "datetime-local" : "date", onKeyDown: (event) => { if (event.code === "Space") event.preventDefault(); else if (event.code === "Enter") handleOk(); }, onChange: (event) => { // Disabled gelmesi durumunda işlem yapmasına izin verme... if (attributes.disabled) return; (() => { if (!calendarIsOpen) setCalendarIsOpen(true); const value = event.target.value; const [date, time] = value.split("T"); const [year, month, day] = date.split("-").map(Number); const hours = time ? time.split(".")[0].split(":").map(Number)[0] : 0; const minutes = time ? time.split(".")[0].split(":").map(Number)[1] : 0; _year.current = year; _month.current = month - 1; _day.current = day; if (hours || minutes) { _hours.current = hours; _minutes.current = minutes; } // Yıl ve Ay'ı anlık yeniler. setSelectedYear(_year.current); setSelectedMonth(_month.current); // Takvimi anlık yeniler. setDateChanged((prev) => !prev); setTimeChanged((prev) => !prev); onChange(value); })(); }, onClick: (event) => { event.preventDefault(); setCalendarIsOpen(true); }, autoComplete: "off", validation: validation })), calendarIsOpen && ReactDOM.createPortal(React.createElement("div", { ref: _arCalendar, className: "ar-date-calendar" }, React.createElement("div", { ref: _calendarHeader, className: "header" }, React.createElement("div", { className: "select-field" }, React.createElement("div", { className: "prev" }, React.createElement("span", { onClick: () => { _year.current -= 1; setSelectedYear(_year.current); setDateChanged((prev) => !prev); } }), React.createElement("span", { onClick: () => { if (_month.current <= 0) { _year.current -= 1; _month.current = 12; } _month.current -= 1; setSelectedYear(_year.current); setSelectedMonth(_month.current); setDateChanged((prev) => !prev); } })), React.createElement("div", { className: "selects" }, React.createElement("div", null, React.createElement("span", null, months.find((month) => month.value === selectedMonth)?.text)), React.createElement("div", null, React.createElement("span", null, years.find((year) => year.value === selectedYear)?.text))), React.createElement("div", { className: "next" }, React.createElement("span", { onClick: () => { if (_month.current >= 11) { _year.current += 1; _month.current = -1; } _month.current += 1; setSelectedYear(_year.current); setSelectedMonth(_month.current); setDateChanged((prev) => !prev); } }), React.createElement("span", { onClick: () => { _year.current += 1; setSelectedYear(_year.current); setDateChanged((prev) => !prev); } })))), React.createElement("div", { className: "content" }, !isNaN(_month.current) && !isNaN(_year.current) ? (React.createElement("div", { className: "calendar" }, React.createElement("div", { className: "weekdays" }, weekdays.map((weekday, index) => (React.createElement("span", { key: index }, weekday)))), React.createElement("div", { className: "days" }, calendarDays))) : (React.createElement(Alert, { status: "warning" }, "Ay veya y\u0131l se\u00E7imi yapman\u0131z gerekmektedir."))), config?.isFooterButton && (React.createElement("div", { ref: _calendarFooter, className: "footer" }, React.createElement("div", null, React.createElement(Button, { variant: "borderless", onClick: () => setNowButton() }, "\u015Eimdi")), React.createElement("div", null, !config?.isClock && okayButton()))), config?.isClock && (React.createElement("div", { ref: _arClock, className: "clock" }, React.createElement("div", { ref: _clockHeader, className: "header" }, _hours.current.toString().padStart(2, "0"), " : ", _minutes.current.toString().padStart(2, "0")), React.createElement("div", { className: "content" }, React.createElement("ul", { ref: _hoursListElement }, hours), React.createElement("ul", { ref: _minutesListElement }, minutes)), React.createElement("div", { ref: _clockFooter, className: "footer" }, okayButton())))), document.body))); }; DatePicker.displayName = "DatePicker"; export default DatePicker;