Özel Arayüz Oluşturma

Bu kılavuz, headless hook’lar üzerine kendi tarih seçici arayüzünüzü oluşturma modelini adım adım açıklamaktadır.

Model

Her headless hook aynı modeli izler:

  1. Hook’u çağırın seçeneklerinizle (value, onChange, yapılandırma)
  2. Dönüş değerini parçalayın state, hesaplanmış değerler ve işleyicileri (handlers) almak için
  3. Kendi işaretlemenizi oluşturun dönen değerleri kullanarak
  4. Etkileşimleri bağlayın sağlanan işleyicileri çağırarak

Hook, tüm karmaşıklığı yönetir — takvim oluşturma, tarih matematiği, açma/kapama durumu, klavye ile gezinme, aralık mantığı — ve siz yalnızca sunuma odaklanırsınız.

Örnek: Özel Tarih Seçici

import { useState } from "react";
import { useDatePicker } from "react-date-range-picker-headless";
function CustomDatePicker() {
const [value, setValue] = useState<Date | null>(null);
const {
isOpen,
calendar,
getDayProps,
displayValue,
hasValue,
canConfirm,
locale,
handleToggle,
handleDateClick,
handleConfirm,
handleCancel,
handleClear,
handlePrevMonth,
handleNextMonth,
handleKeyDown,
containerRef,
popupRef,
} = useDatePicker({ value, onChange: setValue });
return (
<div ref={containerRef} style={{ position: "relative", display: "inline-block" }}>
{/* Trigger */}
<button
onClick={handleToggle}
style={{
padding: "8px 16px",
border: "1px solid #d1d5db",
borderRadius: 6,
background: "white",
cursor: "pointer",
minWidth: 180,
textAlign: "left",
}}
>
{displayValue || locale.placeholder}
</button>
{/* Popup */}
{isOpen && (
<div
ref={popupRef}
onKeyDown={handleKeyDown}
style={{
position: "absolute",
top: "100%",
left: 0,
marginTop: 4,
padding: 16,
background: "white",
border: "1px solid #e5e7eb",
borderRadius: 8,
boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
zIndex: 50,
}}
>
{/* Header */}
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 12,
}}
>
<button onClick={handlePrevMonth}>{locale.prevMonth}</button>
<span style={{ fontWeight: 600 }}>{locale.formatMonthYear(calendar.month)}</span>
<button onClick={handleNextMonth}>{locale.nextMonth}</button>
</div>
{/* Weekday headers */}
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(7, 36px)",
gap: 2,
textAlign: "center",
}}
>
{locale.weekdays.map((wd) => (
<span key={wd} style={{ fontSize: 12, color: "#9ca3af", padding: 4 }}>
{wd}
</span>
))}
</div>
{/* Calendar grid */}
<div style={{ display: "grid", gridTemplateColumns: "repeat(7, 36px)", gap: 2 }}>
{calendar.weeks.flat().map((day, i) => {
if (!day) return <span key={i} />;
const dp = getDayProps(day);
return (
<button
key={i}
onClick={() => handleDateClick(day)}
disabled={dp.isDisabled}
style={{
width: 36,
height: 36,
borderRadius: "50%",
border: dp.isFocused ? "2px solid #0ea5e9" : "none",
background: dp.isSelected ? "#0ea5e9" : dp.isToday ? "#f0f9ff" : "transparent",
color: dp.isSelected ? "white" : dp.isDisabled ? "#d1d5db" : "inherit",
cursor: dp.isDisabled ? "not-allowed" : "pointer",
fontWeight: dp.isToday ? 600 : 400,
}}
>
{dp.day}
</button>
);
})}
</div>
{/* Footer */}
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 12 }}>
{hasValue && (
<button onClick={handleClear} style={{ color: "#ef4444" }}>
{locale.clear}
</button>
)}
<button onClick={handleCancel}>{locale.cancel}</button>
<button
onClick={handleConfirm}
disabled={!canConfirm}
style={{
background: canConfirm ? "#0ea5e9" : "#e5e7eb",
color: canConfirm ? "white" : "#9ca3af",
padding: "4px 16px",
borderRadius: 4,
border: "none",
}}
>
{locale.confirm}
</button>
</div>
</div>
)}
</div>
);
}

Temel Kavramlar

getDayProps

getDayProps fonksiyonu, takvim oluşturmanın merkezindedir. Her tarih hücresi için, hücrenin durumunu tanımlayan 18 boolean/sayı bayrağı içeren bir DayProps nesnesi döndürür:

const dp = getDayProps(day);
// Seçim durumu
dp.isSelected; // Mevcut seçili tarih
dp.isToday; // Bugünün tarihi
// Aralık durumu (aralık seçiciler için)
dp.isInRange; // Başlangıç ve bitiş tarihleri arasında
dp.isRangeStart; // Aralığın ilk tarihi
dp.isRangeEnd; // Aralığın son tarihi
dp.isInHoverRange; // Fare ile üzerine gelinen önizleme aralığında
dp.isHoverTarget; // Üzerine gelinen tarihin kendisi
dp.isRangeSingle; // Hem aralık başlangıcı hem de bitişi (tek günlük aralık)
// Görsel bağlantı yardımcıları
dp.hasLeftConnection; // Önceki güne bağlı (aralık arka planları için)
dp.hasRightConnection; // Sonraki güne bağlı
dp.isConsecutiveRange; // Çok günlük bir aralığın parçası
// Durum
dp.isDisabled; // minDate/maxDate/isDateUnavailable tarafından devre dışı bırakıldı
dp.isFocused; // Klavye odağına sahip
dp.isOutsideDay; // Bitişik aya ait (showOutsideDays etkinken)
dp.isHighlighted; // highlightDates dizisinde
// Veri
dp.date; // Date nesnesi
dp.day; // Ayın gün numarası
dp.dayOfWeek; // 0-6 (Pazar-Cumartesi)

Dışarı Tıklama için Ref’ler

Hook’lar containerRef ve popupRef döndürür. Bunları sarmalayıcı ve açılır pencere elemanlarınıza ekleyin — hook, bunları dışarıdaki tıklamaları algılamak ve açılır pencereyi otomatik olarak kapatmak için kullanır.

Klavye ile Gezinme

handleKeyDown işleyicisi, tam klavye ile gezinme için ok tuşlarını, Enter, Escape ve Tab tuşlarını destekler. Bunu açılır pencere konteynerine ekleyin.

Yerelleştirme (Locale)

Kullanıcıya dönük tüm metinler locale nesnesinden gelir. Kısmi bir locale seçeneği geçirerek herhangi bir metni özelleştirebilirsiniz:

const picker = useDatePicker({
value,
onChange: setValue,
locale: {
confirm: "Tamam",
cancel: "Geri",
placeholder: "Bir tarih seçin...",
},
});

Sonraki Adımlar