@lichta/react 2.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # @lichta/react
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@lichta/react.svg)](https://www.npmjs.com/package/@lichta/react)
4
- [![npm version](https://img.shields.io/npm/v/@lichta/core.svg)](https://www.npmjs.com/package/@lichta/core)
3
+ [![@lichta/react](https://img.shields.io/npm/v/@lichta/react.svg?label=%40lichta%2Freact)](https://www.npmjs.com/package/@lichta/react)
4
+ [![@lichta/core](https://img.shields.io/npm/v/@lichta/core.svg?label=%40lichta%2Fcore)](https://www.npmjs.com/package/@lichta/core)
5
5
 
6
6
  🚀 **[Demo trực tiếp](https://lichta.zeneo.app/)**
7
7
 
@@ -58,13 +58,16 @@ export default function App() {
58
58
  | `year` | `number` | Năm hiện tại | Năm hiển thị ban đầu |
59
59
  | `showLunar` | `boolean` | `true` | Hiện ngày âm lịch dưới ngày dương |
60
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 |
61
63
  | `theme` | `'classic' \| 'glass'` | `'classic'` | Giao diện lịch |
62
64
  | `className` | `string` | `''` | Class CSS bổ sung cho container |
63
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 |
64
67
  | `renderDay` | `(cell: DayCellData) => React.ReactNode` | — | Custom render cho mỗi ô ngày |
65
68
  | `children` | `React.ReactNode` | — | Nội dung footer tùy chỉnh |
66
69
 
67
- > Lưu ý: `month`/`year` hiện chỉ dùng làm giá trị khởi tạo state nội bộ (uncontrolled) thay đổi prop sau khi mount chưa tự động cập nhật lại lịch đang hiển thị.
70
+ > `month`/`year` 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).
68
71
 
69
72
  ## DatePicker
70
73
 
@@ -96,8 +99,10 @@ export default function App() {
96
99
  |---|---|---|---|
97
100
  | `value` | `Date \| null` | `null` | Ngày đang chọn (uncontrolled — component tự quản lý sau lần mount đầu) |
98
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 |
99
103
  | `placeholder` | `string` | `'Chọn ngày'` | Placeholder khi chưa chọn ngày |
100
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 |
101
106
  | `theme` | `'classic' \| 'glass'` | `'classic'` | Giao diện input + popover |
102
107
  | `showLunar` | `boolean` | `true` | Hiện ngày âm lịch trong popover |
103
108
  | `format` | `(date: Date) => string` | `dd/MM/yyyy` | Hàm format ngày hiển thị trên input |
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { LunarDate, Locale, CalendarDayCell } from '@lichta/core';
2
+ import { LunarDate, Locale, FirstDayOfWeek, CalendarDayCell } from '@lichta/core';
3
3
 
4
4
  interface CalendarProps {
5
5
  month?: number;
@@ -7,8 +7,14 @@ interface CalendarProps {
7
7
  /** Ngày được chọn (controlled). Bỏ qua thì Calendar tự quản lý selection nội bộ (uncontrolled). */
8
8
  selectedDate?: Date | null;
9
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;
10
12
  showLunar?: boolean;
11
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;
12
18
  theme?: 'classic' | 'glass';
13
19
  className?: string;
14
20
  children?: React.ReactNode;
@@ -22,10 +28,14 @@ interface DatePickerProps {
22
28
  value?: Date | null;
23
29
  /** Gọi khi người dùng chọn 1 ngày trong popover */
24
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;
25
33
  /** Placeholder khi chưa chọn ngày nào */
26
34
  placeholder?: string;
27
35
  /** Ngôn ngữ hiển thị Can Chi/thứ trong tuần */
28
36
  locale?: Locale;
37
+ /** Ngày bắt đầu tuần: 0 = Chủ Nhật (mặc định), 1 = Thứ Hai */
38
+ firstDayOfWeek?: FirstDayOfWeek;
29
39
  /** Theme cho input + popover */
30
40
  theme?: 'classic' | 'glass';
31
41
  /** Hiển thị ngày âm lịch trong popover */
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { LunarDate, Locale, CalendarDayCell } from '@lichta/core';
2
+ import { LunarDate, Locale, FirstDayOfWeek, CalendarDayCell } from '@lichta/core';
3
3
 
4
4
  interface CalendarProps {
5
5
  month?: number;
@@ -7,8 +7,14 @@ interface CalendarProps {
7
7
  /** Ngày được chọn (controlled). Bỏ qua thì Calendar tự quản lý selection nội bộ (uncontrolled). */
8
8
  selectedDate?: Date | null;
9
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;
10
12
  showLunar?: boolean;
11
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;
12
18
  theme?: 'classic' | 'glass';
13
19
  className?: string;
14
20
  children?: React.ReactNode;
@@ -22,10 +28,14 @@ interface DatePickerProps {
22
28
  value?: Date | null;
23
29
  /** Gọi khi người dùng chọn 1 ngày trong popover */
24
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;
25
33
  /** Placeholder khi chưa chọn ngày nào */
26
34
  placeholder?: string;
27
35
  /** Ngôn ngữ hiển thị Can Chi/thứ trong tuần */
28
36
  locale?: Locale;
37
+ /** Ngày bắt đầu tuần: 0 = Chủ Nhật (mặc định), 1 = Thứ Hai */
38
+ firstDayOfWeek?: FirstDayOfWeek;
29
39
  /** Theme cho input + popover */
30
40
  theme?: 'classic' | 'glass';
31
41
  /** Hiển thị ngày âm lịch trong popover */
package/dist/index.js CHANGED
@@ -44,8 +44,11 @@ var Calendar = ({
44
44
  year = (/* @__PURE__ */ new Date()).getFullYear(),
45
45
  selectedDate: selectedDateProp,
46
46
  onSelect,
47
+ onMonthChange,
47
48
  showLunar = true,
48
49
  locale = "vi",
50
+ firstDayOfWeek = 0,
51
+ showWeekNumber = false,
49
52
  theme = "classic",
50
53
  className = "",
51
54
  children,
@@ -62,26 +65,25 @@ var Calendar = ({
62
65
  setCurrentYear(year);
63
66
  }, [year]);
64
67
  const yearInfo = (0, import_react.useMemo)(() => (0, import_core.getYearDetails)(currentYear), [currentYear]);
65
- const { weekDays: weekDayLabels, solarMonthNames: monthNames } = (0, import_core.t)(locale);
68
+ const { solarMonthNames: monthNames } = (0, import_core.t)(locale);
69
+ const weekDayLabels = (0, import_react.useMemo)(() => (0, import_core.getWeekDayLabels)(locale, firstDayOfWeek), [locale, firstDayOfWeek]);
66
70
  const calendarGrid = (0, import_react.useMemo)(
67
- () => (0, import_core.getCalendarGrid)(currentMonth, currentYear, selectedDate),
68
- [currentMonth, currentYear, selectedDate]
71
+ () => (0, import_core.getCalendarGrid)(currentMonth, currentYear, selectedDate, firstDayOfWeek),
72
+ [currentMonth, currentYear, selectedDate, firstDayOfWeek]
69
73
  );
70
74
  const prevMonth = () => {
71
- if (currentMonth === 1) {
72
- setCurrentMonth(12);
73
- setCurrentYear((y) => y - 1);
74
- } else {
75
- setCurrentMonth((m) => m - 1);
76
- }
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);
77
80
  };
78
81
  const nextMonth = () => {
79
- if (currentMonth === 12) {
80
- setCurrentMonth(1);
81
- setCurrentYear((y) => y + 1);
82
- } else {
83
- setCurrentMonth((m) => m + 1);
84
- }
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);
85
87
  };
86
88
  const handleDayClick = (cell) => {
87
89
  if (selectedDateProp === void 0) {
@@ -97,7 +99,7 @@ var Calendar = ({
97
99
  handleDayClick(cell);
98
100
  }
99
101
  };
100
- 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: [
101
103
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lich-ta-calendar-header", children: [
102
104
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lich-ta-calendar-nav", onClick: prevMonth, "aria-label": "Th\xE1ng tr\u01B0\u1EDBc", children: "\u25C0" }),
103
105
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lich-ta-calendar-title", children: [
@@ -114,30 +116,39 @@ var Calendar = ({
114
116
  ] }),
115
117
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lich-ta-calendar-nav", onClick: nextMonth, "aria-label": "Th\xE1ng sau", children: "\u25B6" })
116
118
  ] }),
117
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lich-ta-calendar-weekdays", children: weekDayLabels.map((label) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lich-ta-calendar-weekday", children: label }, label)) }),
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
+ ] }),
118
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;
119
125
  if (renderDay) {
120
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.default.Fragment, { children: renderDay(cell) }, idx);
126
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
127
+ weekNumberCell,
128
+ renderDay(cell)
129
+ ] }, idx);
121
130
  }
122
131
  let classes = "lich-ta-calendar-day";
123
132
  if (cell.isToday) classes += " is-today";
124
133
  if (cell.isSelected) classes += " is-selected";
125
134
  if (!cell.isCurrentMonth) classes += " is-other-month";
126
135
  if (cell.lunar.day === 1) classes += " is-first-lunar";
127
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
128
- "button",
129
- {
130
- className: classes,
131
- onClick: () => handleDayClick(cell),
132
- onKeyDown: (e) => handleKeydown(e, cell),
133
- tabIndex: cell.isCurrentMonth ? 0 : -1,
134
- children: [
135
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "solar-day", children: cell.solar.getDate() }),
136
- 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 })
137
- ]
138
- },
139
- idx
140
- );
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);
141
152
  }) }),
