@lichta/react 2.0.1 → 2.2.0
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 +52 -2
- package/dist/index.d.mts +35 -9
- package/dist/index.d.ts +35 -9
- package/dist/index.js +216 -111
- package/dist/index.mjs +216 -112
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# @lichta/react
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@lichta/react)
|
|
4
|
+
[](https://www.npmjs.com/package/@lichta/core)
|
|
5
|
+
|
|
6
|
+
🚀 **[Demo trực tiếp](https://lichta.zeneo.app/)**
|
|
4
7
|
|
|
5
8
|
Component `Calendar` (lịch tháng có ngày âm lịch) cho React, đóng gói trên [`@lichta/core`](https://www.npmjs.com/package/@lichta/core).
|
|
6
9
|
|
|
@@ -55,13 +58,58 @@ export default function App() {
|
|
|
55
58
|
| `year` | `number` | Năm hiện tại | Năm hiển thị ban đầu |
|
|
56
59
|
| `showLunar` | `boolean` | `true` | Hiện ngày âm lịch dưới ngày dương |
|
|
57
60
|
| `locale` | `'vi' \| 'en' \| 'ja' \| 'ko'` | `'vi'` | Ngôn ngữ nhãn thứ trong tuần |
|
|
61
|
+
| `firstDayOfWeek` | `0 \| 1` | `0` | Ngày bắt đầu tuần: `0` = Chủ Nhật, `1` = Thứ Hai |
|
|
62
|
+
| `showWeekNumber` | `boolean` | `false` | Hiện số tuần (ISO-8601) ở đầu mỗi hàng |
|
|
58
63
|
| `theme` | `'classic' \| 'glass'` | `'classic'` | Giao diện lịch |
|
|
59
64
|
| `className` | `string` | `''` | Class CSS bổ sung cho container |
|
|
60
65
|
| `onSelect` | `(date: Date, lunar: LunarDate) => void` | — | Callback khi chọn ngày |
|
|
66
|
+
| `onMonthChange` | `(month: number, year: number) => void` | — | Callback khi điều hướng tháng qua nút prev/next trong header |
|
|
61
67
|
| `renderDay` | `(cell: DayCellData) => React.ReactNode` | — | Custom render cho mỗi ô ngày |
|
|
62
68
|
| `children` | `React.ReactNode` | — | Nội dung footer tùy chỉnh |
|
|
63
69
|
|
|
64
|
-
>
|
|
70
|
+
> `month`/`year` là controlled prop — component tự đồng bộ lại khi cha đổi giá trị. Kết hợp với `onMonthChange` để đồng bộ 2 chiều với 1 nguồn điều hướng khác (ví dụ FullCalendar).
|
|
71
|
+
|
|
72
|
+
## DatePicker
|
|
73
|
+
|
|
74
|
+
Component `DatePicker` (input + popover chọn ngày, dựng trên `Calendar` ở trên).
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { DatePicker } from '@lichta/react';
|
|
78
|
+
import '@lichta/core/styles/calendar-base.css';
|
|
79
|
+
import '@lichta/core/styles/calendar-glass.css'; // theme "glass" (tùy chọn)
|
|
80
|
+
import '@lichta/core/styles/datepicker-base.css';
|
|
81
|
+
import '@lichta/core/styles/datepicker-glass.css'; // theme "glass" (tùy chọn)
|
|
82
|
+
|
|
83
|
+
export default function App() {
|
|
84
|
+
return (
|
|
85
|
+
<DatePicker
|
|
86
|
+
theme="glass"
|
|
87
|
+
locale="vi"
|
|
88
|
+
onSelect={(date, lunar) => console.log(date, lunar)}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> ⚠️ Cần import cả CSS của `Calendar` (dùng trong popover) lẫn `DatePicker` (input + popover chrome) — thiếu 1 trong 2 sẽ hiển thị thiếu style.
|
|
95
|
+
|
|
96
|
+
### Props
|
|
97
|
+
|
|
98
|
+
| Prop | Kiểu | Mặc định | Mô tả |
|
|
99
|
+
|---|---|---|---|
|
|
100
|
+
| `value` | `Date \| null` | `null` | Ngày đang chọn (uncontrolled — component tự quản lý sau lần mount đầu) |
|
|
101
|
+
| `onSelect` | `(date: Date, lunar: LunarDate) => void` | — | Callback khi chọn ngày trong popover |
|
|
102
|
+
| `onMonthChange` | `(month: number, year: number) => void` | — | Callback khi điều hướng tháng qua nút prev/next trong popover |
|
|
103
|
+
| `placeholder` | `string` | `'Chọn ngày'` | Placeholder khi chưa chọn ngày |
|
|
104
|
+
| `locale` | `'vi' \| 'en' \| 'ja' \| 'ko'` | `'vi'` | Ngôn ngữ hiển thị trong popover |
|
|
105
|
+
| `firstDayOfWeek` | `0 \| 1` | `0` | Ngày bắt đầu tuần: `0` = Chủ Nhật, `1` = Thứ Hai |
|
|
106
|
+
| `theme` | `'classic' \| 'glass'` | `'classic'` | Giao diện input + popover |
|
|
107
|
+
| `showLunar` | `boolean` | `true` | Hiện ngày âm lịch trong popover |
|
|
108
|
+
| `format` | `(date: Date) => string` | `dd/MM/yyyy` | Hàm format ngày hiển thị trên input |
|
|
109
|
+
| `disabled` | `boolean` | `false` | Vô hiệu hoá input |
|
|
110
|
+
| `className` | `string` | `''` | Class CSS bổ sung cho container |
|
|
111
|
+
|
|
112
|
+
Popover tự đóng khi: chọn xong 1 ngày, click ra ngoài, hoặc nhấn phím `Escape`.
|
|
65
113
|
|
|
66
114
|
## Theming
|
|
67
115
|
|
|
@@ -78,6 +126,8 @@ export default function App() {
|
|
|
78
126
|
}
|
|
79
127
|
```
|
|
80
128
|
|
|
129
|
+
`DatePicker` dùng chung biến CSS `--lichta-*` ở trên — không cần custom class riêng.
|
|
130
|
+
|
|
81
131
|
## License
|
|
82
132
|
|
|
83
133
|
[MIT](./LICENSE)
|
package/dist/index.d.mts
CHANGED
|
@@ -1,24 +1,50 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { LunarDate, Locale } from '@lichta/core';
|
|
2
|
+
import { LunarDate, Locale, FirstDayOfWeek, CalendarDayCell } from '@lichta/core';
|
|
3
3
|
|
|
4
4
|
interface CalendarProps {
|
|
5
5
|
month?: number;
|
|
6
6
|
year?: number;
|
|
7
|
+
/** Ngày được chọn (controlled). Bỏ qua thì Calendar tự quản lý selection nội bộ (uncontrolled). */
|
|
8
|
+
selectedDate?: Date | null;
|
|
7
9
|
onSelect?: (date: Date, lunar: LunarDate) => void;
|
|
10
|
+
/** Callback khi user điều hướng tháng qua nút prev/next trong header */
|
|
11
|
+
onMonthChange?: (month: number, year: number) => void;
|
|
8
12
|
showLunar?: boolean;
|
|
9
13
|
locale?: Locale;
|
|
14
|
+
/** Ngày bắt đầu tuần: 0 = Chủ Nhật (mặc định), 1 = Thứ Hai */
|
|
15
|
+
firstDayOfWeek?: FirstDayOfWeek;
|
|
16
|
+
/** Hiển thị số tuần (ISO-8601) ở đầu mỗi hàng */
|
|
17
|
+
showWeekNumber?: boolean;
|
|
10
18
|
theme?: 'classic' | 'glass';
|
|
11
19
|
className?: string;
|
|
12
20
|
children?: React.ReactNode;
|
|
13
21
|
renderDay?: (cell: DayCellData) => React.ReactNode;
|
|
14
22
|
}
|
|
15
|
-
|
|
16
|
-
solar: Date;
|
|
17
|
-
lunar: LunarDate;
|
|
18
|
-
isToday: boolean;
|
|
19
|
-
isSelected: boolean;
|
|
20
|
-
isCurrentMonth: boolean;
|
|
21
|
-
}
|
|
23
|
+
type DayCellData = CalendarDayCell;
|
|
22
24
|
declare const Calendar: React.FC<CalendarProps>;
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
interface DatePickerProps {
|
|
27
|
+
/** Ngày đang chọn (uncontrolled — component tự quản lý sau lần mount đầu) */
|
|
28
|
+
value?: Date | null;
|
|
29
|
+
/** Gọi khi người dùng chọn 1 ngày trong popover */
|
|
30
|
+
onSelect?: (date: Date, lunar: LunarDate) => void;
|
|
31
|
+
/** Callback khi user điều hướng tháng qua nút prev/next trong popover */
|
|
32
|
+
onMonthChange?: (month: number, year: number) => void;
|
|
33
|
+
/** Placeholder khi chưa chọn ngày nào */
|
|
34
|
+
placeholder?: string;
|
|
35
|
+
/** Ngôn ngữ hiển thị Can Chi/thứ trong tuần */
|
|
36
|
+
locale?: Locale;
|
|
37
|
+
/** Ngày bắt đầu tuần: 0 = Chủ Nhật (mặc định), 1 = Thứ Hai */
|
|
38
|
+
firstDayOfWeek?: FirstDayOfWeek;
|
|
39
|
+
/** Theme cho input + popover */
|
|
40
|
+
theme?: 'classic' | 'glass';
|
|
41
|
+
/** Hiển thị ngày âm lịch trong popover */
|
|
42
|
+
showLunar?: boolean;
|
|
43
|
+
/** Hàm format ngày hiển thị trên input, mặc định dd/MM/yyyy */
|
|
44
|
+
format?: (date: Date) => string;
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
className?: string;
|
|
47
|
+
}
|
|
48
|
+
declare const DatePicker: React.FC<DatePickerProps>;
|
|
49
|
+
|
|
50
|
+
export { Calendar, type CalendarProps, DatePicker, type DatePickerProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,24 +1,50 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { LunarDate, Locale } from '@lichta/core';
|
|
2
|
+
import { LunarDate, Locale, FirstDayOfWeek, CalendarDayCell } from '@lichta/core';
|
|
3
3
|
|
|
4
4
|
interface CalendarProps {
|
|
5
5
|
month?: number;
|
|
6
6
|
year?: number;
|
|
7
|
+
/** Ngày được chọn (controlled). Bỏ qua thì Calendar tự quản lý selection nội bộ (uncontrolled). */
|
|
8
|
+
selectedDate?: Date | null;
|
|
7
9
|
onSelect?: (date: Date, lunar: LunarDate) => void;
|
|
10
|
+
/** Callback khi user điều hướng tháng qua nút prev/next trong header */
|
|
11
|
+
onMonthChange?: (month: number, year: number) => void;
|
|
8
12
|
showLunar?: boolean;
|
|
9
13
|
locale?: Locale;
|
|
14
|
+
/** Ngày bắt đầu tuần: 0 = Chủ Nhật (mặc định), 1 = Thứ Hai */
|
|
15
|
+
firstDayOfWeek?: FirstDayOfWeek;
|
|
16
|
+
/** Hiển thị số tuần (ISO-8601) ở đầu mỗi hàng */
|
|
17
|
+
showWeekNumber?: boolean;
|
|
10
18
|
theme?: 'classic' | 'glass';
|
|
11
19
|
className?: string;
|
|
12
20
|
children?: React.ReactNode;
|
|
13
21
|
renderDay?: (cell: DayCellData) => React.ReactNode;
|
|
14
22
|
}
|
|
15
|
-
|
|
16
|
-
solar: Date;
|
|
17
|
-
lunar: LunarDate;
|
|
18
|
-
isToday: boolean;
|
|
19
|
-
isSelected: boolean;
|
|
20
|
-
isCurrentMonth: boolean;
|
|
21
|
-
}
|
|
23
|
+
type DayCellData = CalendarDayCell;
|
|
22
24
|
declare const Calendar: React.FC<CalendarProps>;
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
interface DatePickerProps {
|
|
27
|
+
/** Ngày đang chọn (uncontrolled — component tự quản lý sau lần mount đầu) */
|
|
28
|
+
value?: Date | null;
|
|
29
|
+
/** Gọi khi người dùng chọn 1 ngày trong popover */
|
|
30
|
+
onSelect?: (date: Date, lunar: LunarDate) => void;
|
|
31
|
+
/** Callback khi user điều hướng tháng qua nút prev/next trong popover */
|
|
32
|
+
onMonthChange?: (month: number, year: number) => void;
|
|
33
|
+
/** Placeholder khi chưa chọn ngày nào */
|
|
34
|
+
placeholder?: string;
|
|
35
|
+
/** Ngôn ngữ hiển thị Can Chi/thứ trong tuần */
|
|
36
|
+
locale?: Locale;
|
|
37
|
+
/** Ngày bắt đầu tuần: 0 = Chủ Nhật (mặc định), 1 = Thứ Hai */
|
|
38
|
+
firstDayOfWeek?: FirstDayOfWeek;
|
|
39
|
+
/** Theme cho input + popover */
|
|
40
|
+
theme?: 'classic' | 'glass';
|
|
41
|
+
/** Hiển thị ngày âm lịch trong popover */
|
|
42
|
+
showLunar?: boolean;
|
|
43
|
+
/** Hàm format ngày hiển thị trên input, mặc định dd/MM/yyyy */
|
|
44
|
+
format?: (date: Date) => string;
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
className?: string;
|
|
47
|
+
}
|
|
48
|
+
declare const DatePicker: React.FC<DatePickerProps>;
|
|
49
|
+
|
|
50
|
+
export { Calendar, type CalendarProps, DatePicker, type DatePickerProps };
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
Calendar: () => Calendar
|
|
33
|
+
Calendar: () => Calendar,
|
|
34
|
+
DatePicker: () => DatePicker
|
|
34
35
|
});
|
|
35
36
|
module.exports = __toCommonJS(index_exports);
|
|
36
37
|
|
|
@@ -38,32 +39,16 @@ module.exports = __toCommonJS(index_exports);
|
|
|
38
39
|
var import_react = __toESM(require("react"));
|
|
39
40
|
var import_core = require("@lichta/core");
|
|
40
41
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
41
|
-
var monthNames = [
|
|
42
|
-
"Th\xE1ng 1",
|
|
43
|
-
"Th\xE1ng 2",
|
|
44
|
-
"Th\xE1ng 3",
|
|
45
|
-
"Th\xE1ng 4",
|
|
46
|
-
"Th\xE1ng 5",
|
|
47
|
-
"Th\xE1ng 6",
|
|
48
|
-
"Th\xE1ng 7",
|
|
49
|
-
"Th\xE1ng 8",
|
|
50
|
-
"Th\xE1ng 9",
|
|
51
|
-
"Th\xE1ng 10",
|
|
52
|
-
"Th\xE1ng 11",
|
|
53
|
-
"Th\xE1ng 12"
|
|
54
|
-
];
|
|
55
|
-
var weekDayLabelsByLocale = {
|
|
56
|
-
vi: ["CN", "T2", "T3", "T4", "T5", "T6", "T7"],
|
|
57
|
-
en: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
58
|
-
ja: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
59
|
-
ko: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
|
|
60
|
-
};
|
|
61
42
|
var Calendar = ({
|
|
62
43
|
month = (/* @__PURE__ */ new Date()).getMonth() + 1,
|
|
63
44
|
year = (/* @__PURE__ */ new Date()).getFullYear(),
|
|
45
|
+
selectedDate: selectedDateProp,
|
|
64
46
|
onSelect,
|
|
47
|
+
onMonthChange,
|
|
65
48
|
showLunar = true,
|
|
66
49
|
locale = "vi",
|
|
50
|
+
firstDayOfWeek = 0,
|
|
51
|
+
showWeekNumber = false,
|
|
67
52
|
theme = "classic",
|
|
68
53
|
className = "",
|
|
69
54
|
children,
|
|
@@ -71,84 +56,39 @@ var Calendar = ({
|
|
|
71
56
|
}) => {
|
|
72
57
|
const [currentMonth, setCurrentMonth] = (0, import_react.useState)(month);
|
|
73
58
|
const [currentYear, setCurrentYear] = (0, import_react.useState)(year);
|
|
74
|
-
const [
|
|
59
|
+
const [internalSelectedDate, setInternalSelectedDate] = (0, import_react.useState)(null);
|
|
60
|
+
const selectedDate = selectedDateProp !== void 0 ? selectedDateProp : internalSelectedDate;
|
|
61
|
+
(0, import_react.useEffect)(() => {
|
|
62
|
+
setCurrentMonth(month);
|
|
63
|
+
}, [month]);
|
|
64
|
+
(0, import_react.useEffect)(() => {
|
|
65
|
+
setCurrentYear(year);
|
|
66
|
+
}, [year]);
|
|
75
67
|
const yearInfo = (0, import_react.useMemo)(() => (0, import_core.getYearDetails)(currentYear), [currentYear]);
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const daysInMonth = lastDay.getDate();
|
|
83
|
-
const today = /* @__PURE__ */ new Date();
|
|
84
|
-
const todayStr = `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`;
|
|
85
|
-
let selStr = "";
|
|
86
|
-
if (selectedDate) {
|
|
87
|
-
selStr = `${selectedDate.getFullYear()}-${selectedDate.getMonth() + 1}-${selectedDate.getDate()}`;
|
|
88
|
-
}
|
|
89
|
-
const prevMonthLastDay = new Date(currentYear, currentMonth - 1, 0).getDate();
|
|
90
|
-
for (let i = startingDayOfWeek - 1; i >= 0; i--) {
|
|
91
|
-
const d = prevMonthLastDay - i;
|
|
92
|
-
const m = currentMonth - 1 < 1 ? 12 : currentMonth - 1;
|
|
93
|
-
const y = currentMonth - 1 < 1 ? currentYear - 1 : currentYear;
|
|
94
|
-
const solar = new Date(y, m - 1, d);
|
|
95
|
-
const lunar = import_core.LichTa.toLunar(d, m, y);
|
|
96
|
-
const dateStr = `${y}-${m}-${d}`;
|
|
97
|
-
grid.push({
|
|
98
|
-
solar,
|
|
99
|
-
lunar,
|
|
100
|
-
isToday: dateStr === todayStr,
|
|
101
|
-
isSelected: dateStr === selStr,
|
|
102
|
-
isCurrentMonth: false
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
for (let d = 1; d <= daysInMonth; d++) {
|
|
106
|
-
const solar = new Date(currentYear, currentMonth - 1, d);
|
|
107
|
-
const lunar = import_core.LichTa.toLunar(d, currentMonth, currentYear);
|
|
108
|
-
const dateStr = `${currentYear}-${currentMonth}-${d}`;
|
|
109
|
-
grid.push({
|
|
110
|
-
solar,
|
|
111
|
-
lunar,
|
|
112
|
-
isToday: dateStr === todayStr,
|
|
113
|
-
isSelected: dateStr === selStr,
|
|
114
|
-
isCurrentMonth: true
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
const remaining = 42 - grid.length;
|
|
118
|
-
for (let d = 1; d <= remaining; d++) {
|
|
119
|
-
const m = currentMonth + 1 > 12 ? 1 : currentMonth + 1;
|
|
120
|
-
const y = currentMonth + 1 > 12 ? currentYear + 1 : currentYear;
|
|
121
|
-
const solar = new Date(y, m - 1, d);
|
|
122
|
-
const lunar = import_core.LichTa.toLunar(d, m, y);
|
|
123
|
-
const dateStr = `${y}-${m}-${d}`;
|
|
124
|
-
grid.push({
|
|
125
|
-
solar,
|
|
126
|
-
lunar,
|
|
127
|
-
isToday: dateStr === todayStr,
|
|
128
|
-
isSelected: dateStr === selStr,
|
|
129
|
-
isCurrentMonth: false
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
return grid;
|
|
133
|
-
}, [currentMonth, currentYear, selectedDate]);
|
|
68
|
+
const { solarMonthNames: monthNames } = (0, import_core.t)(locale);
|
|
69
|
+
const weekDayLabels = (0, import_react.useMemo)(() => (0, import_core.getWeekDayLabels)(locale, firstDayOfWeek), [locale, firstDayOfWeek]);
|
|
70
|
+
const calendarGrid = (0, import_react.useMemo)(
|
|
71
|
+
() => (0, import_core.getCalendarGrid)(currentMonth, currentYear, selectedDate, firstDayOfWeek),
|
|
72
|
+
[currentMonth, currentYear, selectedDate, firstDayOfWeek]
|
|
73
|
+
);
|
|
134
74
|
const prevMonth = () => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
75
|
+
const newMonth = currentMonth === 1 ? 12 : currentMonth - 1;
|
|
76
|
+
const newYear = currentMonth === 1 ? currentYear - 1 : currentYear;
|
|
77
|
+
setCurrentMonth(newMonth);
|
|
78
|
+
setCurrentYear(newYear);
|
|
79
|
+
onMonthChange?.(newMonth, newYear);
|
|
141
80
|
};
|
|
142
81
|
const nextMonth = () => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
82
|
+
const newMonth = currentMonth === 12 ? 1 : currentMonth + 1;
|
|
83
|
+
const newYear = currentMonth === 12 ? currentYear + 1 : currentYear;
|
|
84
|
+
setCurrentMonth(newMonth);
|
|
85
|
+
setCurrentYear(newYear);
|
|
86
|
+
onMonthChange?.(newMonth, newYear);
|
|
149
87
|
};
|
|
150
88
|
const handleDayClick = (cell) => {
|
|
151
|
-
|
|
89
|
+
if (selectedDateProp === void 0) {
|
|
90
|
+
setInternalSelectedDate(cell.solar);
|
|
91
|
+
}
|
|
152
92
|
if (onSelect) {
|
|
153
93
|
onSelect(cell.solar, cell.lunar);
|
|
154
94
|
}
|
|
@@ -159,7 +99,7 @@ var Calendar = ({
|
|
|
159
99
|
handleDayClick(cell);
|
|
160
100
|
}
|
|
161
101
|
};
|
|
162
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `lich-ta-calendar lichta-theme-${theme} ${className}`, children: [
|
|
102
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `lich-ta-calendar lichta-theme-${theme} ${showWeekNumber ? "lich-ta-calendar--with-week-number" : ""} ${className}`, children: [
|
|
163
103
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lich-ta-calendar-header", children: [
|
|
164
104
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lich-ta-calendar-nav", onClick: prevMonth, "aria-label": "Th\xE1ng tr\u01B0\u1EDBc", children: "\u25C0" }),
|
|
165
105
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lich-ta-calendar-title", children: [
|
|
@@ -176,35 +116,200 @@ var Calendar = ({
|
|
|
176
116
|
] }),
|
|
177
117
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lich-ta-calendar-nav", onClick: nextMonth, "aria-label": "Th\xE1ng sau", children: "\u25B6" })
|
|
178
118
|
] }),
|
|
179
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
119
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lich-ta-calendar-weekdays", children: [
|
|
120
|
+
showWeekNumber && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lich-ta-calendar-week-number", "aria-hidden": "true" }),
|
|
121
|
+
weekDayLabels.map((label) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lich-ta-calendar-weekday", children: label }, label))
|
|
122
|
+
] }),
|
|
180
123
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lich-ta-calendar-grid", children: calendarGrid.map((cell, idx) => {
|
|
124
|
+
const weekNumberCell = showWeekNumber && idx % 7 === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lich-ta-calendar-week-number", children: cell.weekNumber }, `wn-${idx}`) : null;
|
|
181
125
|
if (renderDay) {
|
|
182
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
126
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
|
|
127
|
+
weekNumberCell,
|
|
128
|
+
renderDay(cell)
|
|
129
|
+
] }, idx);
|
|
183
130
|
}
|
|
184
131
|
let classes = "lich-ta-calendar-day";
|
|
185
132
|
if (cell.isToday) classes += " is-today";
|
|
186
133
|
if (cell.isSelected) classes += " is-selected";
|
|
187
134
|
if (!cell.isCurrentMonth) classes += " is-other-month";
|
|
188
135
|
if (cell.lunar.day === 1) classes += " is-first-lunar";
|
|
189
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
136
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
|
|
137
|
+
weekNumberCell,
|
|
138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
139
|
+
"button",
|
|
140
|
+
{
|
|
141
|
+
className: classes,
|
|
142
|
+
onClick: () => handleDayClick(cell),
|
|
143
|
+
onKeyDown: (e) => handleKeydown(e, cell),
|
|
144
|
+
tabIndex: cell.isCurrentMonth ? 0 : -1,
|
|
145
|
+
children: [
|
|
146
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "solar-day", children: cell.solar.getDate() }),
|
|
147
|
+
showLunar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "lunar-day", children: cell.lunar.day === 1 ? `${cell.lunar.day}/${cell.lunar.month}${cell.lunar.isLeap ? "*" : ""}` : cell.lunar.day })
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
] }, idx);
|
|
203
152
|
}) }),
|
|
204
153
|
children && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lich-ta-calendar-footer", children })
|
|
205
154
|
] });
|
|
206
155
|
};
|
|
156
|
+
|
|
157
|
+
// src/DatePicker.tsx
|
|
158
|
+
var import_react2 = require("react");
|
|
159
|
+
var import_core2 = require("@lichta/core");
|
|
160
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
161
|
+
function defaultFormat(date) {
|
|
162
|
+
const dd = String(date.getDate()).padStart(2, "0");
|
|
163
|
+
const mm = String(date.getMonth() + 1).padStart(2, "0");
|
|
164
|
+
return `${dd}/${mm}/${date.getFullYear()}`;
|
|
165
|
+
}
|
|
166
|
+
var DatePicker = ({
|
|
167
|
+
value = null,
|
|
168
|
+
onSelect,
|
|
169
|
+
onMonthChange,
|
|
170
|
+
placeholder = "Ch\u1ECDn ng\xE0y",
|
|
171
|
+
locale = "vi",
|
|
172
|
+
firstDayOfWeek = 0,
|
|
173
|
+
theme = "classic",
|
|
174
|
+
showLunar = true,
|
|
175
|
+
format = defaultFormat,
|
|
176
|
+
disabled = false,
|
|
177
|
+
className = ""
|
|
178
|
+
}) => {
|
|
179
|
+
const [selected, setSelected] = (0, import_react2.useState)(value);
|
|
180
|
+
const [isOpen, setIsOpen] = (0, import_react2.useState)(false);
|
|
181
|
+
const anchor = selected ?? /* @__PURE__ */ new Date();
|
|
182
|
+
const [viewMonth, setViewMonth] = (0, import_react2.useState)(anchor.getMonth() + 1);
|
|
183
|
+
const [viewYear, setViewYear] = (0, import_react2.useState)(anchor.getFullYear());
|
|
184
|
+
const rootRef = (0, import_react2.useRef)(null);
|
|
185
|
+
(0, import_react2.useEffect)(() => {
|
|
186
|
+
const next = value ?? null;
|
|
187
|
+
setSelected(next);
|
|
188
|
+
if (next) {
|
|
189
|
+
setViewMonth(next.getMonth() + 1);
|
|
190
|
+
setViewYear(next.getFullYear());
|
|
191
|
+
}
|
|
192
|
+
}, [value]);
|
|
193
|
+
(0, import_react2.useEffect)(() => {
|
|
194
|
+
if (!isOpen) return;
|
|
195
|
+
function handlePointerDown(event) {
|
|
196
|
+
if (rootRef.current && !rootRef.current.contains(event.target)) {
|
|
197
|
+
setIsOpen(false);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function handleKeydown(event) {
|
|
201
|
+
if (event.key === "Escape") setIsOpen(false);
|
|
202
|
+
}
|
|
203
|
+
document.addEventListener("mousedown", handlePointerDown);
|
|
204
|
+
document.addEventListener("keydown", handleKeydown);
|
|
205
|
+
return () => {
|
|
206
|
+
document.removeEventListener("mousedown", handlePointerDown);
|
|
207
|
+
document.removeEventListener("keydown", handleKeydown);
|
|
208
|
+
};
|
|
209
|
+
}, [isOpen]);
|
|
210
|
+
const yearInfo = (0, import_react2.useMemo)(() => (0, import_core2.getYearDetails)(viewYear), [viewYear]);
|
|
211
|
+
const { solarMonthNames: monthNames } = (0, import_core2.t)(locale);
|
|
212
|
+
const weekDayLabels = (0, import_react2.useMemo)(() => (0, import_core2.getWeekDayLabels)(locale, firstDayOfWeek), [locale, firstDayOfWeek]);
|
|
213
|
+
const grid = (0, import_react2.useMemo)(
|
|
214
|
+
() => (0, import_core2.getCalendarGrid)(viewMonth, viewYear, selected, firstDayOfWeek),
|
|
215
|
+
[viewMonth, viewYear, selected, firstDayOfWeek]
|
|
216
|
+
);
|
|
217
|
+
function prevMonth() {
|
|
218
|
+
const newMonth = viewMonth === 1 ? 12 : viewMonth - 1;
|
|
219
|
+
const newYear = viewMonth === 1 ? viewYear - 1 : viewYear;
|
|
220
|
+
setViewMonth(newMonth);
|
|
221
|
+
setViewYear(newYear);
|
|
222
|
+
onMonthChange?.(newMonth, newYear);
|
|
223
|
+
}
|
|
224
|
+
function nextMonth() {
|
|
225
|
+
const newMonth = viewMonth === 12 ? 1 : viewMonth + 1;
|
|
226
|
+
const newYear = viewMonth === 12 ? viewYear + 1 : viewYear;
|
|
227
|
+
setViewMonth(newMonth);
|
|
228
|
+
setViewYear(newYear);
|
|
229
|
+
onMonthChange?.(newMonth, newYear);
|
|
230
|
+
}
|
|
231
|
+
function handleDaySelect(cell) {
|
|
232
|
+
setSelected(cell.solar);
|
|
233
|
+
setIsOpen(false);
|
|
234
|
+
onSelect?.(cell.solar, cell.lunar);
|
|
235
|
+
}
|
|
236
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: rootRef, className: `lich-ta-datepicker lichta-theme-${theme} ${className}`, children: [
|
|
237
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
238
|
+
"input",
|
|
239
|
+
{
|
|
240
|
+
type: "text",
|
|
241
|
+
className: "lich-ta-datepicker-input",
|
|
242
|
+
readOnly: true,
|
|
243
|
+
disabled,
|
|
244
|
+
placeholder,
|
|
245
|
+
value: selected ? format(selected) : "",
|
|
246
|
+
"aria-haspopup": "dialog",
|
|
247
|
+
"aria-expanded": isOpen,
|
|
248
|
+
onClick: () => !disabled && setIsOpen((open) => !open)
|
|
249
|
+
}
|
|
250
|
+
),
|
|
251
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lich-ta-datepicker-popover", role: "dialog", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lich-ta-datepicker-calendar", children: [
|
|
252
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lich-ta-datepicker-calendar-header", children: [
|
|
253
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
254
|
+
"button",
|
|
255
|
+
{
|
|
256
|
+
type: "button",
|
|
257
|
+
className: "lich-ta-datepicker-calendar-nav",
|
|
258
|
+
onClick: prevMonth,
|
|
259
|
+
"aria-label": "Th\xE1ng tr\u01B0\u1EDBc",
|
|
260
|
+
children: "\u25C0"
|
|
261
|
+
}
|
|
262
|
+
),
|
|
263
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lich-ta-datepicker-calendar-title", children: [
|
|
264
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "lich-ta-datepicker-calendar-month-year", children: [
|
|
265
|
+
monthNames[viewMonth - 1],
|
|
266
|
+
", ",
|
|
267
|
+
viewYear
|
|
268
|
+
] }),
|
|
269
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "lich-ta-datepicker-calendar-canchi", children: [
|
|
270
|
+
yearInfo.can,
|
|
271
|
+
" ",
|
|
272
|
+
yearInfo.chi
|
|
273
|
+
] })
|
|
274
|
+
] }),
|
|
275
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
276
|
+
"button",
|
|
277
|
+
{
|
|
278
|
+
type: "button",
|
|
279
|
+
className: "lich-ta-datepicker-calendar-nav",
|
|
280
|
+
onClick: nextMonth,
|
|
281
|
+
"aria-label": "Th\xE1ng sau",
|
|
282
|
+
children: "\u25B6"
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
] }),
|
|
286
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lich-ta-datepicker-calendar-weekdays", children: weekDayLabels.map((label) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lich-ta-datepicker-calendar-weekday", children: label }, label)) }),
|
|
287
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lich-ta-datepicker-calendar-grid", children: grid.map((cell, idx) => {
|
|
288
|
+
let classes = "lich-ta-datepicker-calendar-day";
|
|
289
|
+
if (cell.isToday) classes += " is-today";
|
|
290
|
+
if (cell.isSelected) classes += " is-selected";
|
|
291
|
+
if (!cell.isCurrentMonth) classes += " is-other-month";
|
|
292
|
+
if (cell.lunar.day === 1) classes += " is-first-lunar";
|
|
293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
294
|
+
"button",
|
|
295
|
+
{
|
|
296
|
+
type: "button",
|
|
297
|
+
className: classes,
|
|
298
|
+
onClick: () => handleDaySelect(cell),
|
|
299
|
+
tabIndex: cell.isCurrentMonth ? 0 : -1,
|
|
300
|
+
children: [
|
|
301
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "solar-day", children: cell.solar.getDate() }),
|
|
302
|
+
showLunar && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "lunar-day", children: cell.lunar.day === 1 ? `${cell.lunar.day}/${cell.lunar.month}${cell.lunar.isLeap ? "*" : ""}` : cell.lunar.day })
|
|
303
|
+
]
|
|
304
|
+
},
|
|
305
|
+
idx
|
|
306
|
+
);
|
|
307
|
+
}) })
|
|
308
|
+
] }) })
|
|
309
|
+
] });
|
|
310
|
+
};
|
|
207
311
|
// Annotate the CommonJS export names for ESM import in node:
|
|
208
312
|
0 && (module.exports = {
|
|
209
|
-
Calendar
|
|
313
|
+
Calendar,
|
|
314
|
+
DatePicker
|
|
210
315
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
1
|
// src/Calendar.tsx
|
|
2
|
-
import React, { useState, useMemo } from "react";
|
|
3
|
-
import {
|
|
2
|
+
import React, { useState, useMemo, useEffect } from "react";
|
|
3
|
+
import { getYearDetails, getCalendarGrid, getWeekDayLabels, t } from "@lichta/core";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
-
var monthNames = [
|
|
6
|
-
"Th\xE1ng 1",
|
|
7
|
-
"Th\xE1ng 2",
|
|
8
|
-
"Th\xE1ng 3",
|
|
9
|
-
"Th\xE1ng 4",
|
|
10
|
-
"Th\xE1ng 5",
|
|
11
|
-
"Th\xE1ng 6",
|
|
12
|
-
"Th\xE1ng 7",
|
|
13
|
-
"Th\xE1ng 8",
|
|
14
|
-
"Th\xE1ng 9",
|
|
15
|
-
"Th\xE1ng 10",
|
|
16
|
-
"Th\xE1ng 11",
|
|
17
|
-
"Th\xE1ng 12"
|
|
18
|
-
];
|
|
19
|
-
var weekDayLabelsByLocale = {
|
|
20
|
-
vi: ["CN", "T2", "T3", "T4", "T5", "T6", "T7"],
|
|
21
|
-
en: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
22
|
-
ja: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
23
|
-
ko: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
|
|
24
|
-
};
|
|
25
5
|
var Calendar = ({
|
|
26
6
|
month = (/* @__PURE__ */ new Date()).getMonth() + 1,
|
|
27
7
|
year = (/* @__PURE__ */ new Date()).getFullYear(),
|
|
8
|
+
selectedDate: selectedDateProp,
|
|
28
9
|
onSelect,
|
|
10
|
+
onMonthChange,
|
|
29
11
|
showLunar = true,
|
|
30
12
|
locale = "vi",
|
|
13
|
+
firstDayOfWeek = 0,
|
|
14
|
+
showWeekNumber = false,
|
|
31
15
|
theme = "classic",
|
|
32
16
|
className = "",
|
|
33
17
|
children,
|
|
@@ -35,84 +19,39 @@ var Calendar = ({
|
|
|
35
19
|
}) => {
|
|
36
20
|
const [currentMonth, setCurrentMonth] = useState(month);
|
|
37
21
|
const [currentYear, setCurrentYear] = useState(year);
|
|
38
|
-
const [
|
|
22
|
+
const [internalSelectedDate, setInternalSelectedDate] = useState(null);
|
|
23
|
+
const selectedDate = selectedDateProp !== void 0 ? selectedDateProp : internalSelectedDate;
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
setCurrentMonth(month);
|
|
26
|
+
}, [month]);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
setCurrentYear(year);
|
|
29
|
+
}, [year]);
|
|
39
30
|
const yearInfo = useMemo(() => getYearDetails(currentYear), [currentYear]);
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const daysInMonth = lastDay.getDate();
|
|
47
|
-
const today = /* @__PURE__ */ new Date();
|
|
48
|
-
const todayStr = `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`;
|
|
49
|
-
let selStr = "";
|
|
50
|
-
if (selectedDate) {
|
|
51
|
-
selStr = `${selectedDate.getFullYear()}-${selectedDate.getMonth() + 1}-${selectedDate.getDate()}`;
|
|
52
|
-
}
|
|
53
|
-
const prevMonthLastDay = new Date(currentYear, currentMonth - 1, 0).getDate();
|
|
54
|
-
for (let i = startingDayOfWeek - 1; i >= 0; i--) {
|
|
55
|
-
const d = prevMonthLastDay - i;
|
|
56
|
-
const m = currentMonth - 1 < 1 ? 12 : currentMonth - 1;
|
|
57
|
-
const y = currentMonth - 1 < 1 ? currentYear - 1 : currentYear;
|
|
58
|
-
const solar = new Date(y, m - 1, d);
|
|
59
|
-
const lunar = LichTa.toLunar(d, m, y);
|
|
60
|
-
const dateStr = `${y}-${m}-${d}`;
|
|
61
|
-
grid.push({
|
|
62
|
-
solar,
|
|
63
|
-
lunar,
|
|
64
|
-
isToday: dateStr === todayStr,
|
|
65
|
-
isSelected: dateStr === selStr,
|
|
66
|
-
isCurrentMonth: false
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
for (let d = 1; d <= daysInMonth; d++) {
|
|
70
|
-
const solar = new Date(currentYear, currentMonth - 1, d);
|
|
71
|
-
const lunar = LichTa.toLunar(d, currentMonth, currentYear);
|
|
72
|
-
const dateStr = `${currentYear}-${currentMonth}-${d}`;
|
|
73
|
-
grid.push({
|
|
74
|
-
solar,
|
|
75
|
-
lunar,
|
|
76
|
-
isToday: dateStr === todayStr,
|
|
77
|
-
isSelected: dateStr === selStr,
|
|
78
|
-
isCurrentMonth: true
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
const remaining = 42 - grid.length;
|
|
82
|
-
for (let d = 1; d <= remaining; d++) {
|
|
83
|
-
const m = currentMonth + 1 > 12 ? 1 : currentMonth + 1;
|
|
84
|
-
const y = currentMonth + 1 > 12 ? currentYear + 1 : currentYear;
|
|
85
|
-
const solar = new Date(y, m - 1, d);
|
|
86
|
-
const lunar = LichTa.toLunar(d, m, y);
|
|
87
|
-
const dateStr = `${y}-${m}-${d}`;
|
|
88
|
-
grid.push({
|
|
89
|
-
solar,
|
|
90
|
-
lunar,
|
|
91
|
-
isToday: dateStr === todayStr,
|
|
92
|
-
isSelected: dateStr === selStr,
|
|
93
|
-
isCurrentMonth: false
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
return grid;
|
|
97
|
-
}, [currentMonth, currentYear, selectedDate]);
|
|
31
|
+
const { solarMonthNames: monthNames } = t(locale);
|
|
32
|
+
const weekDayLabels = useMemo(() => getWeekDayLabels(locale, firstDayOfWeek), [locale, firstDayOfWeek]);
|
|
33
|
+
const calendarGrid = useMemo(
|
|
34
|
+
() => getCalendarGrid(currentMonth, currentYear, selectedDate, firstDayOfWeek),
|
|
35
|
+
[currentMonth, currentYear, selectedDate, firstDayOfWeek]
|
|
36
|
+
);
|
|
98
37
|
const prevMonth = () => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
38
|
+
const newMonth = currentMonth === 1 ? 12 : currentMonth - 1;
|
|
39
|
+
const newYear = currentMonth === 1 ? currentYear - 1 : currentYear;
|
|
40
|
+
setCurrentMonth(newMonth);
|
|
41
|
+
setCurrentYear(newYear);
|
|
42
|
+
onMonthChange?.(newMonth, newYear);
|
|
105
43
|
};
|
|
106
44
|
const nextMonth = () => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
45
|
+
const newMonth = currentMonth === 12 ? 1 : currentMonth + 1;
|
|
46
|
+
const newYear = currentMonth === 12 ? currentYear + 1 : currentYear;
|
|
47
|
+
setCurrentMonth(newMonth);
|
|
48
|
+
setCurrentYear(newYear);
|
|
49
|
+
onMonthChange?.(newMonth, newYear);
|
|
113
50
|
};
|
|
114
51
|
const handleDayClick = (cell) => {
|
|
115
|
-
|
|
52
|
+
if (selectedDateProp === void 0) {
|
|
53
|
+
setInternalSelectedDate(cell.solar);
|
|
54
|
+
}
|
|
116
55
|
if (onSelect) {
|
|
117
56
|
onSelect(cell.solar, cell.lunar);
|
|
118
57
|
}
|
|
@@ -123,7 +62,7 @@ var Calendar = ({
|
|
|
123
62
|
handleDayClick(cell);
|
|
124
63
|
}
|
|
125
64
|
};
|
|
126
|
-
return /* @__PURE__ */ jsxs("div", { className: `lich-ta-calendar lichta-theme-${theme} ${className}`, children: [
|
|
65
|
+
return /* @__PURE__ */ jsxs("div", { className: `lich-ta-calendar lichta-theme-${theme} ${showWeekNumber ? "lich-ta-calendar--with-week-number" : ""} ${className}`, children: [
|
|
127
66
|
/* @__PURE__ */ jsxs("div", { className: "lich-ta-calendar-header", children: [
|
|
128
67
|
/* @__PURE__ */ jsx("button", { className: "lich-ta-calendar-nav", onClick: prevMonth, "aria-label": "Th\xE1ng tr\u01B0\u1EDBc", children: "\u25C0" }),
|
|
129
68
|
/* @__PURE__ */ jsxs("div", { className: "lich-ta-calendar-title", children: [
|
|
@@ -140,34 +79,199 @@ var Calendar = ({
|
|
|
140
79
|
] }),
|
|
141
80
|
/* @__PURE__ */ jsx("button", { className: "lich-ta-calendar-nav", onClick: nextMonth, "aria-label": "Th\xE1ng sau", children: "\u25B6" })
|
|
142
81
|
] }),
|
|
143
|
-
/* @__PURE__ */
|
|
82
|
+
/* @__PURE__ */ jsxs("div", { className: "lich-ta-calendar-weekdays", children: [
|
|
83
|
+
showWeekNumber && /* @__PURE__ */ jsx("div", { className: "lich-ta-calendar-week-number", "aria-hidden": "true" }),
|
|
84
|
+
weekDayLabels.map((label) => /* @__PURE__ */ jsx("div", { className: "lich-ta-calendar-weekday", children: label }, label))
|
|
85
|
+
] }),
|
|
144
86
|
/* @__PURE__ */ jsx("div", { className: "lich-ta-calendar-grid", children: calendarGrid.map((cell, idx) => {
|
|
87
|
+
const weekNumberCell = showWeekNumber && idx % 7 === 0 ? /* @__PURE__ */ jsx("div", { className: "lich-ta-calendar-week-number", children: cell.weekNumber }, `wn-${idx}`) : null;
|
|
145
88
|
if (renderDay) {
|
|
146
|
-
return /* @__PURE__ */
|
|
89
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
90
|
+
weekNumberCell,
|
|
91
|
+
renderDay(cell)
|
|
92
|
+
] }, idx);
|
|
147
93
|
}
|
|
148
94
|
let classes = "lich-ta-calendar-day";
|
|
149
95
|
if (cell.isToday) classes += " is-today";
|
|
150
96
|
if (cell.isSelected) classes += " is-selected";
|
|
151
97
|
if (!cell.isCurrentMonth) classes += " is-other-month";
|
|
152
98
|
if (cell.lunar.day === 1) classes += " is-first-lunar";
|
|
153
|
-
return /* @__PURE__ */ jsxs(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
99
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
100
|
+
weekNumberCell,
|
|
101
|
+
/* @__PURE__ */ jsxs(
|
|
102
|
+
"button",
|
|
103
|
+
{
|
|
104
|
+
className: classes,
|
|
105
|
+
onClick: () => handleDayClick(cell),
|
|
106
|
+
onKeyDown: (e) => handleKeydown(e, cell),
|
|
107
|
+
tabIndex: cell.isCurrentMonth ? 0 : -1,
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ jsx("span", { className: "solar-day", children: cell.solar.getDate() }),
|
|
110
|
+
showLunar && /* @__PURE__ */ jsx("span", { className: "lunar-day", children: cell.lunar.day === 1 ? `${cell.lunar.day}/${cell.lunar.month}${cell.lunar.isLeap ? "*" : ""}` : cell.lunar.day })
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
] }, idx);
|
|
167
115
|
}) }),
|
|
168
116
|
children && /* @__PURE__ */ jsx("div", { className: "lich-ta-calendar-footer", children })
|
|
169
117
|
] });
|
|
170
118
|
};
|
|
119
|
+
|
|
120
|
+
// src/DatePicker.tsx
|
|
121
|
+
import { useState as useState2, useRef, useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
122
|
+
import { getYearDetails as getYearDetails2, getCalendarGrid as getCalendarGrid2, getWeekDayLabels as getWeekDayLabels2, t as t2 } from "@lichta/core";
|
|
123
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
124
|
+
function defaultFormat(date) {
|
|
125
|
+
const dd = String(date.getDate()).padStart(2, "0");
|
|
126
|
+
const mm = String(date.getMonth() + 1).padStart(2, "0");
|
|
127
|
+
return `${dd}/${mm}/${date.getFullYear()}`;
|
|
128
|
+
}
|
|
129
|
+
var DatePicker = ({
|
|
130
|
+
value = null,
|
|
131
|
+
onSelect,
|
|
132
|
+
onMonthChange,
|
|
133
|
+
placeholder = "Ch\u1ECDn ng\xE0y",
|
|
134
|
+
locale = "vi",
|
|
135
|
+
firstDayOfWeek = 0,
|
|
136
|
+
theme = "classic",
|
|
137
|
+
showLunar = true,
|
|
138
|
+
format = defaultFormat,
|
|
139
|
+
disabled = false,
|
|
140
|
+
className = ""
|
|
141
|
+
}) => {
|
|
142
|
+
const [selected, setSelected] = useState2(value);
|
|
143
|
+
const [isOpen, setIsOpen] = useState2(false);
|
|
144
|
+
const anchor = selected ?? /* @__PURE__ */ new Date();
|
|
145
|
+
const [viewMonth, setViewMonth] = useState2(anchor.getMonth() + 1);
|
|
146
|
+
const [viewYear, setViewYear] = useState2(anchor.getFullYear());
|
|
147
|
+
const rootRef = useRef(null);
|
|
148
|
+
useEffect2(() => {
|
|
149
|
+
const next = value ?? null;
|
|
150
|
+
setSelected(next);
|
|
151
|
+
if (next) {
|
|
152
|
+
setViewMonth(next.getMonth() + 1);
|
|
153
|
+
setViewYear(next.getFullYear());
|
|
154
|
+
}
|
|
155
|
+
}, [value]);
|
|
156
|
+
useEffect2(() => {
|
|
157
|
+
if (!isOpen) return;
|
|
158
|
+
function handlePointerDown(event) {
|
|
159
|
+
if (rootRef.current && !rootRef.current.contains(event.target)) {
|
|
160
|
+
setIsOpen(false);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function handleKeydown(event) {
|
|
164
|
+
if (event.key === "Escape") setIsOpen(false);
|
|
165
|
+
}
|
|
166
|
+
document.addEventListener("mousedown", handlePointerDown);
|
|
167
|
+
document.addEventListener("keydown", handleKeydown);
|
|
168
|
+
return () => {
|
|
169
|
+
document.removeEventListener("mousedown", handlePointerDown);
|
|
170
|
+
document.removeEventListener("keydown", handleKeydown);
|
|
171
|
+
};
|
|
172
|
+
}, [isOpen]);
|
|
173
|
+
const yearInfo = useMemo2(() => getYearDetails2(viewYear), [viewYear]);
|
|
174
|
+
const { solarMonthNames: monthNames } = t2(locale);
|
|
175
|
+
const weekDayLabels = useMemo2(() => getWeekDayLabels2(locale, firstDayOfWeek), [locale, firstDayOfWeek]);
|
|
176
|
+
const grid = useMemo2(
|
|
177
|
+
() => getCalendarGrid2(viewMonth, viewYear, selected, firstDayOfWeek),
|
|
178
|
+
[viewMonth, viewYear, selected, firstDayOfWeek]
|
|
179
|
+
);
|
|
180
|
+
function prevMonth() {
|
|
181
|
+
const newMonth = viewMonth === 1 ? 12 : viewMonth - 1;
|
|
182
|
+
const newYear = viewMonth === 1 ? viewYear - 1 : viewYear;
|
|
183
|
+
setViewMonth(newMonth);
|
|
184
|
+
setViewYear(newYear);
|
|
185
|
+
onMonthChange?.(newMonth, newYear);
|
|
186
|
+
}
|
|
187
|
+
function nextMonth() {
|
|
188
|
+
const newMonth = viewMonth === 12 ? 1 : viewMonth + 1;
|
|
189
|
+
const newYear = viewMonth === 12 ? viewYear + 1 : viewYear;
|
|
190
|
+
setViewMonth(newMonth);
|
|
191
|
+
setViewYear(newYear);
|
|
192
|
+
onMonthChange?.(newMonth, newYear);
|
|
193
|
+
}
|
|
194
|
+
function handleDaySelect(cell) {
|
|
195
|
+
setSelected(cell.solar);
|
|
196
|
+
setIsOpen(false);
|
|
197
|
+
onSelect?.(cell.solar, cell.lunar);
|
|
198
|
+
}
|
|
199
|
+
return /* @__PURE__ */ jsxs2("div", { ref: rootRef, className: `lich-ta-datepicker lichta-theme-${theme} ${className}`, children: [
|
|
200
|
+
/* @__PURE__ */ jsx2(
|
|
201
|
+
"input",
|
|
202
|
+
{
|
|
203
|
+
type: "text",
|
|
204
|
+
className: "lich-ta-datepicker-input",
|
|
205
|
+
readOnly: true,
|
|
206
|
+
disabled,
|
|
207
|
+
placeholder,
|
|
208
|
+
value: selected ? format(selected) : "",
|
|
209
|
+
"aria-haspopup": "dialog",
|
|
210
|
+
"aria-expanded": isOpen,
|
|
211
|
+
onClick: () => !disabled && setIsOpen((open) => !open)
|
|
212
|
+
}
|
|
213
|
+
),
|
|
214
|
+
isOpen && /* @__PURE__ */ jsx2("div", { className: "lich-ta-datepicker-popover", role: "dialog", children: /* @__PURE__ */ jsxs2("div", { className: "lich-ta-datepicker-calendar", children: [
|
|
215
|
+
/* @__PURE__ */ jsxs2("div", { className: "lich-ta-datepicker-calendar-header", children: [
|
|
216
|
+
/* @__PURE__ */ jsx2(
|
|
217
|
+
"button",
|
|
218
|
+
{
|
|
219
|
+
type: "button",
|
|
220
|
+
className: "lich-ta-datepicker-calendar-nav",
|
|
221
|
+
onClick: prevMonth,
|
|
222
|
+
"aria-label": "Th\xE1ng tr\u01B0\u1EDBc",
|
|
223
|
+
children: "\u25C0"
|
|
224
|
+
}
|
|
225
|
+
),
|
|
226
|
+
/* @__PURE__ */ jsxs2("div", { className: "lich-ta-datepicker-calendar-title", children: [
|
|
227
|
+
/* @__PURE__ */ jsxs2("span", { className: "lich-ta-datepicker-calendar-month-year", children: [
|
|
228
|
+
monthNames[viewMonth - 1],
|
|
229
|
+
", ",
|
|
230
|
+
viewYear
|
|
231
|
+
] }),
|
|
232
|
+
/* @__PURE__ */ jsxs2("span", { className: "lich-ta-datepicker-calendar-canchi", children: [
|
|
233
|
+
yearInfo.can,
|
|
234
|
+
" ",
|
|
235
|
+
yearInfo.chi
|
|
236
|
+
] })
|
|
237
|
+
] }),
|
|
238
|
+
/* @__PURE__ */ jsx2(
|
|
239
|
+
"button",
|
|
240
|
+
{
|
|
241
|
+
type: "button",
|
|
242
|
+
className: "lich-ta-datepicker-calendar-nav",
|
|
243
|
+
onClick: nextMonth,
|
|
244
|
+
"aria-label": "Th\xE1ng sau",
|
|
245
|
+
children: "\u25B6"
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
] }),
|
|
249
|
+
/* @__PURE__ */ jsx2("div", { className: "lich-ta-datepicker-calendar-weekdays", children: weekDayLabels.map((label) => /* @__PURE__ */ jsx2("div", { className: "lich-ta-datepicker-calendar-weekday", children: label }, label)) }),
|
|
250
|
+
/* @__PURE__ */ jsx2("div", { className: "lich-ta-datepicker-calendar-grid", children: grid.map((cell, idx) => {
|
|
251
|
+
let classes = "lich-ta-datepicker-calendar-day";
|
|
252
|
+
if (cell.isToday) classes += " is-today";
|
|
253
|
+
if (cell.isSelected) classes += " is-selected";
|
|
254
|
+
if (!cell.isCurrentMonth) classes += " is-other-month";
|
|
255
|
+
if (cell.lunar.day === 1) classes += " is-first-lunar";
|
|
256
|
+
return /* @__PURE__ */ jsxs2(
|
|
257
|
+
"button",
|
|
258
|
+
{
|
|
259
|
+
type: "button",
|
|
260
|
+
className: classes,
|
|
261
|
+
onClick: () => handleDaySelect(cell),
|
|
262
|
+
tabIndex: cell.isCurrentMonth ? 0 : -1,
|
|
263
|
+
children: [
|
|
264
|
+
/* @__PURE__ */ jsx2("span", { className: "solar-day", children: cell.solar.getDate() }),
|
|
265
|
+
showLunar && /* @__PURE__ */ jsx2("span", { className: "lunar-day", children: cell.lunar.day === 1 ? `${cell.lunar.day}/${cell.lunar.month}${cell.lunar.isLeap ? "*" : ""}` : cell.lunar.day })
|
|
266
|
+
]
|
|
267
|
+
},
|
|
268
|
+
idx
|
|
269
|
+
);
|
|
270
|
+
}) })
|
|
271
|
+
] }) })
|
|
272
|
+
] });
|
|
273
|
+
};
|
|
171
274
|
export {
|
|
172
|
-
Calendar
|
|
275
|
+
Calendar,
|
|
276
|
+
DatePicker
|
|
173
277
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lichta/react",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "React components for LichTa",
|
|
5
5
|
"author": "Zeforc Labs | Stridev",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"LICENSE"
|
|
13
13
|
],
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@lichta/core": "2.0
|
|
15
|
+
"@lichta/core": "2.2.0"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"react": ">=18.0.0",
|