react-jalali-events-calendar 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +159 -0
- package/dist/index.d.mts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +434 -0
- package/dist/index.mjs +407 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
### π
Persian Calendar for React & Next.js
|
|
2
|
+
A fully customizable Persian calendar component for React and Next.js with support for events, holidays, tooltips, RTL layout, and modern UI.
|
|
3
|
+
|
|
4
|
+
## β¨ Features:
|
|
5
|
+
|
|
6
|
+
- β
Accurate Gregorian β Jalali date conversion
|
|
7
|
+
- β
Event & holiday support
|
|
8
|
+
- β
Holiday detection
|
|
9
|
+
- β
Tooltip with Persian & Gregorian dates
|
|
10
|
+
- β
Highlights todayβs date
|
|
11
|
+
- β
Shows previous & next month days
|
|
12
|
+
- β
Fully responsive
|
|
13
|
+
- β
RTL-first design
|
|
14
|
+
- β
External month navigation control
|
|
15
|
+
- β
No external calendar dependencies
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## π¦ Installation:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install react-persian-calendar
|
|
22
|
+
```
|
|
23
|
+
or
|
|
24
|
+
```bash
|
|
25
|
+
yarn add react-persian-calendar
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## β οΈ Tailwind CSS Requirement
|
|
29
|
+
|
|
30
|
+
This calendar **requires Tailwind CSS** to be installed in your project.
|
|
31
|
+
|
|
32
|
+
All styles, layout, responsiveness, and tooltips are built using Tailwind utility classes.
|
|
33
|
+
Without Tailwind CSS, the calendar **will not render correctly**.
|
|
34
|
+
|
|
35
|
+
### Install Tailwind CSS
|
|
36
|
+
|
|
37
|
+
If you are using Next.js or React, follow the official guide:
|
|
38
|
+
|
|
39
|
+
π https://tailwindcss.com/docs/installation
|
|
40
|
+
|
|
41
|
+
After installation, make sure Tailwind is properly configured in your project.
|
|
42
|
+
|
|
43
|
+
## π Basic Usage:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { useState } from "react";
|
|
47
|
+
import { Calendar } from "react-persian-calendar";
|
|
48
|
+
|
|
49
|
+
function App() {
|
|
50
|
+
const [currentDate, setCurrentDate] = useState("");
|
|
51
|
+
const [goNext, setGoNext] = useState(false);
|
|
52
|
+
const [goPrev, setGoPrev] = useState(false);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Calendar
|
|
56
|
+
goNext={goNext}
|
|
57
|
+
goPrev={goPrev}
|
|
58
|
+
setCurrentDate={setCurrentDate}
|
|
59
|
+
setNext={() => setGoNext(false)}
|
|
60
|
+
setPrev={() => setGoPrev(false)}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
## π§ Props:
|
|
66
|
+
|
|
67
|
+
| Prop | Type | Description |
|
|
68
|
+
| --------------------------- | -------------------------- | ------------------------------------- |
|
|
69
|
+
| `setCurrentDate` | `(value: string) => void` | Returns formatted current date string |
|
|
70
|
+
| `goNext` | `boolean` | Navigate to next month |
|
|
71
|
+
| `goPrev` | `boolean` | Navigate to previous month |
|
|
72
|
+
| `setNext` | `(value: boolean) => void` | Reset next navigation trigger |
|
|
73
|
+
| `setPrev` | `(value: boolean) => void` | Reset previous navigation trigger |
|
|
74
|
+
| `showEvents` | `boolean` | Enable event tooltips |
|
|
75
|
+
| `headerColor` | `string` | Header background color |
|
|
76
|
+
| `headerTextColor` | `string` | Header text color |
|
|
77
|
+
| `daysBgColor` | `string` | Current month days background |
|
|
78
|
+
| `daysTextColor` | `string` | Current month days text color |
|
|
79
|
+
| `holidaysBgColor` | `string` | Holiday background color |
|
|
80
|
+
| `holidayTextColor` | `string` | Holiday text color |
|
|
81
|
+
| `currentDayBgColor` | `string` | Today background color |
|
|
82
|
+
| `currentDaytextColor` | `string` | Today text color |
|
|
83
|
+
| `outsideMonthDaysBg` | `string` | Outside month days background |
|
|
84
|
+
| `outsideMonthDaysTextColor` | `string` | Outside month days text color |
|
|
85
|
+
| `borderColor` | `string` | Calendar border color |
|
|
86
|
+
|
|
87
|
+
## π¨ Full Customization Example:
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
<Calendar
|
|
91
|
+
setCurrentDate={setCurrentDate}
|
|
92
|
+
goNext={goNext}
|
|
93
|
+
goPrev={goPrev}
|
|
94
|
+
setNext={setGoNext}
|
|
95
|
+
setPrev={setGoPrev}
|
|
96
|
+
showEvents={true}
|
|
97
|
+
headerColor="#F1F4F9"
|
|
98
|
+
headerTextColor="#111827"
|
|
99
|
+
daysBgColor="#ffffff"
|
|
100
|
+
daysTextColor="#111827"
|
|
101
|
+
holidaysBgColor="#FEE2E2"
|
|
102
|
+
holidayTextColor="#DC2626"
|
|
103
|
+
currentDayBgColor="#2563EB"
|
|
104
|
+
currentDaytextColor="#ffffff"
|
|
105
|
+
outsideMonthDaysBg="#F9FAFB"
|
|
106
|
+
outsideMonthDaysTextColor="#9CA3AF"
|
|
107
|
+
borderColor="#D1D5DB"
|
|
108
|
+
/>
|
|
109
|
+
```
|
|
110
|
+
## π‘ Events Data Source:
|
|
111
|
+
By default, the calendar fetches events from:
|
|
112
|
+
```http
|
|
113
|
+
GET https://badesaba.ir/api/site/getDataCalendar/{month}/{year}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Expected Response Shape:
|
|
117
|
+
```ts
|
|
118
|
+
type Event = {
|
|
119
|
+
event: string;
|
|
120
|
+
holiday: boolean;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
type CalendarData = {
|
|
124
|
+
date: string; // YYYY-MM-DD (Gregorian)
|
|
125
|
+
events: Event[];
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
## π§ Event Tooltip
|
|
129
|
+
|
|
130
|
+
- Appears on hover
|
|
131
|
+
- Displays:
|
|
132
|
+
- Jalali date
|
|
133
|
+
- Gregorian date
|
|
134
|
+
- Event list
|
|
135
|
+
- Holidays are visually highlighted
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
## π Built With:
|
|
140
|
+
- React
|
|
141
|
+
- Next.js (App Router compatible)
|
|
142
|
+
- TypeScript
|
|
143
|
+
- Tailwind CSS
|
|
144
|
+
- RTL layout support
|
|
145
|
+
|
|
146
|
+
## β οΈ Important Notes:
|
|
147
|
+
This component must be used inside a Client Component
|
|
148
|
+
Required at the top of the file:
|
|
149
|
+
```tsx
|
|
150
|
+
"use client";
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## π License:
|
|
154
|
+
MIT License
|
|
155
|
+
Free for personal and commercial use.
|
|
156
|
+
|
|
157
|
+
## β€οΈ Contributing:
|
|
158
|
+
Issues and Pull Requests are welcome!
|
|
159
|
+
Feel free to improve or extend this calendar π±
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type Props = {
|
|
4
|
+
setCurrentDate: (value: string) => void;
|
|
5
|
+
goNext: boolean;
|
|
6
|
+
goPrev: boolean;
|
|
7
|
+
headerColor?: string;
|
|
8
|
+
headerTextColor?: string;
|
|
9
|
+
showEvents?: boolean;
|
|
10
|
+
holidaysBgColor?: string;
|
|
11
|
+
daysTextColor?: string;
|
|
12
|
+
currentDayBgColor?: string;
|
|
13
|
+
currentDaytextColor?: string;
|
|
14
|
+
holidayTextColor?: string;
|
|
15
|
+
daysBgColor?: string;
|
|
16
|
+
outsideMonthDaysBg?: string;
|
|
17
|
+
outsideMonthDaysTextColor?: string;
|
|
18
|
+
borderColor?: string;
|
|
19
|
+
setNext: (value: boolean) => void;
|
|
20
|
+
setPrev: (value: boolean) => void;
|
|
21
|
+
};
|
|
22
|
+
declare function Calendar({ setCurrentDate, headerColor, showEvents, goNext, goPrev, headerTextColor, setNext, daysBgColor, setPrev, holidaysBgColor, daysTextColor, holidayTextColor, currentDayBgColor, currentDaytextColor, borderColor, outsideMonthDaysBg, outsideMonthDaysTextColor, }: Props): react_jsx_runtime.JSX.Element;
|
|
23
|
+
|
|
24
|
+
export { Calendar };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type Props = {
|
|
4
|
+
setCurrentDate: (value: string) => void;
|
|
5
|
+
goNext: boolean;
|
|
6
|
+
goPrev: boolean;
|
|
7
|
+
headerColor?: string;
|
|
8
|
+
headerTextColor?: string;
|
|
9
|
+
showEvents?: boolean;
|
|
10
|
+
holidaysBgColor?: string;
|
|
11
|
+
daysTextColor?: string;
|
|
12
|
+
currentDayBgColor?: string;
|
|
13
|
+
currentDaytextColor?: string;
|
|
14
|
+
holidayTextColor?: string;
|
|
15
|
+
daysBgColor?: string;
|
|
16
|
+
outsideMonthDaysBg?: string;
|
|
17
|
+
outsideMonthDaysTextColor?: string;
|
|
18
|
+
borderColor?: string;
|
|
19
|
+
setNext: (value: boolean) => void;
|
|
20
|
+
setPrev: (value: boolean) => void;
|
|
21
|
+
};
|
|
22
|
+
declare function Calendar({ setCurrentDate, headerColor, showEvents, goNext, goPrev, headerTextColor, setNext, daysBgColor, setPrev, holidaysBgColor, daysTextColor, holidayTextColor, currentDayBgColor, currentDaytextColor, borderColor, outsideMonthDaysBg, outsideMonthDaysTextColor, }: Props): react_jsx_runtime.JSX.Element;
|
|
23
|
+
|
|
24
|
+
export { Calendar };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Calendar: () => Calendar
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/Calendar.tsx
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
30
|
+
function gregorianToJalali(gy, gm, gd) {
|
|
31
|
+
const g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
|
32
|
+
const gy2 = gm > 2 ? gy + 1 : gy;
|
|
33
|
+
let days = 355666 + 365 * gy + Math.floor((gy2 + 3) / 4) - Math.floor((gy2 + 99) / 100) + Math.floor((gy2 + 399) / 400) + gd + g_d_m[gm - 1];
|
|
34
|
+
let jy = -1595 + 33 * Math.floor(days / 12053);
|
|
35
|
+
days %= 12053;
|
|
36
|
+
jy += 4 * Math.floor(days / 1461);
|
|
37
|
+
days %= 1461;
|
|
38
|
+
if (days > 365) {
|
|
39
|
+
jy += Math.floor((days - 1) / 365);
|
|
40
|
+
days = (days - 1) % 365;
|
|
41
|
+
}
|
|
42
|
+
let jm, jd;
|
|
43
|
+
if (days < 186) {
|
|
44
|
+
jm = 1 + Math.floor(days / 31);
|
|
45
|
+
jd = 1 + days % 31;
|
|
46
|
+
} else {
|
|
47
|
+
jm = 7 + Math.floor((days - 186) / 30);
|
|
48
|
+
jd = 1 + (days - 186) % 30;
|
|
49
|
+
}
|
|
50
|
+
return [jy, jm, jd];
|
|
51
|
+
}
|
|
52
|
+
function jalaliToGregorian(jy, jm, jd) {
|
|
53
|
+
jy += 1595;
|
|
54
|
+
let days = -355668 + 365 * jy + Math.floor(jy / 33) * 8 + Math.floor((jy % 33 + 3) / 4) + jd + (jm < 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186);
|
|
55
|
+
let gy = 400 * Math.floor(days / 146097);
|
|
56
|
+
days %= 146097;
|
|
57
|
+
if (days > 36524) {
|
|
58
|
+
gy += 100 * Math.floor(--days / 36524);
|
|
59
|
+
days %= 36524;
|
|
60
|
+
if (days >= 365) days++;
|
|
61
|
+
}
|
|
62
|
+
gy += 4 * Math.floor(days / 1461);
|
|
63
|
+
days %= 1461;
|
|
64
|
+
if (days > 365) {
|
|
65
|
+
gy += Math.floor((days - 1) / 365);
|
|
66
|
+
days = (days - 1) % 365;
|
|
67
|
+
}
|
|
68
|
+
let gd = days + 1;
|
|
69
|
+
const sal_a = [
|
|
70
|
+
0,
|
|
71
|
+
31,
|
|
72
|
+
gy % 4 === 0 && gy % 100 !== 0 || gy % 400 === 0 ? 29 : 28,
|
|
73
|
+
31,
|
|
74
|
+
30,
|
|
75
|
+
31,
|
|
76
|
+
30,
|
|
77
|
+
31,
|
|
78
|
+
31,
|
|
79
|
+
30,
|
|
80
|
+
31,
|
|
81
|
+
30,
|
|
82
|
+
31
|
|
83
|
+
];
|
|
84
|
+
let gm;
|
|
85
|
+
for (gm = 0; gm < 13 && gd > sal_a[gm]; gm++) gd -= sal_a[gm];
|
|
86
|
+
return [gy, gm, gd];
|
|
87
|
+
}
|
|
88
|
+
function isLeapJalaliYear(jy) {
|
|
89
|
+
const fm = jy % 33;
|
|
90
|
+
return fm === 1 || fm === 5 || fm === 9 || fm === 13 || fm === 17 || fm === 22 || fm === 26 || fm === 30;
|
|
91
|
+
}
|
|
92
|
+
function jalaliMonthLength(jy, jm) {
|
|
93
|
+
if (jm <= 6) return 31;
|
|
94
|
+
if (jm <= 11) return 30;
|
|
95
|
+
return isLeapJalaliYear(jy) ? 30 : 29;
|
|
96
|
+
}
|
|
97
|
+
function Calendar({
|
|
98
|
+
setCurrentDate,
|
|
99
|
+
headerColor,
|
|
100
|
+
showEvents,
|
|
101
|
+
goNext,
|
|
102
|
+
goPrev,
|
|
103
|
+
headerTextColor,
|
|
104
|
+
setNext,
|
|
105
|
+
daysBgColor,
|
|
106
|
+
setPrev,
|
|
107
|
+
holidaysBgColor,
|
|
108
|
+
daysTextColor,
|
|
109
|
+
holidayTextColor,
|
|
110
|
+
currentDayBgColor,
|
|
111
|
+
currentDaytextColor,
|
|
112
|
+
borderColor,
|
|
113
|
+
outsideMonthDaysBg,
|
|
114
|
+
outsideMonthDaysTextColor
|
|
115
|
+
}) {
|
|
116
|
+
const gregorianMonthsFa = [
|
|
117
|
+
"",
|
|
118
|
+
"\u0698\u0627\u0646\u0648\u06CC\u0647",
|
|
119
|
+
"\u0641\u0648\u0631\u06CC\u0647",
|
|
120
|
+
"\u0645\u0627\u0631\u0633",
|
|
121
|
+
"\u0622\u0648\u0631\u06CC\u0644",
|
|
122
|
+
"\u0645\u0647",
|
|
123
|
+
"\u0698\u0648\u0626\u0646",
|
|
124
|
+
"\u0698\u0648\u0626\u06CC\u0647",
|
|
125
|
+
"\u0627\u0648\u062A",
|
|
126
|
+
"\u0633\u067E\u062A\u0627\u0645\u0628\u0631",
|
|
127
|
+
"\u0627\u06A9\u062A\u0628\u0631",
|
|
128
|
+
"\u0646\u0648\u0627\u0645\u0628\u0631",
|
|
129
|
+
"\u062F\u0633\u0627\u0645\u0628\u0631"
|
|
130
|
+
];
|
|
131
|
+
const now = /* @__PURE__ */ new Date();
|
|
132
|
+
const tehranOffset = 3.5;
|
|
133
|
+
const utc = now.getTime() + now.getTimezoneOffset() * 6e4;
|
|
134
|
+
const tehranDate = new Date(utc + 36e5 * tehranOffset);
|
|
135
|
+
const [gy, gm, gd] = [
|
|
136
|
+
tehranDate.getFullYear(),
|
|
137
|
+
tehranDate.getMonth() + 1,
|
|
138
|
+
tehranDate.getDate()
|
|
139
|
+
];
|
|
140
|
+
const [todayYear, todayMonth, todayDate] = gregorianToJalali(gy, gm, gd);
|
|
141
|
+
const [currentYear, setCurrentYear] = (0, import_react.useState)(todayYear);
|
|
142
|
+
const [currentMonth, setCurrentMonth] = (0, import_react.useState)(todayMonth);
|
|
143
|
+
const [calendarData, setCalendarData] = (0, import_react.useState)([]);
|
|
144
|
+
const weekDays = [
|
|
145
|
+
"\u0634\u0646\u0628\u0647",
|
|
146
|
+
"\u06CC\u06A9\u0634\u0646\u0628\u0647",
|
|
147
|
+
"\u062F\u0648\u0634\u0646\u0628\u0647",
|
|
148
|
+
"\u0633\u0647\u200C\u0634\u0646\u0628\u0647",
|
|
149
|
+
"\u0686\u0647\u0627\u0631\u0634\u0646\u0628\u0647",
|
|
150
|
+
"\u067E\u0646\u062C\u200C\u0634\u0646\u0628\u0647",
|
|
151
|
+
"\u062C\u0645\u0639\u0647"
|
|
152
|
+
];
|
|
153
|
+
(0, import_react.useEffect)(() => {
|
|
154
|
+
const fetchData = async () => {
|
|
155
|
+
try {
|
|
156
|
+
const response = await fetch(
|
|
157
|
+
`https://badesaba.ir/api/site/getDataCalendar/${currentMonth}/${currentYear}`
|
|
158
|
+
);
|
|
159
|
+
const data = await response.json();
|
|
160
|
+
setCalendarData(data);
|
|
161
|
+
} catch {
|
|
162
|
+
console.log("Error fetching calendar data:");
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
fetchData();
|
|
166
|
+
}, [currentMonth, currentYear]);
|
|
167
|
+
(0, import_react.useEffect)(() => {
|
|
168
|
+
const weekDays2 = [
|
|
169
|
+
"\u0634\u0646\u0628\u0647",
|
|
170
|
+
"\u06CC\u06A9\u0634\u0646\u0628\u0647",
|
|
171
|
+
"\u062F\u0648\u0634\u0646\u0628\u0647",
|
|
172
|
+
"\u0633\u0647\u200C\u0634\u0646\u0628\u0647",
|
|
173
|
+
"\u0686\u0647\u0627\u0631\u0634\u0646\u0628\u0647",
|
|
174
|
+
"\u067E\u0646\u062C\u0634\u0646\u0628\u0647",
|
|
175
|
+
"\u062C\u0645\u0639\u0647"
|
|
176
|
+
];
|
|
177
|
+
const months = [
|
|
178
|
+
"\u0641\u0631\u0648\u0631\u062F\u06CC\u0646",
|
|
179
|
+
"\u0627\u0631\u062F\u06CC\u0628\u0647\u0634\u062A",
|
|
180
|
+
"\u062E\u0631\u062F\u0627\u062F",
|
|
181
|
+
"\u062A\u06CC\u0631",
|
|
182
|
+
"\u0645\u0631\u062F\u0627\u062F",
|
|
183
|
+
"\u0634\u0647\u0631\u06CC\u0648\u0631",
|
|
184
|
+
"\u0645\u0647\u0631",
|
|
185
|
+
"\u0622\u0628\u0627\u0646",
|
|
186
|
+
"\u0622\u0630\u0631",
|
|
187
|
+
"\u062F\u06CC",
|
|
188
|
+
"\u0628\u0647\u0645\u0646",
|
|
189
|
+
"\u0627\u0633\u0641\u0646\u062F"
|
|
190
|
+
];
|
|
191
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
192
|
+
const tehranOffset2 = 3.5;
|
|
193
|
+
const utc2 = now2.getTime() + now2.getTimezoneOffset() * 6e4;
|
|
194
|
+
const tehranDate2 = new Date(utc2 + 36e5 * tehranOffset2);
|
|
195
|
+
const [gy2, gm2, gd2] = [
|
|
196
|
+
tehranDate2.getFullYear(),
|
|
197
|
+
tehranDate2.getMonth() + 1,
|
|
198
|
+
tehranDate2.getDate()
|
|
199
|
+
];
|
|
200
|
+
const [jy, jm, jd] = gregorianToJalali(gy2, gm2, gd2);
|
|
201
|
+
const weekdayIndex = tehranDate2.getDay();
|
|
202
|
+
const weekdayName = weekDays2[(weekdayIndex + 1) % 7];
|
|
203
|
+
const monthName = months[jm - 1];
|
|
204
|
+
setCurrentDate(`${weekdayName}\u060C ${jd} ${monthName} ${jy}`);
|
|
205
|
+
}, [currentMonth, currentYear]);
|
|
206
|
+
const days = (0, import_react.useMemo)(() => {
|
|
207
|
+
const result = [];
|
|
208
|
+
const daysInMonth = jalaliMonthLength(currentYear, currentMonth);
|
|
209
|
+
const [startGy, startGm, startGd] = jalaliToGregorian(
|
|
210
|
+
currentYear,
|
|
211
|
+
currentMonth,
|
|
212
|
+
1
|
|
213
|
+
);
|
|
214
|
+
const startDate = new Date(startGy, startGm - 1, startGd);
|
|
215
|
+
const firstDayOfWeek = (startDate.getDay() + 1) % 7;
|
|
216
|
+
let prevMonth = currentMonth - 1;
|
|
217
|
+
let prevYear = currentYear;
|
|
218
|
+
if (prevMonth < 1) {
|
|
219
|
+
prevMonth = 12;
|
|
220
|
+
prevYear--;
|
|
221
|
+
}
|
|
222
|
+
const prevDaysInMonth = jalaliMonthLength(prevYear, prevMonth);
|
|
223
|
+
for (let i = 0; i < firstDayOfWeek; i++) {
|
|
224
|
+
const date = prevDaysInMonth - firstDayOfWeek + 1 + i;
|
|
225
|
+
result.push({
|
|
226
|
+
year: prevYear,
|
|
227
|
+
month: prevMonth,
|
|
228
|
+
date,
|
|
229
|
+
currentMonth: false
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
for (let i = 1; i <= daysInMonth; i++) {
|
|
233
|
+
result.push({
|
|
234
|
+
year: currentYear,
|
|
235
|
+
month: currentMonth,
|
|
236
|
+
date: i,
|
|
237
|
+
currentMonth: true
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
let nextMonth = currentMonth + 1;
|
|
241
|
+
let nextYear = currentYear;
|
|
242
|
+
if (nextMonth > 12) {
|
|
243
|
+
nextMonth = 1;
|
|
244
|
+
nextYear++;
|
|
245
|
+
}
|
|
246
|
+
let nextDate = 1;
|
|
247
|
+
while (result.length < 42) {
|
|
248
|
+
result.push({
|
|
249
|
+
year: nextYear,
|
|
250
|
+
month: nextMonth,
|
|
251
|
+
date: nextDate++,
|
|
252
|
+
currentMonth: false
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return result;
|
|
256
|
+
}, [currentYear, currentMonth]);
|
|
257
|
+
const handlePrevMonth = (0, import_react.useCallback)(() => {
|
|
258
|
+
let newMonth = currentMonth - 1;
|
|
259
|
+
let newYear = currentYear;
|
|
260
|
+
if (newMonth < 1) {
|
|
261
|
+
newMonth = 12;
|
|
262
|
+
newYear--;
|
|
263
|
+
}
|
|
264
|
+
setCurrentMonth(newMonth);
|
|
265
|
+
setCurrentYear(newYear);
|
|
266
|
+
setPrev(false);
|
|
267
|
+
}, [currentMonth, currentYear]);
|
|
268
|
+
const handleNextMonth = (0, import_react.useCallback)(() => {
|
|
269
|
+
let newMonth = currentMonth + 1;
|
|
270
|
+
let newYear = currentYear;
|
|
271
|
+
if (newMonth > 12) {
|
|
272
|
+
newMonth = 1;
|
|
273
|
+
newYear++;
|
|
274
|
+
}
|
|
275
|
+
setCurrentMonth(newMonth);
|
|
276
|
+
setCurrentYear(newYear);
|
|
277
|
+
setNext(false);
|
|
278
|
+
}, [currentMonth, currentYear]);
|
|
279
|
+
(0, import_react.useEffect)(() => {
|
|
280
|
+
if (goNext) {
|
|
281
|
+
handleNextMonth();
|
|
282
|
+
}
|
|
283
|
+
if (goPrev) {
|
|
284
|
+
handlePrevMonth();
|
|
285
|
+
}
|
|
286
|
+
}, [goNext, goPrev, handleNextMonth, handlePrevMonth]);
|
|
287
|
+
const calendarMap = (0, import_react.useMemo)(() => {
|
|
288
|
+
const map = /* @__PURE__ */ new Map();
|
|
289
|
+
calendarData.forEach((item) => {
|
|
290
|
+
map.set(item.date, item.events);
|
|
291
|
+
});
|
|
292
|
+
return map;
|
|
293
|
+
}, [calendarData]);
|
|
294
|
+
const getEventsForDate = (year, month, date) => {
|
|
295
|
+
var _a;
|
|
296
|
+
const [gy2, gm2, gd2] = jalaliToGregorian(year, month, date);
|
|
297
|
+
const key = `${gy2}-${String(gm2).padStart(2, "0")}-${String(gd2).padStart(
|
|
298
|
+
2,
|
|
299
|
+
"0"
|
|
300
|
+
)}`;
|
|
301
|
+
return (_a = calendarMap.get(key)) != null ? _a : [];
|
|
302
|
+
};
|
|
303
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mx-auto h-full flex flex-col", dir: "rtl", children: [
|
|
304
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
305
|
+
"div",
|
|
306
|
+
{
|
|
307
|
+
style: {
|
|
308
|
+
...headerTextColor ? { color: headerTextColor } : {},
|
|
309
|
+
...headerColor ? { backgroundColor: headerColor } : { backgroundColor: "#F1F4F9" },
|
|
310
|
+
...borderColor ? { borderColor } : { borderColor: "#d9d7e0" }
|
|
311
|
+
},
|
|
312
|
+
className: `grid-cols-7 hidden 350:grid text-center bg-[#F1F4F9] border border-b-0 font-semibold rounded-t-[10px] py-2.5`,
|
|
313
|
+
children: weekDays.map((day, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "350:text-[13px]", children: day }, index))
|
|
314
|
+
}
|
|
315
|
+
),
|
|
316
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
317
|
+
"div",
|
|
318
|
+
{
|
|
319
|
+
style: {
|
|
320
|
+
backgroundColor: headerColor ? headerColor : "#F1F4F9"
|
|
321
|
+
},
|
|
322
|
+
className: `grid-cols-7 grid 350:hidden text-center bg-[#F1F4F9] font-semibold rounded-t-[10px] py-2.5`,
|
|
323
|
+
children: weekDays.map((day, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-12 350:hidden", children: day[0] }, index))
|
|
324
|
+
}
|
|
325
|
+
),
|
|
326
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
327
|
+
"div",
|
|
328
|
+
{
|
|
329
|
+
style: borderColor ? { borderColor } : { borderColor: "#d9d7e0" },
|
|
330
|
+
className: "grid grid-cols-7 flex-1 border-l-0 border-b-0 border",
|
|
331
|
+
children: days.map((d, i) => {
|
|
332
|
+
const events = getEventsForDate(d.year, d.month, d.date);
|
|
333
|
+
const isHolidayDay = events.some((event) => event.holiday);
|
|
334
|
+
const columnIndex = i % 7;
|
|
335
|
+
let tooltipPosition = "";
|
|
336
|
+
if (columnIndex <= 1) {
|
|
337
|
+
tooltipPosition = "right-0";
|
|
338
|
+
} else if (columnIndex >= 5) {
|
|
339
|
+
tooltipPosition = "left-0";
|
|
340
|
+
} else {
|
|
341
|
+
tooltipPosition = "left-1/2 -translate-x-1/2";
|
|
342
|
+
}
|
|
343
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
344
|
+
"div",
|
|
345
|
+
{
|
|
346
|
+
style: {
|
|
347
|
+
...!d.currentMonth && outsideMonthDaysBg ? { backgroundColor: outsideMonthDaysBg } : {},
|
|
348
|
+
...!d.currentMonth && outsideMonthDaysTextColor ? { color: outsideMonthDaysTextColor } : !d.currentMonth ? { color: "#99a1af " } : {},
|
|
349
|
+
...daysBgColor && d.currentMonth ? { backgroundColor: daysBgColor } : {},
|
|
350
|
+
...daysTextColor && d.currentMonth ? { color: daysTextColor } : {},
|
|
351
|
+
...holidayTextColor && isHolidayDay && d.currentMonth ? { color: holidayTextColor } : {},
|
|
352
|
+
...isHolidayDay && holidaysBgColor && d.currentMonth ? { backgroundColor: holidaysBgColor } : {},
|
|
353
|
+
...d.year === todayYear && d.month === todayMonth && d.date === todayDate && d.currentMonth && currentDayBgColor ? { backgroundColor: currentDayBgColor } : {},
|
|
354
|
+
...d.year === todayYear && d.month === todayMonth && d.date === todayDate && d.currentMonth && currentDaytextColor ? { backgroundColor: currentDaytextColor } : {},
|
|
355
|
+
...borderColor ? { borderColor } : { borderColor: "#d9d7e0" }
|
|
356
|
+
},
|
|
357
|
+
className: `
|
|
358
|
+
border-r-0 border-t-0 border p-[5px]
|
|
359
|
+
flex justify-center items-center 390:text-14 390:p-[10px] 700:p-[15px]
|
|
360
|
+
relative text-12 group
|
|
361
|
+
${d.currentMonth ? "" : ""}
|
|
362
|
+
${d.year === todayYear && d.month === todayMonth && d.date === todayDate && d.currentMonth ? "bg-blue-500 text-white" : ""}
|
|
363
|
+
${isHolidayDay && d.currentMonth ? "text-red-500" : ""}
|
|
364
|
+
`,
|
|
365
|
+
children: [
|
|
366
|
+
d.date,
|
|
367
|
+
showEvents && d.currentMonth && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
368
|
+
"div",
|
|
369
|
+
{
|
|
370
|
+
className: `
|
|
371
|
+
absolute hidden group-hover:block
|
|
372
|
+
bg-white border z-50 border-gray-300 overflow-hidden rounded shadow-lg
|
|
373
|
+
top-full mt-2 w-[220px] text-sm text-right ${tooltipPosition}
|
|
374
|
+
`,
|
|
375
|
+
children: (() => {
|
|
376
|
+
const [gy2, gm2, gd2] = jalaliToGregorian(
|
|
377
|
+
d.year,
|
|
378
|
+
d.month,
|
|
379
|
+
d.date
|
|
380
|
+
);
|
|
381
|
+
const gregorianMonthName = gregorianMonthsFa[gm2];
|
|
382
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
383
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
384
|
+
"div",
|
|
385
|
+
{
|
|
386
|
+
className: `
|
|
387
|
+
flex p-2 justify-between mb-2 pb-2
|
|
388
|
+
bg-linear-to-r from-[#00A1EE] to-[#1349E6] font-semibold text-white
|
|
389
|
+
${isHolidayDay ? "from-red-500 via-red-600 to-rose-700" : ""}
|
|
390
|
+
`,
|
|
391
|
+
children: [
|
|
392
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: weekDays[i % 7] }),
|
|
393
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
394
|
+
d.year,
|
|
395
|
+
"/",
|
|
396
|
+
d.month,
|
|
397
|
+
"/",
|
|
398
|
+
d.date
|
|
399
|
+
] })
|
|
400
|
+
]
|
|
401
|
+
}
|
|
402
|
+
),
|
|
403
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "text-[11px] px-2 text-gray-700 mb-2 pb-2 border-b border-gray-200", children: [
|
|
404
|
+
gd2,
|
|
405
|
+
" ",
|
|
406
|
+
gregorianMonthName,
|
|
407
|
+
" ",
|
|
408
|
+
gy2
|
|
409
|
+
] }),
|
|
410
|
+
events.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "p-2 pt-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "list-disc pr-4 flex flex-col", children: events.map((event, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
411
|
+
"li",
|
|
412
|
+
{
|
|
413
|
+
className: `text-10 ${isHolidayDay ? "text-red-600" : "text-[#23242e]"}`,
|
|
414
|
+
children: event.event
|
|
415
|
+
},
|
|
416
|
+
index
|
|
417
|
+
)) }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-10 p-2 pt-0 text-[#23242e]", children: "\u0631\u0648\u06CC\u062F\u0627\u062F\u06CC \u0648\u062C\u0648\u062F \u0646\u062F\u0627\u0631\u062F" })
|
|
418
|
+
] });
|
|
419
|
+
})()
|
|
420
|
+
}
|
|
421
|
+
)
|
|
422
|
+
]
|
|
423
|
+
},
|
|
424
|
+
i
|
|
425
|
+
);
|
|
426
|
+
})
|
|
427
|
+
}
|
|
428
|
+
)
|
|
429
|
+
] });
|
|
430
|
+
}
|
|
431
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
432
|
+
0 && (module.exports = {
|
|
433
|
+
Calendar
|
|
434
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
// src/Calendar.tsx
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
function gregorianToJalali(gy, gm, gd) {
|
|
5
|
+
const g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
|
6
|
+
const gy2 = gm > 2 ? gy + 1 : gy;
|
|
7
|
+
let days = 355666 + 365 * gy + Math.floor((gy2 + 3) / 4) - Math.floor((gy2 + 99) / 100) + Math.floor((gy2 + 399) / 400) + gd + g_d_m[gm - 1];
|
|
8
|
+
let jy = -1595 + 33 * Math.floor(days / 12053);
|
|
9
|
+
days %= 12053;
|
|
10
|
+
jy += 4 * Math.floor(days / 1461);
|
|
11
|
+
days %= 1461;
|
|
12
|
+
if (days > 365) {
|
|
13
|
+
jy += Math.floor((days - 1) / 365);
|
|
14
|
+
days = (days - 1) % 365;
|
|
15
|
+
}
|
|
16
|
+
let jm, jd;
|
|
17
|
+
if (days < 186) {
|
|
18
|
+
jm = 1 + Math.floor(days / 31);
|
|
19
|
+
jd = 1 + days % 31;
|
|
20
|
+
} else {
|
|
21
|
+
jm = 7 + Math.floor((days - 186) / 30);
|
|
22
|
+
jd = 1 + (days - 186) % 30;
|
|
23
|
+
}
|
|
24
|
+
return [jy, jm, jd];
|
|
25
|
+
}
|
|
26
|
+
function jalaliToGregorian(jy, jm, jd) {
|
|
27
|
+
jy += 1595;
|
|
28
|
+
let days = -355668 + 365 * jy + Math.floor(jy / 33) * 8 + Math.floor((jy % 33 + 3) / 4) + jd + (jm < 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186);
|
|
29
|
+
let gy = 400 * Math.floor(days / 146097);
|
|
30
|
+
days %= 146097;
|
|
31
|
+
if (days > 36524) {
|
|
32
|
+
gy += 100 * Math.floor(--days / 36524);
|
|
33
|
+
days %= 36524;
|
|
34
|
+
if (days >= 365) days++;
|
|
35
|
+
}
|
|
36
|
+
gy += 4 * Math.floor(days / 1461);
|
|
37
|
+
days %= 1461;
|
|
38
|
+
if (days > 365) {
|
|
39
|
+
gy += Math.floor((days - 1) / 365);
|
|
40
|
+
days = (days - 1) % 365;
|
|
41
|
+
}
|
|
42
|
+
let gd = days + 1;
|
|
43
|
+
const sal_a = [
|
|
44
|
+
0,
|
|
45
|
+
31,
|
|
46
|
+
gy % 4 === 0 && gy % 100 !== 0 || gy % 400 === 0 ? 29 : 28,
|
|
47
|
+
31,
|
|
48
|
+
30,
|
|
49
|
+
31,
|
|
50
|
+
30,
|
|
51
|
+
31,
|
|
52
|
+
31,
|
|
53
|
+
30,
|
|
54
|
+
31,
|
|
55
|
+
30,
|
|
56
|
+
31
|
|
57
|
+
];
|
|
58
|
+
let gm;
|
|
59
|
+
for (gm = 0; gm < 13 && gd > sal_a[gm]; gm++) gd -= sal_a[gm];
|
|
60
|
+
return [gy, gm, gd];
|
|
61
|
+
}
|
|
62
|
+
function isLeapJalaliYear(jy) {
|
|
63
|
+
const fm = jy % 33;
|
|
64
|
+
return fm === 1 || fm === 5 || fm === 9 || fm === 13 || fm === 17 || fm === 22 || fm === 26 || fm === 30;
|
|
65
|
+
}
|
|
66
|
+
function jalaliMonthLength(jy, jm) {
|
|
67
|
+
if (jm <= 6) return 31;
|
|
68
|
+
if (jm <= 11) return 30;
|
|
69
|
+
return isLeapJalaliYear(jy) ? 30 : 29;
|
|
70
|
+
}
|
|
71
|
+
function Calendar({
|
|
72
|
+
setCurrentDate,
|
|
73
|
+
headerColor,
|
|
74
|
+
showEvents,
|
|
75
|
+
goNext,
|
|
76
|
+
goPrev,
|
|
77
|
+
headerTextColor,
|
|
78
|
+
setNext,
|
|
79
|
+
daysBgColor,
|
|
80
|
+
setPrev,
|
|
81
|
+
holidaysBgColor,
|
|
82
|
+
daysTextColor,
|
|
83
|
+
holidayTextColor,
|
|
84
|
+
currentDayBgColor,
|
|
85
|
+
currentDaytextColor,
|
|
86
|
+
borderColor,
|
|
87
|
+
outsideMonthDaysBg,
|
|
88
|
+
outsideMonthDaysTextColor
|
|
89
|
+
}) {
|
|
90
|
+
const gregorianMonthsFa = [
|
|
91
|
+
"",
|
|
92
|
+
"\u0698\u0627\u0646\u0648\u06CC\u0647",
|
|
93
|
+
"\u0641\u0648\u0631\u06CC\u0647",
|
|
94
|
+
"\u0645\u0627\u0631\u0633",
|
|
95
|
+
"\u0622\u0648\u0631\u06CC\u0644",
|
|
96
|
+
"\u0645\u0647",
|
|
97
|
+
"\u0698\u0648\u0626\u0646",
|
|
98
|
+
"\u0698\u0648\u0626\u06CC\u0647",
|
|
99
|
+
"\u0627\u0648\u062A",
|
|
100
|
+
"\u0633\u067E\u062A\u0627\u0645\u0628\u0631",
|
|
101
|
+
"\u0627\u06A9\u062A\u0628\u0631",
|
|
102
|
+
"\u0646\u0648\u0627\u0645\u0628\u0631",
|
|
103
|
+
"\u062F\u0633\u0627\u0645\u0628\u0631"
|
|
104
|
+
];
|
|
105
|
+
const now = /* @__PURE__ */ new Date();
|
|
106
|
+
const tehranOffset = 3.5;
|
|
107
|
+
const utc = now.getTime() + now.getTimezoneOffset() * 6e4;
|
|
108
|
+
const tehranDate = new Date(utc + 36e5 * tehranOffset);
|
|
109
|
+
const [gy, gm, gd] = [
|
|
110
|
+
tehranDate.getFullYear(),
|
|
111
|
+
tehranDate.getMonth() + 1,
|
|
112
|
+
tehranDate.getDate()
|
|
113
|
+
];
|
|
114
|
+
const [todayYear, todayMonth, todayDate] = gregorianToJalali(gy, gm, gd);
|
|
115
|
+
const [currentYear, setCurrentYear] = useState(todayYear);
|
|
116
|
+
const [currentMonth, setCurrentMonth] = useState(todayMonth);
|
|
117
|
+
const [calendarData, setCalendarData] = useState([]);
|
|
118
|
+
const weekDays = [
|
|
119
|
+
"\u0634\u0646\u0628\u0647",
|
|
120
|
+
"\u06CC\u06A9\u0634\u0646\u0628\u0647",
|
|
121
|
+
"\u062F\u0648\u0634\u0646\u0628\u0647",
|
|
122
|
+
"\u0633\u0647\u200C\u0634\u0646\u0628\u0647",
|
|
123
|
+
"\u0686\u0647\u0627\u0631\u0634\u0646\u0628\u0647",
|
|
124
|
+
"\u067E\u0646\u062C\u200C\u0634\u0646\u0628\u0647",
|
|
125
|
+
"\u062C\u0645\u0639\u0647"
|
|
126
|
+
];
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
const fetchData = async () => {
|
|
129
|
+
try {
|
|
130
|
+
const response = await fetch(
|
|
131
|
+
`https://badesaba.ir/api/site/getDataCalendar/${currentMonth}/${currentYear}`
|
|
132
|
+
);
|
|
133
|
+
const data = await response.json();
|
|
134
|
+
setCalendarData(data);
|
|
135
|
+
} catch {
|
|
136
|
+
console.log("Error fetching calendar data:");
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
fetchData();
|
|
140
|
+
}, [currentMonth, currentYear]);
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
const weekDays2 = [
|
|
143
|
+
"\u0634\u0646\u0628\u0647",
|
|
144
|
+
"\u06CC\u06A9\u0634\u0646\u0628\u0647",
|
|
145
|
+
"\u062F\u0648\u0634\u0646\u0628\u0647",
|
|
146
|
+
"\u0633\u0647\u200C\u0634\u0646\u0628\u0647",
|
|
147
|
+
"\u0686\u0647\u0627\u0631\u0634\u0646\u0628\u0647",
|
|
148
|
+
"\u067E\u0646\u062C\u0634\u0646\u0628\u0647",
|
|
149
|
+
"\u062C\u0645\u0639\u0647"
|
|
150
|
+
];
|
|
151
|
+
const months = [
|
|
152
|
+
"\u0641\u0631\u0648\u0631\u062F\u06CC\u0646",
|
|
153
|
+
"\u0627\u0631\u062F\u06CC\u0628\u0647\u0634\u062A",
|
|
154
|
+
"\u062E\u0631\u062F\u0627\u062F",
|
|
155
|
+
"\u062A\u06CC\u0631",
|
|
156
|
+
"\u0645\u0631\u062F\u0627\u062F",
|
|
157
|
+
"\u0634\u0647\u0631\u06CC\u0648\u0631",
|
|
158
|
+
"\u0645\u0647\u0631",
|
|
159
|
+
"\u0622\u0628\u0627\u0646",
|
|
160
|
+
"\u0622\u0630\u0631",
|
|
161
|
+
"\u062F\u06CC",
|
|
162
|
+
"\u0628\u0647\u0645\u0646",
|
|
163
|
+
"\u0627\u0633\u0641\u0646\u062F"
|
|
164
|
+
];
|
|
165
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
166
|
+
const tehranOffset2 = 3.5;
|
|
167
|
+
const utc2 = now2.getTime() + now2.getTimezoneOffset() * 6e4;
|
|
168
|
+
const tehranDate2 = new Date(utc2 + 36e5 * tehranOffset2);
|
|
169
|
+
const [gy2, gm2, gd2] = [
|
|
170
|
+
tehranDate2.getFullYear(),
|
|
171
|
+
tehranDate2.getMonth() + 1,
|
|
172
|
+
tehranDate2.getDate()
|
|
173
|
+
];
|
|
174
|
+
const [jy, jm, jd] = gregorianToJalali(gy2, gm2, gd2);
|
|
175
|
+
const weekdayIndex = tehranDate2.getDay();
|
|
176
|
+
const weekdayName = weekDays2[(weekdayIndex + 1) % 7];
|
|
177
|
+
const monthName = months[jm - 1];
|
|
178
|
+
setCurrentDate(`${weekdayName}\u060C ${jd} ${monthName} ${jy}`);
|
|
179
|
+
}, [currentMonth, currentYear]);
|
|
180
|
+
const days = useMemo(() => {
|
|
181
|
+
const result = [];
|
|
182
|
+
const daysInMonth = jalaliMonthLength(currentYear, currentMonth);
|
|
183
|
+
const [startGy, startGm, startGd] = jalaliToGregorian(
|
|
184
|
+
currentYear,
|
|
185
|
+
currentMonth,
|
|
186
|
+
1
|
|
187
|
+
);
|
|
188
|
+
const startDate = new Date(startGy, startGm - 1, startGd);
|
|
189
|
+
const firstDayOfWeek = (startDate.getDay() + 1) % 7;
|
|
190
|
+
let prevMonth = currentMonth - 1;
|
|
191
|
+
let prevYear = currentYear;
|
|
192
|
+
if (prevMonth < 1) {
|
|
193
|
+
prevMonth = 12;
|
|
194
|
+
prevYear--;
|
|
195
|
+
}
|
|
196
|
+
const prevDaysInMonth = jalaliMonthLength(prevYear, prevMonth);
|
|
197
|
+
for (let i = 0; i < firstDayOfWeek; i++) {
|
|
198
|
+
const date = prevDaysInMonth - firstDayOfWeek + 1 + i;
|
|
199
|
+
result.push({
|
|
200
|
+
year: prevYear,
|
|
201
|
+
month: prevMonth,
|
|
202
|
+
date,
|
|
203
|
+
currentMonth: false
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
for (let i = 1; i <= daysInMonth; i++) {
|
|
207
|
+
result.push({
|
|
208
|
+
year: currentYear,
|
|
209
|
+
month: currentMonth,
|
|
210
|
+
date: i,
|
|
211
|
+
currentMonth: true
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
let nextMonth = currentMonth + 1;
|
|
215
|
+
let nextYear = currentYear;
|
|
216
|
+
if (nextMonth > 12) {
|
|
217
|
+
nextMonth = 1;
|
|
218
|
+
nextYear++;
|
|
219
|
+
}
|
|
220
|
+
let nextDate = 1;
|
|
221
|
+
while (result.length < 42) {
|
|
222
|
+
result.push({
|
|
223
|
+
year: nextYear,
|
|
224
|
+
month: nextMonth,
|
|
225
|
+
date: nextDate++,
|
|
226
|
+
currentMonth: false
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
}, [currentYear, currentMonth]);
|
|
231
|
+
const handlePrevMonth = useCallback(() => {
|
|
232
|
+
let newMonth = currentMonth - 1;
|
|
233
|
+
let newYear = currentYear;
|
|
234
|
+
if (newMonth < 1) {
|
|
235
|
+
newMonth = 12;
|
|
236
|
+
newYear--;
|
|
237
|
+
}
|
|
238
|
+
setCurrentMonth(newMonth);
|
|
239
|
+
setCurrentYear(newYear);
|
|
240
|
+
setPrev(false);
|
|
241
|
+
}, [currentMonth, currentYear]);
|
|
242
|
+
const handleNextMonth = useCallback(() => {
|
|
243
|
+
let newMonth = currentMonth + 1;
|
|
244
|
+
let newYear = currentYear;
|
|
245
|
+
if (newMonth > 12) {
|
|
246
|
+
newMonth = 1;
|
|
247
|
+
newYear++;
|
|
248
|
+
}
|
|
249
|
+
setCurrentMonth(newMonth);
|
|
250
|
+
setCurrentYear(newYear);
|
|
251
|
+
setNext(false);
|
|
252
|
+
}, [currentMonth, currentYear]);
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
if (goNext) {
|
|
255
|
+
handleNextMonth();
|
|
256
|
+
}
|
|
257
|
+
if (goPrev) {
|
|
258
|
+
handlePrevMonth();
|
|
259
|
+
}
|
|
260
|
+
}, [goNext, goPrev, handleNextMonth, handlePrevMonth]);
|
|
261
|
+
const calendarMap = useMemo(() => {
|
|
262
|
+
const map = /* @__PURE__ */ new Map();
|
|
263
|
+
calendarData.forEach((item) => {
|
|
264
|
+
map.set(item.date, item.events);
|
|
265
|
+
});
|
|
266
|
+
return map;
|
|
267
|
+
}, [calendarData]);
|
|
268
|
+
const getEventsForDate = (year, month, date) => {
|
|
269
|
+
var _a;
|
|
270
|
+
const [gy2, gm2, gd2] = jalaliToGregorian(year, month, date);
|
|
271
|
+
const key = `${gy2}-${String(gm2).padStart(2, "0")}-${String(gd2).padStart(
|
|
272
|
+
2,
|
|
273
|
+
"0"
|
|
274
|
+
)}`;
|
|
275
|
+
return (_a = calendarMap.get(key)) != null ? _a : [];
|
|
276
|
+
};
|
|
277
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-auto h-full flex flex-col", dir: "rtl", children: [
|
|
278
|
+
/* @__PURE__ */ jsx(
|
|
279
|
+
"div",
|
|
280
|
+
{
|
|
281
|
+
style: {
|
|
282
|
+
...headerTextColor ? { color: headerTextColor } : {},
|
|
283
|
+
...headerColor ? { backgroundColor: headerColor } : { backgroundColor: "#F1F4F9" },
|
|
284
|
+
...borderColor ? { borderColor } : { borderColor: "#d9d7e0" }
|
|
285
|
+
},
|
|
286
|
+
className: `grid-cols-7 hidden 350:grid text-center bg-[#F1F4F9] border border-b-0 font-semibold rounded-t-[10px] py-2.5`,
|
|
287
|
+
children: weekDays.map((day, index) => /* @__PURE__ */ jsx("p", { className: "350:text-[13px]", children: day }, index))
|
|
288
|
+
}
|
|
289
|
+
),
|
|
290
|
+
/* @__PURE__ */ jsx(
|
|
291
|
+
"div",
|
|
292
|
+
{
|
|
293
|
+
style: {
|
|
294
|
+
backgroundColor: headerColor ? headerColor : "#F1F4F9"
|
|
295
|
+
},
|
|
296
|
+
className: `grid-cols-7 grid 350:hidden text-center bg-[#F1F4F9] font-semibold rounded-t-[10px] py-2.5`,
|
|
297
|
+
children: weekDays.map((day, index) => /* @__PURE__ */ jsx("p", { className: "text-12 350:hidden", children: day[0] }, index))
|
|
298
|
+
}
|
|
299
|
+
),
|
|
300
|
+
/* @__PURE__ */ jsx(
|
|
301
|
+
"div",
|
|
302
|
+
{
|
|
303
|
+
style: borderColor ? { borderColor } : { borderColor: "#d9d7e0" },
|
|
304
|
+
className: "grid grid-cols-7 flex-1 border-l-0 border-b-0 border",
|
|
305
|
+
children: days.map((d, i) => {
|
|
306
|
+
const events = getEventsForDate(d.year, d.month, d.date);
|
|
307
|
+
const isHolidayDay = events.some((event) => event.holiday);
|
|
308
|
+
const columnIndex = i % 7;
|
|
309
|
+
let tooltipPosition = "";
|
|
310
|
+
if (columnIndex <= 1) {
|
|
311
|
+
tooltipPosition = "right-0";
|
|
312
|
+
} else if (columnIndex >= 5) {
|
|
313
|
+
tooltipPosition = "left-0";
|
|
314
|
+
} else {
|
|
315
|
+
tooltipPosition = "left-1/2 -translate-x-1/2";
|
|
316
|
+
}
|
|
317
|
+
return /* @__PURE__ */ jsxs(
|
|
318
|
+
"div",
|
|
319
|
+
{
|
|
320
|
+
style: {
|
|
321
|
+
...!d.currentMonth && outsideMonthDaysBg ? { backgroundColor: outsideMonthDaysBg } : {},
|
|
322
|
+
...!d.currentMonth && outsideMonthDaysTextColor ? { color: outsideMonthDaysTextColor } : !d.currentMonth ? { color: "#99a1af " } : {},
|
|
323
|
+
...daysBgColor && d.currentMonth ? { backgroundColor: daysBgColor } : {},
|
|
324
|
+
...daysTextColor && d.currentMonth ? { color: daysTextColor } : {},
|
|
325
|
+
...holidayTextColor && isHolidayDay && d.currentMonth ? { color: holidayTextColor } : {},
|
|
326
|
+
...isHolidayDay && holidaysBgColor && d.currentMonth ? { backgroundColor: holidaysBgColor } : {},
|
|
327
|
+
...d.year === todayYear && d.month === todayMonth && d.date === todayDate && d.currentMonth && currentDayBgColor ? { backgroundColor: currentDayBgColor } : {},
|
|
328
|
+
...d.year === todayYear && d.month === todayMonth && d.date === todayDate && d.currentMonth && currentDaytextColor ? { backgroundColor: currentDaytextColor } : {},
|
|
329
|
+
...borderColor ? { borderColor } : { borderColor: "#d9d7e0" }
|
|
330
|
+
},
|
|
331
|
+
className: `
|
|
332
|
+
border-r-0 border-t-0 border p-[5px]
|
|
333
|
+
flex justify-center items-center 390:text-14 390:p-[10px] 700:p-[15px]
|
|
334
|
+
relative text-12 group
|
|
335
|
+
${d.currentMonth ? "" : ""}
|
|
336
|
+
${d.year === todayYear && d.month === todayMonth && d.date === todayDate && d.currentMonth ? "bg-blue-500 text-white" : ""}
|
|
337
|
+
${isHolidayDay && d.currentMonth ? "text-red-500" : ""}
|
|
338
|
+
`,
|
|
339
|
+
children: [
|
|
340
|
+
d.date,
|
|
341
|
+
showEvents && d.currentMonth && /* @__PURE__ */ jsx(
|
|
342
|
+
"div",
|
|
343
|
+
{
|
|
344
|
+
className: `
|
|
345
|
+
absolute hidden group-hover:block
|
|
346
|
+
bg-white border z-50 border-gray-300 overflow-hidden rounded shadow-lg
|
|
347
|
+
top-full mt-2 w-[220px] text-sm text-right ${tooltipPosition}
|
|
348
|
+
`,
|
|
349
|
+
children: (() => {
|
|
350
|
+
const [gy2, gm2, gd2] = jalaliToGregorian(
|
|
351
|
+
d.year,
|
|
352
|
+
d.month,
|
|
353
|
+
d.date
|
|
354
|
+
);
|
|
355
|
+
const gregorianMonthName = gregorianMonthsFa[gm2];
|
|
356
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
357
|
+
/* @__PURE__ */ jsxs(
|
|
358
|
+
"div",
|
|
359
|
+
{
|
|
360
|
+
className: `
|
|
361
|
+
flex p-2 justify-between mb-2 pb-2
|
|
362
|
+
bg-linear-to-r from-[#00A1EE] to-[#1349E6] font-semibold text-white
|
|
363
|
+
${isHolidayDay ? "from-red-500 via-red-600 to-rose-700" : ""}
|
|
364
|
+
`,
|
|
365
|
+
children: [
|
|
366
|
+
/* @__PURE__ */ jsx("span", { children: weekDays[i % 7] }),
|
|
367
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
368
|
+
d.year,
|
|
369
|
+
"/",
|
|
370
|
+
d.month,
|
|
371
|
+
"/",
|
|
372
|
+
d.date
|
|
373
|
+
] })
|
|
374
|
+
]
|
|
375
|
+
}
|
|
376
|
+
),
|
|
377
|
+
/* @__PURE__ */ jsxs("div", { className: "text-[11px] px-2 text-gray-700 mb-2 pb-2 border-b border-gray-200", children: [
|
|
378
|
+
gd2,
|
|
379
|
+
" ",
|
|
380
|
+
gregorianMonthName,
|
|
381
|
+
" ",
|
|
382
|
+
gy2
|
|
383
|
+
] }),
|
|
384
|
+
events.length > 0 ? /* @__PURE__ */ jsx("div", { className: "p-2 pt-0", children: /* @__PURE__ */ jsx("ul", { className: "list-disc pr-4 flex flex-col", children: events.map((event, index) => /* @__PURE__ */ jsx(
|
|
385
|
+
"li",
|
|
386
|
+
{
|
|
387
|
+
className: `text-10 ${isHolidayDay ? "text-red-600" : "text-[#23242e]"}`,
|
|
388
|
+
children: event.event
|
|
389
|
+
},
|
|
390
|
+
index
|
|
391
|
+
)) }) }) : /* @__PURE__ */ jsx("p", { className: "text-10 p-2 pt-0 text-[#23242e]", children: "\u0631\u0648\u06CC\u062F\u0627\u062F\u06CC \u0648\u062C\u0648\u062F \u0646\u062F\u0627\u0631\u062F" })
|
|
392
|
+
] });
|
|
393
|
+
})()
|
|
394
|
+
}
|
|
395
|
+
)
|
|
396
|
+
]
|
|
397
|
+
},
|
|
398
|
+
i
|
|
399
|
+
);
|
|
400
|
+
})
|
|
401
|
+
}
|
|
402
|
+
)
|
|
403
|
+
] });
|
|
404
|
+
}
|
|
405
|
+
export {
|
|
406
|
+
Calendar
|
|
407
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-jalali-events-calendar",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Persian (Jalali) calendar for React",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"module": "dist/index.mjs",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": ">=17",
|
|
21
|
+
"react-dom": ">=17",
|
|
22
|
+
"tailwindcss": ">=3.0.0"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"tsup": "^8.5.1",
|
|
29
|
+
"typescript": "^5.9.3",
|
|
30
|
+
"@types/react": "^19.0.0",
|
|
31
|
+
"@types/react-dom": "^19.0.0"
|
|
32
|
+
}
|
|
33
|
+
}
|