142
153
  children && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lich-ta-calendar-footer", children })
143
154
  ] });
@@ -155,8 +166,10 @@ function defaultFormat(date) {
155
166
  var DatePicker = ({
156
167
  value = null,
157
168
  onSelect,
169
+ onMonthChange,
158
170
  placeholder = "Ch\u1ECDn ng\xE0y",
159
171
  locale = "vi",
172
+ firstDayOfWeek = 0,
160
173
  theme = "classic",
161
174
  showLunar = true,
162
175
  format = defaultFormat,
@@ -195,26 +208,25 @@ var DatePicker = ({
195
208
  };
196
209
  }, [isOpen]);
197
210
  const yearInfo = (0, import_react2.useMemo)(() => (0, import_core2.getYearDetails)(viewYear), [viewYear]);
198
- const { weekDays: weekDayLabels, solarMonthNames: monthNames } = (0, import_core2.t)(locale);
211
+ const { solarMonthNames: monthNames } = (0, import_core2.t)(locale);
212
+ const weekDayLabels = (0, import_react2.useMemo)(() => (0, import_core2.getWeekDayLabels)(locale, firstDayOfWeek), [locale, firstDayOfWeek]);
199
213
  const grid = (0, import_react2.useMemo)(
200
- () => (0, import_core2.getCalendarGrid)(viewMonth, viewYear, selected),
201
- [viewMonth, viewYear, selected]
214
+ () => (0, import_core2.getCalendarGrid)(viewMonth, viewYear, selected, firstDayOfWeek),
215
+ [viewMonth, viewYear, selected, firstDayOfWeek]
202
216
  );
203
217
  function prevMonth() {
204
- if (viewMonth === 1) {
205
- setViewMonth(12);
206
- setViewYear((y) => y - 1);
207
- } else {
208
- setViewMonth((m) => m - 1);
209
- }
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);
210
223
  }
211
224
  function nextMonth() {
212
- if (viewMonth === 12) {
213
- setViewMonth(1);
214
- setViewYear((y) => y + 1);
215
- } else {
216
- setViewMonth((m) => m + 1);
217
- }
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);
218
230
  }
