初めに
reactで綺麗でカスタマイズできるカレンダーを作ろうと挑戦して出来上がったもの残します。
なにせ初心者の66歳のおじいちゃん作ですので、効率性が悪かったり、不要なコードがあると思いますので、お許しください。
構成はreactのCalendar.jsとcalendar.scssです。
日付を押すと、その日のデータが取得できます。
左上の「2025/1」のところをクリックすると、年の選択ができます。
項番 | ページ内リンク |
---|---|
1 | 完成品 |
2 | 基本的な構成? |
3 | 書き込み等ができるコード |
4 | カレンダースタイル |
完成品
基本的な構成
calendar.js
import React, { useEffect, useState } from "react";
import "./Calendar.scss";
const Calendar = () => {
const [year, setYear] = useState(new Date().getFullYear());
const [selectedYear, setSelectedYear] = useState(new Date().getFullYear());
const [month, setMonth] = useState(new Date().getMonth());
const [holidays, setHolidays] = useState({});
const [showModal, setShowModal] = useState(false);
const [selectDay, setSelectDay] = useState("");
const getDaysInMonth = (year, month) => {
return new Date(year, month + 1, 0).getDate();
};
const getFirstDayOfMonth = (year, month) => {
return new Date(year, month, 1).getDay();
};
const getPrevMonthDays = (year, month) => {
const prevMonth = month === 0 ? 11 : month - 1;
const prevMonthYear = month === 0 ? year - 1 : year;
const prevMonthDays = getDaysInMonth(prevMonthYear, prevMonth);
const prevMonthFirstDay = getFirstDayOfMonth(prevMonthYear, prevMonth);
return { prevMonthDays, prevMonthFirstDay };
};
const getNextMonthDays = (year, month) => {
const nextMonth = month === 11 ? 0 : month + 1;
const nextMonthYear = month === 11 ? year + 1 : year;
const nextMonthDays = getDaysInMonth(nextMonthYear, nextMonth);
const nextMonthFirstDay = getFirstDayOfMonth(nextMonthYear, nextMonth);
return { nextMonthDays, nextMonthFirstDay };
};
const daysInMonth = getDaysInMonth(year, month);
const firstDayOfMonth = getFirstDayOfMonth(year, month);
const { prevMonthDays, prevMonthFirstDay } = getPrevMonthDays(year, month);
const { nextMonthDays, nextMonthFirstDay } = getNextMonthDays(year, month);
const prevRemainDays = firstDayOfMonth;
const days = [];
let l = 0;
if (firstDayOfMonth !== 6) {
for (let i = prevMonthDays - firstDayOfMonth; i < prevMonthDays + 1; i++) {
l += 1;
// 日付の形式を整える
const date = i;
const targetmonth = "prevMonth";
const formattedDate = `${year}-${String(month).padStart(2, "0")}-${String(
date
).padStart(2, "0")}`;
// その日が祝日かどうかを確認
const isHoliday = holidays[formattedDate] || null;
// 配列にオブジェクトを追加
days.push({
day: date, // 日付
isHoliday: !!isHoliday, // 祝日かどうか (真偽値)
holidayName: isHoliday || null, // 祝日名または種別
targetmonth: targetmonth, // 月の分類 (prevMonth, nowMonth, nextMonth)
formattedDate: formattedDate, // 日付の文字列形式
});
}
}
for (let i = 1; i <= daysInMonth; i++) {
const date = i;
const targetmonth = "nowMonth";
const formattedDate = `${year}-${String(month).padStart(2, "0")}-${String(
date
).padStart(2, "0")}`;
const formattedDateForHoliday = `${year + Math.floor(month / 12)}-${String(
(month % 12) + 1
).padStart(2, "0")}-${String(date).padStart(2, "0")}`;
// その日が祝日かどうかを確認
const isHoliday = holidays[formattedDateForHoliday] || null;
// 配列にオブジェクトを追加
days.push({
day: date, // 日付
isHoliday: !!isHoliday, // 祝日かどうか (真偽値)
holidayName: isHoliday || null, // 祝日名または種別
targetmonth: targetmonth, // 月の分類 (prevMonth, nowMonth, nextMonth)
formattedDate: formattedDate, // 日付の文字列形式
});
}
for (let i = 1; i <= 6 - nextMonthFirstDay; i++) {
let nextmonth = 0;
let nextmonthyear = 0;
if (daysInMonth + l === 35 || daysInMonth + l === 28) {
let l = null;
} else {
const date = i;
const targetmonth = "nextMonth";
if (month === 11) {
nextmonth = 0;
nextmonthyear = year + 1;
} else {
nextmonth = month + 2;
nextmonthyear = year;
}
const formattedDate = `${nextmonthyear}-${String(nextmonth).padStart(
2,
"0"
)}-${String(date).padStart(2, "0")}`;
console.log("月だよ" + nextmonth);
// その日が祝日かどうかを確認
const isHoliday = holidays[formattedDate] || null;
// 配列にオブジェクトを追加
days.push({
day: date, // 日付
isHoliday: !!isHoliday, // 祝日かどうか (真偽値)
holidayName: isHoliday || null, // 祝日名または種別
targetmonth: targetmonth, // 月の分類 (prevMonth, nowMonth, nextMonth)
formattedDate: formattedDate, // 日付の文字列形式
});
}
}
const handleYearChange = (direction) => {
setYear((prev) => prev + direction);
};
const handleMonthChange = (direction) => {
setMonth((prev) => {
const newMonth = prev + direction;
if (newMonth < 0) {
setYear((y) => y - 1);
return 11;
} else if (newMonth > 11) {
setYear((y) => y + 1);
return 0;
}
return newMonth;
});
};
useEffect(() => {
const fetchHolidays = async (year) => {
const response = await fetch(
`https://holidays-jp.github.io/api/v1/date.json`
);
if (response.ok) {
const data = await response.json();
const filteredHolidays = {};
for (const [date, name] of Object.entries(data)) {
if (date.startsWith(year) || date.startsWith(year + 1)) {
filteredHolidays[date] = name;
}
}
setHolidays(filteredHolidays);
} else {
console.error("祝日データの取得に失敗しました。");
}
};
fetchHolidays(year);
}, [year]);
const openModal = () => {
setShowModal(true);
};
const closeModal = () => {
setShowModal(false);
};
const years = [];
const yearSelect = () => {
for (let year = 2100; year >= 1924; year--) {
years.push(year);
}
};
yearSelect();
const handleChange = (event) => {
setSelectedYear(event.target.value);
};
const changeSelectYear = () => {
setYear(selectedYear);
setShowModal(false);
};
const changeSelectDay = (day) => {
if (day.targetmonth === "prevMonth") {
let premonth = "";
let preyear = "";
if (month === 0) {
premonth = 12;
preyear = year - 1;
} else {
premonth = month;
preyear = year;
}
const newSelectDay = `${preyear}-${String(premonth).padStart(
2,
"0"
)}-${String(day.day).padStart(2, "0")}`;
setSelectDay(newSelectDay);
} else if (day.targetmonth === "nextMonth") {
let nextmonth = "";
let nextyear = "";
if (month === 11) {
nextmonth = 1;
nextyear = year + 1;
} else {
nextmonth = month + 2;
nextyear = year;
}
const newSelectDay = `${nextyear}-${String(nextmonth).padStart(
2,
"0"
)}-${String(day.day).padStart(2, "0")}`;
setSelectDay(newSelectDay);
} else {
const newSelectDay = `${year}-${String(month + 1).padStart(
2,
"0"
)}-${String(day.day).padStart(2, "0")}`;
setSelectDay(newSelectDay);
}
};
// selectDayが変更された時にアラートを表示
useEffect(() => {
if (selectDay) {
alert(selectDay);
}
}, [selectDay]);
return (
<>
<div className="calendar-container">
<div className="calendar-content">
<button id="year_button" onClick={openModal}>
{year} / {month + 1 > 12 ? 1 : month + 1}
</button>
<button
className="calendar-year-prev"
onClick={() => handleYearChange(-1)}
>
▲
</button>
<button
className="calendar-year-next"
onClick={() => handleYearChange(1)}
>
▼
</button>
<button
className="calendar-prev"
onClick={() => handleMonthChange(-1)}
>
◀◀
</button>
<button
className="calendar-next"
onClick={() => handleMonthChange(1)}
>
▶▶
</button>
<div className="calendar-field">
{["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map(
(day, index) => (
<div
className={`calendar-label ${
index === 0 ? "sunday" : index === 6 ? "saturday" : ""
}`}
key={day}
>
{day}
</div>
)
)}
{days.map((day, index) => {
const dayOfWeek = index % 7;
const dateKey = `${year}-${String(month + 1).padStart(
2,
"0"
)}-${String(day.date).padStart(2, "0")}`;
return (
<div
className={`calendar-cell ${
day.day && dayOfWeek === 0
? "sunday"
: day.day && dayOfWeek === 6
? "saturday"
: "weekday"
} ${day.isHoliday ? "holiday" : ""} ${
day.targetmonth === "prevMonth" ? "prevmonth" : ""
} ${day.targetmonth === "nextMonth" ? "nextmonth" : ""}`}
key={index}
onClick={() => day.day && changeSelectDay(day)}
>
{day.day || ""}
<div className="holiday-name">{day.holidayName || ""}</div>
</div>
);
})}
</div>
<div id="modal" style={{ display: showModal ? "" : "none" }}>
<div className="modal-container">
<select
id="year-select"
value={selectedYear}
onChange={handleChange}
>
{years.map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
<button id="modal-ok" onClick={changeSelectYear}>
OK
</button>
<button id="modal-cancel" onClick={closeModal}>
Cancel
</button>
</div>
</div>
<div className="selected-day">
{selectDay && <p>選択した日: {selectDay}</p>}
</div>
</div>
</div>
</>
);
};
export default Calendar;
カレンダーSCSS
calendar.scss
/* 全体のコンテナ */
.calendar-container {
width: 100%;
}
/* カレンダーの内容 */
.calendar-content {
position: relative;
width: 700px;
height: 660px;
padding: 10px;
font-size: 18px;
font-style: italic;
margin: 10px auto;
justify-content: center;
text-align: center;
@media (max-width: 576px) {
margin: 5px auto;
width: 98%;
height: 400px;
}
}
/* 年変更ボタン */
#year_button {
position: absolute;
top: 28px;
left: 12px;
font-size: 30px;
border: none;
padding: 1px 15px 0;
border-radius: 5px;
background-color: #0074d0;
color: white;
font-style: italic;
box-shadow: 3px 3px 3px -3px black;
transition: background-color 0.3s ease, color 0.3s ease;
@media (max-width: 576px) {
top: 12px;
left: 18px;
font-size: 20px;
}
}
#year_button:hover {
background-color: #6df049;
}
/* 年移動ボタン */
.calendar-year-prev,
.calendar-year-next {
border: none;
font-size: 12px;
font-style: italic;
position: absolute;
transform: scale(5, 1);
color: #0074d0;
background-color: transparent;
@media (max-width: 576px) {
font-size: 10px;
}
}
.calendar-year-prev {
top: 15px;
left: 82px;
@media (max-width: 576px) {
top: 0;
left: 65px;
}
}
.calendar-year-next {
top: 64px;
left: 82px;
@media (max-width: 576px) {
top: 36.6px;
left: 65px;
}
}
.calendar-year-prev:hover,
.calendar-year-next:hover {
color: #6df049;
}
/* 月移動ボタン */
.calendar-prev,
.calendar-next {
font-size: 20px;
font-style: italic;
position: absolute;
color: #0074d0;
background-color: transparent;
border: none;
top: 54px;
@media (max-width: 576px) {
font-size: 14px;
top: 28px;
}
}
.calendar-prev {
right: 62px;
@media (max-width: 576px) {
right: 44px;
}
}
.calendar-next {
right: 10px;
}
.calendar-prev:hover,
.calendar-next:hover {
color: #6df049;
}
/* カレンダーのフィールド */
.calendar-field {
display: grid;
grid-template-columns: repeat(7, 1fr);
margin: 80px auto !important;
width: 100%;
border-bottom: 3px solid #0074d0;
border-left: 3px solid #0074d0;
@media (max-width: 576px) {
margin: 44px auto !important;
}
}
/* 曜日ラベル */
.calendar-label {
display: flex;
align-items: center;
justify-content: center;
height: 50px !important;
color: white;
text-align: center;
border-right: 3px solid white;
border-top: 3px solid #0074d0;
margin: 0 !important;
font-size: 22px;
font-weight: 400;
background-color: #0074d0;
@media (max-width: 576px) {
font-size: 14px;
}
}
.calendar-label.sunday {
color: red;
}
.calendar-label.saturday {
color: blue;
border-right: 3px solid #0074d0;
}
/* カレンダーのセル */
.calendar-cell {
position: relative;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
height: 59px;
background-color: white;
border-right: 3px solid #0074d0;
border-top: 3px solid #0074d0;
margin: 0;
@media (max-width: 576px) {
font-size: 18px;
height: 54px;
}
}
.calendar-cell.sunday {
color: red;
}
.calendar-cell.sunday.prevmonth,
.calendar-cell.sunday.nextmonth {
color: rgb(200, 36, 36);
font-size: 14px;
background-color: rgb(228, 231, 230);
}
.calendar-cell.saturday {
color: blue;
}
.calendar-cell.saturday.prevmonth,
.calendar-cell.saturday.nextmonth {
color: rgb(29, 29, 208);
font-size: 14px;
background-color: rgb(228, 231, 230);
}
.calendar-cell.weekday {
color: black;
}
.calendar-cell.weekday.prevmonth,
.calendar-cell.weekday.nextmonth {
color: rgb(108, 105, 105);
font-size: 14px;
background-color: rgb(228, 231, 230);
}
.calendar-cell.weekday.holiday,
.calendar-cell.saturday.holiday {
color: red;
position: relative;
}
/* 祝日名 */
div.holiday-name {
position: absolute;
font-size: 8px !important;
color: red;
text-align: center;
top: 40px;
@media (max-width: 576px) {
font-size: 6px !important;
top: 40px;
}
}
/* モーダルスタイル */
#modal {
position: absolute;
width: 250px;
height: 59px;
margin-top: 0;
font-size: 24px;
background-color: white;
border: solid 4px;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
top: 16px;
z-index: 2;
}
.modal-container {
display: flex;
padding: 0 !important;
}
/* モーダルのボタンとセレクトボックス */
select#year-select {
font-size: 21px;
font-style: italic;
margin: 0 5px;
padding: 0 10px;
border: none;
background-color: transparent;
}
button#modal-ok,
button#modal-cancel {
border: none;
background-color: #0074d0;
color: white;
font-size: 19px;
margin: 0 5px;
padding: 1px 7px 1px 3px !important;
font-style: italic;
border-radius: 5px;
}
button#modal-cancel {
background-color: red;
}
書き込み等ができるコード
上記を進化させて、カレンダーにイベントを追加してデータベースとの連携の元となるものを作成しました。
カレンダーファイル
calendar.js
import React, { cloneElement, useEffect, useState } from "react";
import "./Calendar.scss";
const Calendar = () => {
const [year, setYear] = useState(new Date().getFullYear());
const [selectedYear, setSelectedYear] = useState(new Date().getFullYear());
const [month, setMonth] = useState(new Date().getMonth());
const [holidays, setHolidays] = useState({});
const [showModal, setShowModal] = useState(false);
const [events, setEvents] = useState({});
const [selectDay, setSelectDay] = useState("");
const [newEventTitle, setNewEventTitle] = useState("");
const [newEventContent, setNewEventContent] = useState("");
const getDaysInMonth = (year, month) => {
return new Date(year, month + 1, 0).getDate();
};
const getFirstDayOfMonth = (year, month) => {
return new Date(year, month, 1).getDay();
};
const getPrevMonthDays = (year, month) => {
const prevMonth = month === 0 ? 11 : month - 1;
const prevMonthYear = month === 0 ? year - 1 : year;
const prevMonthDays = getDaysInMonth(prevMonthYear, prevMonth);
const prevMonthFirstDay = getFirstDayOfMonth(prevMonthYear, prevMonth);
return { prevMonthDays, prevMonthFirstDay };
};
const getNextMonthDays = (year, month) => {
const nextMonth = month === 11 ? 0 : month + 1;
const nextMonthYear = month === 11 ? year + 1 : year;
const nextMonthDays = getDaysInMonth(nextMonthYear, nextMonth);
const nextMonthFirstDay = getFirstDayOfMonth(nextMonthYear, nextMonth);
return { nextMonthDays, nextMonthFirstDay };
};
const daysInMonth = getDaysInMonth(year, month);
const firstDayOfMonth = getFirstDayOfMonth(year, month);
const { prevMonthDays, prevMonthFirstDay } = getPrevMonthDays(year, month);
const { nextMonthDays, nextMonthFirstDay } = getNextMonthDays(year, month);
const prevRemainDays = firstDayOfMonth;
const renderDays = () => {
const days = [];
let l = 0;
if (firstDayOfMonth !== 6) {
for (
let i = prevMonthDays - firstDayOfMonth;
i < prevMonthDays + 1;
i++
) {
l += 1;
// 日付の形式を整える
const date = i;
const targetmonth = "prevMonth";
let prevmonth = "";
let prevyear = "";
if (month === 0) {
prevmonth = 12;
prevyear = year - 1;
} else {
prevmonth = month;
prevyear = year;
}
const formattedDate = `${prevyear}-${String(prevmonth).padStart(
2,
"0"
)}-${String(date).padStart(2, "0")}`;
const isHoliday = holidays[formattedDate] || null;
// const eventall = events[formattedDate];
days.push({
day: date,
isHoliday: !!isHoliday,
holidayName: isHoliday || null,
targetmonth: targetmonth,
formattedDate: formattedDate,
// event: eventall,
});
}
}
for (let i = 1; i <= daysInMonth; i++) {
const date = i;
const targetmonth = "nowMonth";
const formattedDate = `${year}-${String(month).padStart(2, "0")}-${String(
date
).padStart(2, "0")}`;
const formattedDatekai = `${year}-${String(month + 1).padStart(
2,
"0"
)}-${String(date).padStart(2, "0")}`;
const formattedDateForHoliday = `${
year + Math.floor(month / 12)
}-${String((month % 12) + 1).padStart(2, "0")}-${String(date).padStart(
2,
"0"
)}`;
const isHoliday = holidays[formattedDateForHoliday] || null;
const eventall = events[formattedDatekai];
days.push({
day: date,
isHoliday: !!isHoliday,
holidayName: isHoliday || null,
targetmonth: targetmonth,
formattedDate: formattedDatekai,
event: eventall || [],
});
}
for (let i = 1; i <= 6 - nextMonthFirstDay; i++) {
let nextmonth = 0;
let nextmonthyear = 0;
if (daysInMonth + l === 35 || daysInMonth + l === 28) {
let l = null;
} else {
const date = i;
const targetmonth = "nextMonth";
if (month === 11) {
nextmonth = 1;
nextmonthyear = year + 1;
} else {
nextmonth = month + 2;
nextmonthyear = year;
}
const formattedDate = `${nextmonthyear}-${String(nextmonth).padStart(
2,
"0"
)}-${String(date).padStart(2, "0")}`;
const isHoliday = holidays[formattedDate] || null;
// const eventall = events[formattedDate];
days.push({
day: date,
isHoliday: !!isHoliday,
holidayName: isHoliday || null,
targetmonth: targetmonth,
formattedDate: formattedDate,
// event: eventall,
});
}
}
return days;
};
const handleYearChange = (direction) => {
setYear((prev) => prev + direction);
};
const handleMonthChange = (direction) => {
setMonth((prev) => {
const newMonth = prev + direction;
if (newMonth < 0) {
setYear((y) => y - 1);
return 11;
} else if (newMonth > 11) {
setYear((y) => y + 1);
return 0;
}
return newMonth;
});
};
useEffect(() => {
const fetchHolidays = async (year) => {
const response = await fetch(
`https://holidays-jp.github.io/api/v1/date.json`
);
if (response.ok) {
const data = await response.json();
const filteredHolidays = {};
for (const [date, name] of Object.entries(data)) {
if (date.startsWith(year) || date.startsWith(year + 1)) {
filteredHolidays[date] = name;
}
}
setHolidays(filteredHolidays);
} else {
console.error("祝日データの取得に失敗しました。");
}
};
fetchHolidays(year);
}, [year]);
const openModal = () => {
setShowModal(true);
};
const closeModal = () => {
setShowModal(false);
};
const years = [];
const yearSelect = () => {
for (let year = 2100; year >= 1924; year--) {
years.push(year);
}
};
yearSelect();
const handleChange = (event) => {
setSelectedYear(event.target.value);
};
const changeSelectYear = () => {
setYear(selectedYear);
setShowModal(false);
};
const changeSelectDay = (day) => {
if (day.targetmonth === "prevMonth") {
let premonth = "";
let preyear = "";
if (month === 0) {
premonth = 12;
preyear = year - 1;
} else {
premonth = month;
preyear = year;
}
const newSelectDay = `${preyear}-${String(premonth).padStart(
2,
"0"
)}-${String(day.day).padStart(2, "0")}`;
setSelectDay(newSelectDay);
} else if (day.targetmonth === "nextMonth") {
let nextmonth = "";
let nextyear = "";
if (month === 11) {
nextmonth = 1;
nextyear = year + 1;
} else {
nextmonth = month + 2;
nextyear = year;
}
const newSelectDay = `${nextyear}-${String(nextmonth).padStart(
2,
"0"
)}-${String(day.day).padStart(2, "0")}`;
setSelectDay(newSelectDay);
} else {
const newSelectDay = `${year}-${String(month + 1).padStart(
2,
"0"
)}-${String(day.day).padStart(2, "0")}`;
setSelectDay(newSelectDay);
}
};
// selectDayが変更された時にアラートを表示
useEffect(() => {
if (selectDay) {
// alert(selectDay);
}
}, [selectDay]);
const handleAddEvent = () => {
console.log("handleAddEvent executed");
if (newEventTitle.trim().length > 10) {
alert("タイトルは4文字以内にしてください。");
return;
}
const eventData = {
title: newEventTitle, // eventTitle
content: newEventContent, // eventContent
};
if (!newEventTitle.trim()) return; // 空白入力を無視
setEvents((prevEvents) => {
const updatedEvents = { ...prevEvents };
if (!updatedEvents[selectDay]) {
updatedEvents[selectDay] = [];
}
// 重複を防ぐ処理を追加
if (
!updatedEvents[selectDay].some(
(event) =>
event.title === eventData.title &&
event.content === eventData.content
)
) {
updatedEvents[selectDay].push(eventData);
}
return updatedEvents;
});
setNewEventTitle("");
setNewEventContent("");
setSelectDay(null);
};
const handleEventClick = (event) => {
alert(`"イベント詳細をリンクしましょう" + ${event.title}`);
};
return (
<>
<div className="calendar-container">
<div className="calendar-content">
<button id="year_button" onClick={openModal}>
{year} / {month + 1 > 12 ? 1 : month + 1}
</button>
<button
className="calendar-year-prev"
onClick={() => handleYearChange(-1)}
>
▲
</button>
<button
className="calendar-year-next"
onClick={() => handleYearChange(1)}
>
▼
</button>
<button
className="calendar-prev"
onClick={() => handleMonthChange(-1)}
>
◀◀
</button>
<button
className="calendar-next"
onClick={() => handleMonthChange(1)}
>
▶▶
</button>
<div className="calendar-field">
{["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map(
(day, index) => (
<div
className={`calendar-label ${
index === 0 ? "sunday" : index === 6 ? "saturday" : ""
}`}
key={day}
>
{day}
</div>
)
)}
{renderDays().map((day, index) => {
const dayOfWeek = index % 7;
const dateKey = `${year}-${String(month + 1).padStart(
2,
"0"
)}-${String(day.date).padStart(2, "0")}`;
return (
<div className="calendar-cell">
<div
className={`calendar-cell-inner ${
day.day && dayOfWeek === 0
? "sunday"
: day.day && dayOfWeek === 6
? "saturday"
: "weekday"
} ${day.isHoliday ? "holiday" : ""} ${
day.targetmonth === "prevMonth" ? "prevmonth" : ""
} ${day.targetmonth === "nextMonth" ? "nextmonth" : ""}`}
key={index}
onClick={() => day.day && changeSelectDay(day)}
>
{day.day || ""}{" "}
</div>
<div className="holiday-name">{day.holidayName || ""}</div>
<div className="calendar-events">
{day.event &&
day.event.map((event, index) => (
<div className="calendar-event" key={index}>
<p
className="calendar-event-button"
onClick={() => handleEventClick(event)}
>
{event.title}
</p>
{/* <p>{event.title}</p> */}
{/* <p>{event.content}</p> */}
</div>
))}
</div>
</div>
);
})}
</div>
<div>ぞんおう{JSON.stringify(events)}</div>
{selectDay && (
<div className="calendar-modal-input-field">
<h3>{`${selectDay}の予定`}</h3>
<input
type="text"
value={newEventTitle}
onChange={(e) => setNewEventTitle(e.target.value)}
placeholder="タイトル入力(カレンダー表示は3文字のみ)"
/>
<textarea
type="text"
value={newEventContent}
onChange={(e) => setNewEventContent(e.target.value)}
placeholder="予定を入力"
/>
<div className="calendar-modal-input-button">
<button onClick={handleAddEvent}>追加</button>
<button onClick={() => setSelectDay(null)}>キャンセル</button>
</div>
</div>
)}
<div id="modal" style={{ display: showModal ? "" : "none" }}>
<div className="modal-container">
<select
id="year-select"
value={selectedYear}
onChange={handleChange}
>
{years.map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
<button id="modal-ok" onClick={changeSelectYear}>
OK
</button>
<button id="modal-cancel" onClick={closeModal}>
Cancel
</button>
</div>
</div>
<div className="selected-day">
{selectDay && <p>選択した日: {selectDay}</p>}
</div>
</div>
</div>
</>
);
};
export default Calendar;
カレンダーSCSS
calendar.scss
/* 全体のコンテナ */
.calendar-container {
width: 100%;
}
/* カレンダーの内容 */
.calendar-content {
position: relative;
width: 700px;
height: 660px;
padding: 10px;
font-size: 18px;
font-style: italic;
margin: 10px auto;
justify-content: center;
text-align: center;
@media (max-width: 576px) {
margin: 5px auto;
width: 98%;
height: 400px;
}
}
/* 年変更ボタン */
#year_button {
position: absolute;
top: 28px;
left: 12px;
font-size: 30px;
border: none;
padding: 1px 15px 0;
border-radius: 5px;
background-color: #0074d0;
color: white;
font-style: italic;
box-shadow: 3px 3px 3px -3px black;
transition: background-color 0.3s ease, color 0.3s ease;
@media (max-width: 576px) {
top: 12px;
left: 18px;
font-size: 20px;
}
}
#year_button:hover {
background-color: #6df049;
}
/* 年移動ボタン */
.calendar-year-prev,
.calendar-year-next {
border: none;
font-size: 12px;
font-style: italic;
position: absolute;
transform: scale(5, 1);
color: #0074d0;
background-color: transparent;
@media (max-width: 576px) {
font-size: 10px;
}
}
.calendar-year-prev {
top: 15px;
left: 82px;
@media (max-width: 576px) {
top: 0;
left: 65px;
}
}
.calendar-year-next {
top: 64px;
left: 82px;
@media (max-width: 576px) {
top: 36.6px;
left: 65px;
}
}
.calendar-year-prev:hover,
.calendar-year-next:hover {
color: #6df049;
}
/* 月移動ボタン */
.calendar-prev,
.calendar-next {
font-size: 20px;
font-style: italic;
position: absolute;
color: #0074d0;
background-color: transparent;
border: none;
top: 54px;
@media (max-width: 576px) {
font-size: 14px;
top: 28px;
}
}
.calendar-prev {
right: 62px;
@media (max-width: 576px) {
right: 44px;
}
}
.calendar-next {
right: 10px;
}
.calendar-prev:hover,
.calendar-next:hover {
color: #6df049;
}
/* カレンダーのフィールド */
.calendar-field {
display: grid;
grid-template-columns: repeat(7, 1fr);
margin: 80px auto !important;
width: 100%;
border-bottom: 3px solid #0074d0;
border-left: 3px solid #0074d0;
@media (max-width: 576px) {
margin: 44px auto !important;
}
}
/* 曜日ラベル */
.calendar-label {
display: flex;
align-items: center;
justify-content: center;
height: 50px !important;
color: white;
text-align: center;
border-right: 3px solid white;
border-top: 3px solid #0074d0;
margin: 0 !important;
font-size: 22px;
font-weight: 400;
background-color: #0074d0;
@media (max-width: 576px) {
font-size: 14px;
}
}
.calendar-label.sunday {
color: red;
}
.calendar-label.saturday {
color: blue;
border-right: 3px solid #0074d0;
}
.calendar-cell {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 22px;
height: 74px;
background-color: white;
border-right: 3px solid #0074d0;
border-top: 3px solid #0074d0;
margin: 0;
@media (max-width: 576px) {
font-size: 18px;
height: 54px;
}
}
/* カレンダーのセル */
.calendar-cell-inner {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 22px;
height: 74px;
background-color: white;
margin: 0;
@media (max-width: 576px) {
font-size: 18px;
height: 54px;
}
}
.calendar-cell-inner.sunday {
color: red;
}
.calendar-cell-inner.sunday.prevmonth,
.calendar-cell-inner.sunday.nextmonth {
color: rgb(200, 36, 36);
width: 100%;
font-size: 14px;
background-color: rgb(228, 231, 230);
}
.calendar-cell-inner.saturday {
color: blue;
}
.calendar-cell-inner.saturday.prevmonth,
.calendar-cell-inner.saturday.nextmonth {
width: 100%;
color: rgb(29, 29, 208);
font-size: 14px;
background-color: rgb(228, 231, 230);
}
.calendar-cell-inner.weekday {
color: black;
}
.calendar-cell-inner.weekday.prevmonth,
.calendar-cell-inner.weekday.nextmonth {
width: 100%;
color: rgb(108, 105, 105);
font-size: 14px;
background-color: rgb(228, 231, 230);
}
.calendar-cell-inner.weekday.holiday,
.calendar-cell-inner.saturday.holiday {
width: 100%;
color: red;
position: relative;
}
/* 祝日名 */
div.holiday-name {
position: absolute;
font-size: 8px !important;
color: red;
text-align: center;
top: 55px;
@media (max-width: 576px) {
font-size: 6px !important;
top: 40px;
}
}
/* モーダルスタイル */
#modal {
position: absolute;
width: 250px;
height: 59px;
margin-top: 0;
font-size: 24px;
background-color: white;
border: solid 4px;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
top: 16px;
z-index: 2;
}
.modal-container {
display: flex;
padding: 0 !important;
}
/* モーダルのボタンとセレクトボックス */
select#year-select {
font-size: 21px;
font-style: italic;
margin: 0 5px;
padding: 0 10px;
border: none;
background-color: transparent;
}
button#modal-ok,
button#modal-cancel {
border: none;
background-color: #0074d0;
color: white;
font-size: 19px;
margin: 0 5px;
padding: 1px 7px 1px 3px !important;
font-style: italic;
border-radius: 5px;
}
button#modal-cancel {
background-color: red;
}
.calendar-modal-input-field {
background-color: #25c732;
position: absolute;
padding: 0 10px;
top: 150px;
left: 200px;
width: 350px;
height: 270px;
border-radius: 5px;
box-shadow: 3px 3px 3px -3px black;
@media (max-width: 576px) {
top: 50px;
left: 10px;
width: 95%;
height: 250px;
}
h3 {
padding: 5px !important;
margin: 5px 0 0;
color: white;
@media (max-width: 576px) {
font-size: 16px;
}
}
input {
padding: 5px;
margin: 0 0 20px;
width: 95%;
border-radius: 3px;
border: none;
@media (max-width: 576px) {
font-size: 14px;
}
}
textarea {
margin: 0;
width: 95%;
height: 100px;
border-radius: 3px;
border: none;
@media (max-width: 576px) {
font-size: 14px;
}
}
}
.calendar-modal-input-button {
margin: 0 20px;
display: flex;
justify-content: space-between;
gap: 10px;
padding: 10px;
}
.calendar-modal-input-button button {
background-color: white;
color: #25c732;
font-size: 16px;
display: flex;
justify-content: space-between;
gap: 30px;
border: none;
margin: 10px 10px;
padding: 5px 10px;
border-radius: 5px;
box-shadow: 5px 5px 5px -5px black;
@media (max-width: 576px) {
font-size: 14px;
margin: 0 10px;
padding: 0 5px;
}
}
.calendar-event {
background-color: rgba(48, 123, 221, 0.5);
font-size: 10px;
font-style: italic;
border-radius: 2px;
width: 30px;
height: 16px;
position: absolute;
bottom: 0;
border-top: white solid 2px;
left: 63px;
text-align: left;
transform: skew(-15deg);
overflow: hidden;
white-space: nowrap;
@media (max-width: 576px) {
height: 10px;
width: 18px;
}
p {
font-size: 10px;
@media (max-width: 576px) {
font-size: 6px;
}
}
}
.calendar-event:nth-child(1) {
position: absolute;
top: 0;
left: 1px;
}
.calendar-event:nth-child(2) {
top: 14px;
left: 1px;
@media (max-width: 576px) {
top: 9px;
}
}
.calendar-event:nth-child(3) {
top: 28px;
left: 1px;
@media (max-width: 576px) {
top: 18px;
}
}
.calendar-event:nth-child(4) {
top: 42px;
left: 1px;
@media (max-width: 576px) {
top: 27px;
}
}
.calendar-event:nth-child(5) {
top: 56px;
left: 1px;
@media (max-width: 576px) {
top: 36px;
}
}
.calendar-event:nth-child(6) {
top: 0;
left: 62px;
@media (max-width: 576px) {
top: 0;
left: 48px;
}
}
.calendar-event:nth-child(7) {
top: 14px;
left: 62px;
@media (max-width: 576px) {
top: 9px;
left: 48px;
}
}
.calendar-event:nth-child(8) {
top: 28px;
left: 62px;
@media (max-width: 576px) {
top: 18px;
left: 48px;
}
}
.calendar-event:nth-child(9) {
top: 42px;
@media (max-width: 576px) {
top: 27px;
left: 48px;
}
}
.calendar-event:nth-child(10) {
top: 56px;
@media (max-width: 576px) {
top: 36px;
left: 48px;
}
}