カスマイズ可能なセレクトコンポーネント

## 内容

日時はカレンダーで時間・分はセレクトタグで選択できるようにしたいが選択量が多くなるとドロップダウンメニューが長くなりすぎるのでカスマイズしたい。しかしドロップダウンメニューはブラウザ依存で高さ調整ができないため、自作した時のメモ記事です。

```react:Select.jsx

import React, { useState, useEffect, useRef } from 'react';
 
const Select = ({ selectedTime, setSelectedTime }) => {
    const [isOpen, setIsOpen] = useState(false); // ドロップダウンの開閉状態
    const dropdownRef = useRef(null);
    const generateTimeOptions = () => {
        const times = [];
        for (let hour = 0; hour < 24; hour++) {
            for (let minute = 0; minute < 60; minute += 30) {
                const formattedTime = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;
                times.push(formattedTime);
            }// 30分刻みの時間を生成する
        }
        //         // const times = [];
        //         // for (let h = 0; h < 24; h++) {
        //         //     const hour = h.toString().padStart(2, "0");
        //         //     for (let m = 0; m < 60; m += 5) {  // 5分刻み
        //         //         const minute = m.toString().padStart(2, "0");
        //         //         times.push(`${hour}:${minute}`);
        //         //     }
        //         // }
        //         // const times = [];
        //         // for (let h = 0; h < 24; h++) {
        //         //     const hour = h.toString().padStart(2, "0");
        //         //     for (let m = 0; m < 60; m += 15) {  // 15分刻み
        //         //         const minute = m.toString().padStart(2, "0");
        //         //         times.push(`${hour}:${minute}`);
        //         //     }
        //         // }
        return times;
    };
    const timeOptions = generateTimeOptions();
    const handleSelect = (time) => {
        setSelectedTime(time);
        setIsOpen(false);
    };
    const toggleDropdown = () => {
        setIsOpen(!isOpen);
    };
    // ドロップダウン外をクリックしたときに閉じる処理
    useEffect(() => {
        const handleClickOutside = (event) => {
            if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
                setIsOpen(false);
            }
        };
        document.addEventListener('mousedown', handleClickOutside);
        return () => {
            document.removeEventListener('mousedown', handleClickOutside);
        };
    }, []);
    return (
        <div className="custom-select" ref={dropdownRef}>
            <div className="select-selected" onClick={toggleDropdown}>
                {selectedTime}
            </div>
            {isOpen && (
                <div className="select-items">
                    {timeOptions.map((time, index) => (
                        <div key={index} onClick={() => handleSelect(time)}>
                            {time}
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
};
 
export default Select;
```


親コンポーネントでの使い方

```Parent.jsx
import React, { useState } from 'react';
import Datetime from 'react-datetime';
import "react-datetime/css/react-datetime.css";
import Select from "./Select"
 
const Parent = () => {
    const [date, setDate] = useState(null);
    // 時間と分を一つにまとめる
    const [time, setTime] = useState("00:00"); 
    const handleDateChange = (selectedDate) => {
        setDate(selectedDate);
    };
    const handleSubmit = () => {
        if (date) {
            const isoDateTime = `${date.format('YYYY-MM-DD')}T${time}:00`;
            console.log('日時:', isoDateTime);
        } else {
            console.log('日付と時間を選択してください');
        }
    };
    return (
        <div className='calendar-container'>
            <label>開催日時</label>
            <Datetime
                onChange={handleDateChange}
                value={date}
                dateFormat="YYYY-MM-DD"
                timeFormat={false}  // 時間選択を非表示に設定
                inputProps={{ className: 'input-style' }}
            />
            <Select selectedTime={time} setSelectedTime={setTime} />
            <button onClick={handleSubmit}>日時を送信</button>
        </div>
 
    );
 
};
 
export default Parent;
```