219
231
  function handleDaySelect(cell) {
220
232
  setSelected(cell.solar);
package/dist/index.mjs CHANGED
@@ -1,14 +1,17 @@
1
1
  // src/Calendar.tsx
2
2
  import React, { useState, useMemo, useEffect } from "react";
3
- import { getYearDetails, getCalendarGrid, t } from "@lichta/core";
3
+ import { getYearDetails, getCalendarGrid, getWeekDayLabels, t } from "@lichta/core";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  var Calendar = ({
6
6
  month = (/* @__PURE__ */ new Date()).getMonth() + 1,
7
7
  year = (/* @__PURE__ */ new Date()).getFullYear(),
8
8
  selectedDate: selectedDateProp,
9
9
  onSelect,
10
+ onMonthChange,
10
11
  showLunar = true,
11
12
  locale = "vi",
13
+ firstDayOfWeek = 0,
14
+ showWeekNumber = false,
12
15
  theme = "classic",
13
16
  className = "",
14
17
  children,
@@ -25,26 +28,25 @@ var Calendar = ({
25
28
  setCurrentYear(year);
26
29
  }, [year]);
27
30
  const yearInfo = useMemo(() => getYearDetails(currentYear), [currentYear]);
28
- const { weekDays: weekDayLabels, solarMonthNames: monthNames } = t(locale);
31
+ const { solarMonthNames: monthNames } = t(locale);
32
+ const weekDayLabels = useMemo(() => getWeekDayLabels(locale, firstDayOfWeek), [locale, firstDayOfWeek]);
29
33
  const calendarGrid = useMemo(
30
- () => getCalendarGrid(currentMonth, currentYear, selectedDate),
31
- [currentMonth, currentYear, selectedDate]
34
+ () => getCalendarGrid(currentMonth, currentYear, selectedDate, firstDayOfWeek),
35
+ [currentMonth, currentYear, selectedDate, firstDayOfWeek]
32
36
  );
33
37
  const prevMonth = () => {
34
- if (currentMonth === 1) {
35
- setCurrentMonth(12);
36
- setCurrentYear((y) => y - 1);
37
- } else {
38
- setCurrentMonth((m) => m - 1);
39
- }
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);
40
43
  };
41
44
  const nextMonth = () => {
42
- if (currentMonth === 12) {
43
- setCurrentMonth(1);
44
- setCurrentYear((y) => y + 1);
45
- } else {
46
- setCurrentMonth((m) => m + 1);
47
- }
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);
48
50
  };
49
51
  const handleDayClick = (cell) => {
50
52
  if (selectedDateProp === void 0) {
@@ -60,7 +62,7 @@ var Calendar = ({
60
62
  handleDayClick(cell);
61
63
  }
62
64
  };
63
- 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: [
64
66
  /* @__PURE__ */ jsxs("div", { className: "lich-ta-calendar-header", children: [
65
67
  /* @__PURE__ */ jsx("button", { className: "lich-ta-calendar-nav", onClick: prevMonth, "aria-label": "Th\xE1ng tr\u01B0\u1EDBc", children: "\u25C0" }),
66
68
  /* @__PURE__ */ jsxs("div", { className: "lich-ta-calendar-title", children: [
@@ -77,30 +79,39 @@ var Calendar = ({
77
79
  ] }),
78
80
  /* @__PURE__ */ jsx("button", { className: "lich-ta-calendar-nav", onClick: nextMonth, "aria-label": "Th\xE1ng sau", children: "\u25B6" })
79
81
  ] }),
80
- /* @__PURE__ */ jsx("div", { className: "lich-ta-calendar-weekdays", children: weekDayLabels.map((label) => /* @__PURE__ */ jsx("div", { className: "lich-ta-calendar-weekday", children: label }, label)) }),
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
+ ] }),
81
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;
82
88
  if (renderDay) {
83
- return /* @__PURE__ */ jsx(React.Fragment, { children: renderDay(cell) }, idx);
89
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
90
+ weekNumberCell,
91
+ renderDay(cell)
92
+ ] }, idx);
84
93
  }
85
94
  let classes = "lich-ta-calendar-day";
86
95
  if (cell.isToday) classes += " is-today";
87
96
  if (cell.isSelected) classes += " is-selected";
88
97
  if (!cell.isCurrentMonth) classes += " is-other-month";
89
98
  if (cell.lunar.day === 1) classes += " is-first-lunar";
90
- return /* @__PURE__ */ jsxs(
91
- "button",
92
- {
93
- className: classes,
94
- onClick: () => handleDayClick(cell),
95
- onKeyDown: (e) => handleKeydown(e, cell),
96
- tabIndex: cell.isCurrentMonth ? 0 : -1,
97
- children: [
98
- /* @__PURE__ */ jsx("span", { className: "solar-day", children: cell.solar.getDate() }),
99
- showLunar && /* @__PURE__ */ jsx("span", { className: "lunar-day", children: cell.lunar.day === 1 ? `${cell.lunar.day}/${cell.lunar.month}${cell.lunar.isLeap ? "*" : ""}` : cell.lunar.day })
100
- ]
101
- },
102
- idx
103
- );
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);
104
115
  }) }),
105
116
  children && /* @__PURE__ */ jsx("div", { className: "lich-ta-calendar-footer", children })
106
117
  ] });
@@ -108,7 +119,7 @@ var Calendar = ({
108
119
 
109
120
  // src/DatePicker.tsx
110
121
  import { useState as useState2, useRef, useEffect as useEffect2, useMemo as useMemo2 } from "react";
111
- import { getYearDetails as getYearDetails2, getCalendarGrid as getCalendarGrid2, t as t2 } from "@lichta/core";
122
+ import { getYearDetails as getYearDetails2, getCalendarGrid as getCalendarGrid2, getWeekDayLabels as getWeekDayLabels2, t as t2 } from "@lichta/core";
112
123
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
113
124
  function defaultFormat(date) {
114
125
  const dd = String(date.getDate()).padStart(2, "0");
@@ -118,8 +129,10 @@ function defaultFormat(date) {
118
129
  var DatePicker = ({
119
130
  value = null,
120
131
  onSelect,
132
+ onMonthChange,
121
133
  placeholder = "Ch\u1ECDn ng\xE0y",
122
134
  locale = "vi",
135
+ firstDayOfWeek = 0,
123
136
  theme = "classic",
124
137
  showLunar = true,
125
138
  format = defaultFormat,
@@ -158,26 +171,25 @@ var DatePicker = ({
158
171
  };
159
172
  }, [isOpen]);
160
173
  const yearInfo = useMemo2(() => getYearDetails2(viewYear), [viewYear]);
161
- const { weekDays: weekDayLabels, solarMonthNames: monthNames } = t2(locale);
174
+ const { solarMonthNames: monthNames } = t2(locale);
175
+ const weekDayLabels = useMemo2(() => getWeekDayLabels2(locale, firstDayOfWeek), [locale, firstDayOfWeek]);
162
176
  const grid = useMemo2(
163
- () => getCalendarGrid2(viewMonth, viewYear, selected),
164
- [viewMonth, viewYear, selected]
177
+ () => getCalendarGrid2(viewMonth, viewYear, selected, firstDayOfWeek),
178
+ [viewMonth, viewYear, selected, firstDayOfWeek]
165
179
  );
166
180
  function prevMonth() {
167
- if (viewMonth === 1) {
168
- setViewMonth(12);
169
- setViewYear((y) => y - 1);
170
- } else {
171
- setViewMonth((m) => m - 1);
172
- }
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);
173
186
  }
174
187
  function nextMonth() {
175
- if (viewMonth === 12) {
176
- setViewMonth(1);
177
- setViewYear((y) => y + 1);
178
- } else {
179
- setViewMonth((m) => m + 1);
180
- }
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);
181
193
  }
182
194
  function handleDaySelect(cell) {
183
195
  setSelected(cell.solar);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lichta/react",
3
- "version": "2.1.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.1.0"
15
+ "@lichta/core": "2.2.0"
16
16
  },
17
17
  "peerDependencies": {
18
18
  "react": ">=18.0.0",