@jalalix/mui 0.1.0 → 0.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.
@@ -0,0 +1,352 @@
1
+ import JalaliX from '@jalalix/typescript';
2
+ import type { AdapterFormats, AdapterOptions, DateBuilderReturnType, FieldFormatTokenMap, MuiPickersAdapter, PickerValidDate, PickersTimezone } from '@mui/x-date-pickers/models';
3
+ declare module '@mui/x-date-pickers/models' {
4
+ interface PickerValidDateLookup {
5
+ jalalix: JalaliX;
6
+ }
7
+ }
8
+ /**
9
+ * MUI X Date Pickers adapter backed by `@jalalix/typescript`.
10
+ *
11
+ * Implements {@link MuiPickersAdapter} with Jalali (Persian / Shamsi) calendar
12
+ * semantics: months are Jalali, the week starts on Saturday, and format tokens
13
+ * follow JalaliX (`YYYY`, `MMM`, `WN`, etc.).
14
+ *
15
+ * Pass to `LocalizationProvider`:
16
+ * ```ts
17
+ * <LocalizationProvider dateAdapter={AdapterJalaliX}>
18
+ * ```
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const adapter = new AdapterJalaliX({ locale: 'fa', formats: { keyboardDate: 'YYYY/MM/DD' } })
23
+ * const today = adapter.date()
24
+ * adapter.format(today, 'fullDate')
25
+ * ```
26
+ */
27
+ export declare class AdapterJalaliX implements MuiPickersAdapter {
28
+ isMUIAdapter: boolean;
29
+ isTimezoneCompatible: boolean;
30
+ lib: string;
31
+ locale?: string;
32
+ formats: AdapterFormats;
33
+ escapedCharacters: {
34
+ start: string;
35
+ end: string;
36
+ };
37
+ formatTokenMap: FieldFormatTokenMap;
38
+ /**
39
+ * Creates a JalaliX-backed MUI pickers adapter.
40
+ * @param options - Adapter options.
41
+ * @param options.locale - Locale code (e.g. `'fa'`, `'en'`); affects 12-hour cycle detection.
42
+ * @param options.formats - Partial overrides for {@link AdapterFormats} keys (e.g. `keyboardDate`).
43
+ */
44
+ constructor({ locale, formats }?: AdapterOptions<string, never>);
45
+ private asJalaliX;
46
+ /** Applies IANA / alias timezone via JalaliX (instant-preserving). */
47
+ private withTimezone;
48
+ /**
49
+ * Sets Jalali Y/M/D and wall clock in the value's active timezone.
50
+ * Month is 0-based (Farvardin = 0), matching MUI / `Date`.
51
+ */
52
+ private setJalaliParts;
53
+ /** Sets wall clock fields in the active timezone (defaults fill from current wall time). */
54
+ private withWallTime;
55
+ private weekDayName;
56
+ private weekDayLetter;
57
+ /**
58
+ * Builds a JalaliX date from a value, or returns “now” / `null`.
59
+ * @param value - ISO/string/timestamp to parse; `undefined` -> now; `null` -> `null`.
60
+ * @param timezone - MUI timezone (`'default'`, `'system'`, or IANA name).
61
+ * @returns A {@link JalaliX} instance, `null`, or an invalid date on parse failure.
62
+ */
63
+ date: <T extends string | null | undefined>(value?: T, timezone?: PickersTimezone) => DateBuilderReturnType<T>;
64
+ /**
65
+ * Returns an invalid JalaliX date (`NaN` timestamp) for failed parses.
66
+ * @returns Invalid {@link JalaliX} instance.
67
+ */
68
+ getInvalidDate: () => JalaliX;
69
+ /**
70
+ * Resolves the timezone string for a picker value.
71
+ * @param value - Date value, or `null`.
72
+ * @returns IANA timezone, `'default'` if `value` is null, or `'system'` on error.
73
+ */
74
+ getTimezone: (value: PickerValidDate | null) => string;
75
+ /**
76
+ * Returns a copy of `value` in the given timezone (instant-preserving).
77
+ * @param value - Source date.
78
+ * @param timezone - Target timezone (`'default'` / `'system'` / IANA / `'UTC'`).
79
+ * @returns JalaliX in the requested timezone.
80
+ */
81
+ setTimezone: (value: PickerValidDate, timezone: PickersTimezone) => JalaliX;
82
+ /**
83
+ * Converts a picker value to a native JavaScript `Date`.
84
+ * @param value - Picker date.
85
+ * @returns Equivalent `Date` (same epoch ms).
86
+ */
87
+ toJsDate: (value: PickerValidDate) => Date;
88
+ /**
89
+ * Parses a user-entered string with the given format into a JalaliX date.
90
+ * Uses {@link JalaliX.parseJalali} for common `YYYY/MM/DD` keyboard formats;
91
+ * otherwise tokenizes `format` (Persian digits accepted).
92
+ * @param value - Input string (empty -> `null`).
93
+ * @param format - Format pattern (e.g. `'YYYY/MM/DD'`, `'D MMM YYYY'`).
94
+ * @returns Parsed {@link JalaliX}, `null` if empty, or an invalid date on failure.
95
+ */
96
+ parse: (value: string, format: string) => JalaliX | null;
97
+ private parseWithFormat;
98
+ /**
99
+ * @returns Active locale code, or `'fa'` when unset.
100
+ */
101
+ getCurrentLocaleCode: () => string;
102
+ /**
103
+ * Whether the current locale prefers a 12-hour clock (`en*` -> true).
104
+ * @returns `true` for English locales; otherwise `false`.
105
+ */
106
+ is12HourCycleInCurrentLocale: () => boolean;
107
+ /**
108
+ * Normalizes MUI/localized format aliases to JalaliX tokens
109
+ * (`MMMM`->`MMM`, `dddd`/`ddd`->`WN`, word-boundary `dd`->`WL`).
110
+ * @param format - Format string from formats map or field props.
111
+ * @returns Expanded format string for {@link formatByString} / {@link parse}.
112
+ */
113
+ expandFormat: (format: string) => string;
114
+ /**
115
+ * @param value - Value to validate.
116
+ * @returns Whether `value` is a valid {@link JalaliX} / Date.
117
+ */
118
+ isValid: (value: PickerValidDate | null) => value is PickerValidDate;
119
+ /**
120
+ * Formats a date using a named key from {@link formats}.
121
+ * @param value - Date to format.
122
+ * @param formatKey - Key in {@link AdapterFormats} (e.g. `'fullDate'`, `'keyboardDate'`).
123
+ * @returns Formatted string.
124
+ */
125
+ format: (value: PickerValidDate, formatKey: keyof AdapterFormats) => string;
126
+ /**
127
+ * Formats a date with an explicit JalaliX format string.
128
+ * Protects weekday tokens (`WN`/`WL`) that would otherwise collide with week-number `W`.
129
+ * @param value - Date to format.
130
+ * @param formatString - Pattern (e.g. `'YYYY/MM/DD'`, `'WN D MMM YYYY'`).
131
+ * @returns Formatted string.
132
+ */
133
+ formatByString: (value: PickerValidDate, formatString: string) => string;
134
+ /**
135
+ * Localizes digits for field display (Western -> Persian; `,` -> `،`).
136
+ * @param numberToFormat - Digit string from MUI fields.
137
+ * @returns String with Persian numerals.
138
+ */
139
+ formatNumber: (numberToFormat: string) => string;
140
+ /**
141
+ * @param value - First date (or `null`).
142
+ * @param comparing - Second date (or `null`).
143
+ * @returns `true` if both null or same epoch ms.
144
+ */
145
+ isEqual: (value: PickerValidDate | null, comparing: PickerValidDate | null) => boolean;
146
+ /** @returns Whether both dates share the same Jalali year. */
147
+ isSameYear: (value: PickerValidDate, comparing: PickerValidDate) => boolean;
148
+ /** @returns Whether both dates share the same Jalali year and month. */
149
+ isSameMonth: (value: PickerValidDate, comparing: PickerValidDate) => boolean;
150
+ /** @returns Whether both dates fall on the same Jalali calendar day. */
151
+ isSameDay: (value: PickerValidDate, comparing: PickerValidDate) => boolean;
152
+ /** @returns Whether both dates share the same day and hour. */
153
+ isSameHour: (value: PickerValidDate, comparing: PickerValidDate) => boolean;
154
+ /** @returns Whether `value` is strictly after `comparing` by timestamp. */
155
+ isAfter: (value: PickerValidDate, comparing: PickerValidDate) => boolean;
156
+ /** @returns Whether `value`'s Jalali year is after `comparing`'s. */
157
+ isAfterYear: (value: PickerValidDate, comparing: PickerValidDate) => boolean;
158
+ /** @returns Whether `value`'s calendar day is after `comparing`'s. */
159
+ isAfterDay: (value: PickerValidDate, comparing: PickerValidDate) => boolean;
160
+ /** @returns Whether `value` is strictly before `comparing` by timestamp. */
161
+ isBefore: (value: PickerValidDate, comparing: PickerValidDate) => boolean;
162
+ /** @returns Whether `value`'s Jalali year is before `comparing`'s. */
163
+ isBeforeYear: (value: PickerValidDate, comparing: PickerValidDate) => boolean;
164
+ /** @returns Whether `value`'s calendar day is before `comparing`'s. */
165
+ isBeforeDay: (value: PickerValidDate, comparing: PickerValidDate) => boolean;
166
+ /**
167
+ * @param value - Date to test.
168
+ * @param range - Inclusive `[start, end]` bounds.
169
+ * @returns Whether `value` lies within the range by timestamp.
170
+ */
171
+ isWithinRange: (value: PickerValidDate, range: [PickerValidDate, PickerValidDate]) => boolean;
172
+ /**
173
+ * @param value - Reference date.
174
+ * @returns Start of that Jalali year (Farvardin 1, 00:00:00.000).
175
+ */
176
+ startOfYear: (value: PickerValidDate) => JalaliX;
177
+ /**
178
+ * @param value - Reference date.
179
+ * @returns Start of that Jalali month (day 1, 00:00:00.000).
180
+ */
181
+ startOfMonth: (value: PickerValidDate) => JalaliX;
182
+ /**
183
+ * Persian week starts on Saturday.
184
+ * @param value - Reference date.
185
+ * @returns Start of that week (Saturday 00:00:00.000).
186
+ */
187
+ startOfWeek: (value: PickerValidDate) => JalaliX;
188
+ /**
189
+ * @param value - Reference date.
190
+ * @returns Same calendar day at 00:00:00.000 local wall time.
191
+ */
192
+ startOfDay: (value: PickerValidDate) => JalaliX;
193
+ /**
194
+ * @param value - Reference date.
195
+ * @returns End of that Jalali year (last moment of Esfand).
196
+ */
197
+ endOfYear: (value: PickerValidDate) => JalaliX;
198
+ /**
199
+ * @param value - Reference date.
200
+ * @returns End of that Jalali month (23:59:59.999 on the last day).
201
+ */
202
+ endOfMonth: (value: PickerValidDate) => JalaliX;
203
+ /**
204
+ * Persian week ends on Friday.
205
+ * @param value - Reference date.
206
+ * @returns End of that week (Friday 23:59:59.999).
207
+ */
208
+ endOfWeek: (value: PickerValidDate) => JalaliX;
209
+ /**
210
+ * @param value - Reference date.
211
+ * @returns Same calendar day at 23:59:59.999 local wall time.
212
+ */
213
+ endOfDay: (value: PickerValidDate) => JalaliX;
214
+ /**
215
+ * @param value - Source date.
216
+ * @param amount - Years to add (may be negative).
217
+ * @returns New JalaliX with year adjusted.
218
+ */
219
+ addYears: (value: PickerValidDate, amount: number) => JalaliX;
220
+ /**
221
+ * Adds months with day clamping to the target month length.
222
+ * @param value - Source date.
223
+ * @param amount - Months to add (may be negative).
224
+ * @returns New JalaliX with month adjusted.
225
+ */
226
+ addMonths: (value: PickerValidDate, amount: number) => JalaliX;
227
+ /**
228
+ * @param value - Source date.
229
+ * @param amount - Weeks to add (may be negative).
230
+ * @returns New JalaliX with days shifted by `amount * 7`.
231
+ */
232
+ addWeeks: (value: PickerValidDate, amount: number) => JalaliX;
233
+ /**
234
+ * Adds calendar days (wall time preserved via JalaliX timezone-aware setters).
235
+ * @param value - Source date.
236
+ * @param amount - Days to add (may be negative).
237
+ * @returns New JalaliX with day adjusted.
238
+ */
239
+ addDays: (value: PickerValidDate, amount: number) => JalaliX;
240
+ /**
241
+ * @param value - Source date.
242
+ * @param amount - Hours to add (may be negative).
243
+ * @returns New JalaliX with timestamp shifted by hours.
244
+ */
245
+ addHours: (value: PickerValidDate, amount: number) => JalaliX;
246
+ /**
247
+ * @param value - Source date.
248
+ * @param amount - Minutes to add (may be negative).
249
+ * @returns New JalaliX with timestamp shifted by minutes.
250
+ */
251
+ addMinutes: (value: PickerValidDate, amount: number) => JalaliX;
252
+ /**
253
+ * @param value - Source date.
254
+ * @param amount - Seconds to add (may be negative).
255
+ * @returns New JalaliX with timestamp shifted by seconds.
256
+ */
257
+ addSeconds: (value: PickerValidDate, amount: number) => JalaliX;
258
+ /** @returns Jalali year. */
259
+ getYear: (value: PickerValidDate) => number;
260
+ /**
261
+ * 0-based Jalali month (0 = Farvardin).
262
+ * @returns Month index 0–11.
263
+ */
264
+ getMonth: (value: PickerValidDate) => number;
265
+ /** @returns Day of month (1–31). */
266
+ getDate: (value: PickerValidDate) => number;
267
+ /** @returns Hours (0–23). */
268
+ getHours: (value: PickerValidDate) => number;
269
+ /** @returns Minutes (0–59). */
270
+ getMinutes: (value: PickerValidDate) => number;
271
+ /** @returns Seconds (0–59). */
272
+ getSeconds: (value: PickerValidDate) => number;
273
+ /** @returns Milliseconds (0–999). */
274
+ getMilliseconds: (value: PickerValidDate) => number;
275
+ /**
276
+ * @param value - Source date.
277
+ * @param year - Jalali year to set.
278
+ * @returns New JalaliX with year replaced (month/day/time preserved).
279
+ */
280
+ setYear: (value: PickerValidDate, year: number) => JalaliX;
281
+ /**
282
+ * @param value - Source date.
283
+ * @param month - 0-based Jalali month (0 = Farvardin).
284
+ * @returns New JalaliX with month replaced.
285
+ */
286
+ setMonth: (value: PickerValidDate, month: number) => JalaliX;
287
+ /**
288
+ * @param value - Source date.
289
+ * @param date - Day of month to set.
290
+ * @returns New JalaliX with day replaced.
291
+ */
292
+ setDate: (value: PickerValidDate, date: number) => JalaliX;
293
+ /**
294
+ * @param value - Source date.
295
+ * @param hours - Hours to set (0–23).
296
+ * @returns New JalaliX with hours replaced.
297
+ */
298
+ setHours: (value: PickerValidDate, hours: number) => JalaliX;
299
+ /**
300
+ * @param value - Source date.
301
+ * @param minutes - Minutes to set.
302
+ * @returns New JalaliX with minutes replaced.
303
+ */
304
+ setMinutes: (value: PickerValidDate, minutes: number) => JalaliX;
305
+ /**
306
+ * @param value - Source date.
307
+ * @param seconds - Seconds to set.
308
+ * @returns New JalaliX with seconds replaced.
309
+ */
310
+ setSeconds: (value: PickerValidDate, seconds: number) => JalaliX;
311
+ /**
312
+ * @param value - Source date.
313
+ * @param milliseconds - Milliseconds to set.
314
+ * @returns New JalaliX with milliseconds replaced.
315
+ */
316
+ setMilliseconds: (value: PickerValidDate, milliseconds: number) => JalaliX;
317
+ /**
318
+ * Days in the Jalali month of `value` (31 / 30 / 29–30 for Esfand).
319
+ * @param value - Reference date.
320
+ * @returns Day count for that month.
321
+ */
322
+ getDaysInMonth: (value: PickerValidDate) => number;
323
+ /**
324
+ * Builds the month grid as weeks (Saturday-start) for calendar UIs.
325
+ * @param value - Any date in the target month.
326
+ * @returns Array of weeks; each week is seven {@link JalaliX} days.
327
+ */
328
+ getWeekArray: (value: PickerValidDate) => JalaliX[][];
329
+ /**
330
+ * @param value - Reference date.
331
+ * @returns Jalali week-of-year (1–53).
332
+ */
333
+ getWeekNumber: (value: PickerValidDate) => number;
334
+ /**
335
+ * 1-based day of week with Saturday as the first day (Persian week).
336
+ * @param value - Reference date.
337
+ * @returns 1 (Saturday) … 7 (Friday).
338
+ */
339
+ getDayOfWeek: (value: PickerValidDate) => number;
340
+ /**
341
+ * Lists year-start dates from `start` through `end` (inclusive range walk).
342
+ * @param range - `[start, end]` picker dates.
343
+ * @returns One {@link JalaliX} per year at start-of-year.
344
+ */
345
+ getYearRange: ([start, end]: [PickerValidDate, PickerValidDate]) => JalaliX[];
346
+ /**
347
+ * Optional helper for older pickers / custom UIs.
348
+ * @param ampm - `'am'` or `'pm'`.
349
+ * @returns Persian meridiem (`ق.ظ.` / `ب.ظ.`).
350
+ */
351
+ getMeridiemText: (ampm: 'am' | 'pm') => string;
352
+ }
@@ -0,0 +1,755 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AdapterJalaliX = void 0;
7
+ const typescript_1 = __importDefault(require("@jalalix/typescript"));
8
+ const MONTH_NAMES = ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'];
9
+ const WEEKDAY_NAMES = ['شنبه', 'یکشنبه', 'دوشنبه', 'سه شنبه', 'چهار شنبه', 'پنج شنبه', 'جمعه'];
10
+ const WEEKDAY_LETTERS = ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج'];
11
+ const formatTokenMap = {
12
+ // Year
13
+ YY: 'year',
14
+ YYYY: { sectionType: 'year', contentType: 'digit', maxLength: 4 },
15
+ // Month
16
+ M: { sectionType: 'month', contentType: 'digit', maxLength: 2 },
17
+ MM: 'month',
18
+ MMM: { sectionType: 'month', contentType: 'letter' },
19
+ MMMM: { sectionType: 'month', contentType: 'letter' },
20
+ // Day of the month
21
+ D: { sectionType: 'day', contentType: 'digit', maxLength: 2 },
22
+ DD: 'day',
23
+ // Day of the week (JalaliX tokens)
24
+ WN: { sectionType: 'weekDay', contentType: 'letter' },
25
+ wn: { sectionType: 'weekDay', contentType: 'letter' },
26
+ WL: { sectionType: 'weekDay', contentType: 'letter' },
27
+ wl: { sectionType: 'weekDay', contentType: 'letter' },
28
+ dddd: { sectionType: 'weekDay', contentType: 'letter' },
29
+ ddd: { sectionType: 'weekDay', contentType: 'letter' },
30
+ dd: { sectionType: 'weekDay', contentType: 'letter' },
31
+ d: { sectionType: 'weekDay', contentType: 'digit', maxLength: 2 },
32
+ // Meridiem
33
+ A: 'meridiem',
34
+ a: 'meridiem',
35
+ // Hours
36
+ H: { sectionType: 'hours', contentType: 'digit', maxLength: 2 },
37
+ HH: 'hours',
38
+ h: { sectionType: 'hours', contentType: 'digit', maxLength: 2 },
39
+ hh: 'hours',
40
+ // Minutes
41
+ m: { sectionType: 'minutes', contentType: 'digit', maxLength: 2 },
42
+ mm: 'minutes',
43
+ // Seconds
44
+ s: { sectionType: 'seconds', contentType: 'digit', maxLength: 2 },
45
+ ss: 'seconds'
46
+ };
47
+ const defaultFormats = {
48
+ year: 'YYYY',
49
+ month: 'MMM',
50
+ monthShort: 'MMM',
51
+ dayOfMonth: 'D',
52
+ dayOfMonthFull: 'D',
53
+ weekday: 'WN',
54
+ weekdayShort: 'WL',
55
+ hours24h: 'HH',
56
+ hours12h: 'hh',
57
+ meridiem: 'A',
58
+ minutes: 'mm',
59
+ seconds: 'ss',
60
+ fullDate: 'WN D MMM YYYY',
61
+ keyboardDate: 'YYYY/MM/DD',
62
+ shortDate: 'D MMM',
63
+ normalDate: 'D MMM',
64
+ normalDateWithWeekday: 'WN، D MMM',
65
+ fullTime12h: 'hh:mm A',
66
+ fullTime24h: 'HH:mm',
67
+ keyboardDateTime12h: 'YYYY/MM/DD hh:mm A',
68
+ keyboardDateTime24h: 'YYYY/MM/DD HH:mm'
69
+ };
70
+ /**
71
+ * MUI X Date Pickers adapter backed by `@jalalix/typescript`.
72
+ *
73
+ * Implements {@link MuiPickersAdapter} with Jalali (Persian / Shamsi) calendar
74
+ * semantics: months are Jalali, the week starts on Saturday, and format tokens
75
+ * follow JalaliX (`YYYY`, `MMM`, `WN`, etc.).
76
+ *
77
+ * Pass to `LocalizationProvider`:
78
+ * ```ts
79
+ * <LocalizationProvider dateAdapter={AdapterJalaliX}>
80
+ * ```
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * const adapter = new AdapterJalaliX({ locale: 'fa', formats: { keyboardDate: 'YYYY/MM/DD' } })
85
+ * const today = adapter.date()
86
+ * adapter.format(today, 'fullDate')
87
+ * ```
88
+ */
89
+ class AdapterJalaliX {
90
+ /**
91
+ * Creates a JalaliX-backed MUI pickers adapter.
92
+ * @param options - Adapter options.
93
+ * @param options.locale - Locale code (e.g. `'fa'`, `'en'`); affects 12-hour cycle detection.
94
+ * @param options.formats - Partial overrides for {@link AdapterFormats} keys (e.g. `keyboardDate`).
95
+ */
96
+ constructor({ locale, formats } = {}) {
97
+ // Always `true` — marks this as an MUI X date adapter.
98
+ this.isMUIAdapter = true;
99
+ // Adapter supports MUI timezone APIs (`date`, `setTimezone`, etc.).
100
+ this.isTimezoneCompatible = true;
101
+ // Library id registered with MUI (`jalalix`).
102
+ this.lib = 'jalalix';
103
+ // Characters that escape literal text inside format strings (`[` … `]`).
104
+ this.escapedCharacters = { start: '[', end: ']' };
105
+ // Token -> section mapping used by MUI field components.
106
+ this.formatTokenMap = formatTokenMap;
107
+ this.asJalaliX = (value) => {
108
+ if (value instanceof typescript_1.default)
109
+ return value;
110
+ return typescript_1.default.create(value);
111
+ };
112
+ /** Applies IANA / alias timezone via JalaliX (instant-preserving). */
113
+ this.withTimezone = (value, timezone = 'default') => {
114
+ return value.setTimezone(timezone);
115
+ };
116
+ /**
117
+ * Sets Jalali Y/M/D and wall clock in the value's active timezone.
118
+ * Month is 0-based (Farvardin = 0), matching MUI / `Date`.
119
+ */
120
+ this.setJalaliParts = (value, year, month, date, hours = 12, minutes = 0, seconds = 0, ms = 0) => {
121
+ const result = this.asJalaliX(value).clone();
122
+ result.setFullYear(year, month, date);
123
+ result.setHours(hours, minutes, seconds, ms);
124
+ return result;
125
+ };
126
+ /** Sets wall clock fields in the active timezone (defaults fill from current wall time). */
127
+ this.withWallTime = (value, hours, minutes, seconds, ms) => {
128
+ const current = this.asJalaliX(value).clone();
129
+ current.setHours(hours, minutes !== null && minutes !== void 0 ? minutes : current.getMinutes(), seconds !== null && seconds !== void 0 ? seconds : current.getSeconds(), ms !== null && ms !== void 0 ? ms : current.getMilliseconds());
130
+ return current;
131
+ };
132
+ this.weekDayName = (value) => WEEKDAY_NAMES[this.getDayOfWeek(value) - 1];
133
+ this.weekDayLetter = (value) => WEEKDAY_LETTERS[this.getDayOfWeek(value) - 1];
134
+ /**
135
+ * Builds a JalaliX date from a value, or returns “now” / `null`.
136
+ * @param value - ISO/string/timestamp to parse; `undefined` -> now; `null` -> `null`.
137
+ * @param timezone - MUI timezone (`'default'`, `'system'`, or IANA name).
138
+ * @returns A {@link JalaliX} instance, `null`, or an invalid date on parse failure.
139
+ */
140
+ this.date = (value, timezone = 'default') => {
141
+ if (value === null)
142
+ return null;
143
+ if (value === undefined) {
144
+ return this.withTimezone(new typescript_1.default(), timezone);
145
+ }
146
+ try {
147
+ return this.withTimezone(typescript_1.default.create(value), timezone);
148
+ }
149
+ catch (_a) {
150
+ return this.getInvalidDate();
151
+ }
152
+ };
153
+ /**
154
+ * Returns an invalid JalaliX date (`NaN` timestamp) for failed parses.
155
+ * @returns Invalid {@link JalaliX} instance.
156
+ */
157
+ this.getInvalidDate = () => new typescript_1.default(Number.NaN);
158
+ /**
159
+ * Resolves the timezone string for a picker value.
160
+ * @param value - Date value, or `null`.
161
+ * @returns IANA timezone, `'default'` if `value` is null, or `'system'` on error.
162
+ */
163
+ this.getTimezone = (value) => {
164
+ if (value == null)
165
+ return 'default';
166
+ try {
167
+ return this.asJalaliX(value).getTimezone();
168
+ }
169
+ catch (_a) {
170
+ return 'system';
171
+ }
172
+ };
173
+ /**
174
+ * Returns a copy of `value` in the given timezone (instant-preserving).
175
+ * @param value - Source date.
176
+ * @param timezone - Target timezone (`'default'` / `'system'` / IANA / `'UTC'`).
177
+ * @returns JalaliX in the requested timezone.
178
+ */
179
+ this.setTimezone = (value, timezone) => {
180
+ return this.asJalaliX(value).setTimezone(timezone);
181
+ };
182
+ /**
183
+ * Converts a picker value to a native JavaScript `Date`.
184
+ * @param value - Picker date.
185
+ * @returns Equivalent `Date` (same epoch ms).
186
+ */
187
+ this.toJsDate = (value) => new Date(this.asJalaliX(value).getTime());
188
+ /**
189
+ * Parses a user-entered string with the given format into a JalaliX date.
190
+ * Uses {@link JalaliX.parseJalali} for common `YYYY/MM/DD` keyboard formats;
191
+ * otherwise tokenizes `format` (Persian digits accepted).
192
+ * @param value - Input string (empty -> `null`).
193
+ * @param format - Format pattern (e.g. `'YYYY/MM/DD'`, `'D MMM YYYY'`).
194
+ * @returns Parsed {@link JalaliX}, `null` if empty, or an invalid date on failure.
195
+ */
196
+ this.parse = (value, format) => {
197
+ if (value === '')
198
+ return null;
199
+ const normalized = typescript_1.default.fromPersian(String(value)).trim();
200
+ const expanded = this.expandFormat(format);
201
+ // Fast path: JalaliX native parse for common keyboard formats
202
+ if (/^YYYY[/.-]MM[/.-]DD(?:[ T]HH:mm(?::ss)?)?$/.test(expanded.replace(/\s+/g, ' ').trim()) || /^YYYY[/.-]MM[/.-]DD$/.test(expanded)) {
203
+ try {
204
+ return typescript_1.default.parseJalali(normalized);
205
+ }
206
+ catch (_a) {
207
+ return this.getInvalidDate();
208
+ }
209
+ }
210
+ return this.parseWithFormat(normalized, expanded);
211
+ };
212
+ this.parseWithFormat = (value, format) => {
213
+ const tokenOrder = [];
214
+ let regexSource = '';
215
+ let i = 0;
216
+ const tokens = [
217
+ { key: 'YYYY', pattern: '(\\d{4})' },
218
+ { key: 'MMMM', pattern: `(${MONTH_NAMES.join('|')})` },
219
+ { key: 'MMM', pattern: `(${MONTH_NAMES.join('|')})` },
220
+ { key: 'MM', pattern: '(\\d{1,2})' },
221
+ { key: 'DD', pattern: '(\\d{1,2})' },
222
+ { key: 'HH', pattern: '(\\d{1,2})' },
223
+ { key: 'hh', pattern: '(\\d{1,2})' },
224
+ { key: 'mm', pattern: '(\\d{1,2})' },
225
+ { key: 'ss', pattern: '(\\d{1,2})' },
226
+ { key: 'A', pattern: '(ق\\.ظ\\.|ب\\.ظ\\.|AM|PM|am|pm)' },
227
+ { key: 'a', pattern: '(ق\\.ظ\\.|ب\\.ظ\\.|AM|PM|am|pm)' },
228
+ { key: 'YY', pattern: '(\\d{2})' },
229
+ { key: 'M', pattern: '(\\d{1,2})' },
230
+ { key: 'D', pattern: '(\\d{1,2})' },
231
+ { key: 'H', pattern: '(\\d{1,2})' },
232
+ { key: 'h', pattern: '(\\d{1,2})' },
233
+ { key: 'm', pattern: '(\\d{1,2})' },
234
+ { key: 's', pattern: '(\\d{1,2})' }
235
+ ];
236
+ while (i < format.length) {
237
+ if (format[i] === this.escapedCharacters.start) {
238
+ const end = format.indexOf(this.escapedCharacters.end, i + 1);
239
+ if (end === -1) {
240
+ regexSource += escapeRegExp(format[i]);
241
+ i += 1;
242
+ continue;
243
+ }
244
+ regexSource += escapeRegExp(format.slice(i + 1, end));
245
+ i = end + 1;
246
+ continue;
247
+ }
248
+ const matched = tokens.find(token => format.startsWith(token.key, i));
249
+ if (matched) {
250
+ tokenOrder.push(matched);
251
+ regexSource += matched.pattern;
252
+ i += matched.key.length;
253
+ continue;
254
+ }
255
+ regexSource += escapeRegExp(format[i]);
256
+ i += 1;
257
+ }
258
+ const match = value.match(new RegExp(`^${regexSource}$`));
259
+ if (!match)
260
+ return this.getInvalidDate();
261
+ let year = new typescript_1.default().getFullYear();
262
+ let month = 1;
263
+ let day = 1;
264
+ let hours = 0;
265
+ let minutes = 0;
266
+ let seconds = 0;
267
+ let isPM = null;
268
+ let hour12 = null;
269
+ tokenOrder.forEach((token, index) => {
270
+ const raw = match[index + 1];
271
+ switch (token.key) {
272
+ case 'YYYY':
273
+ year = Number(raw);
274
+ break;
275
+ case 'YY':
276
+ year = 1400 + Number(raw);
277
+ break;
278
+ case 'MMMM':
279
+ case 'MMM': {
280
+ const monthIndex = MONTH_NAMES.indexOf(raw);
281
+ if (monthIndex >= 0)
282
+ month = monthIndex + 1;
283
+ break;
284
+ }
285
+ case 'MM':
286
+ case 'M':
287
+ month = Number(raw);
288
+ break;
289
+ case 'DD':
290
+ case 'D':
291
+ day = Number(raw);
292
+ break;
293
+ case 'HH':
294
+ case 'H':
295
+ hours = Number(raw);
296
+ break;
297
+ case 'hh':
298
+ case 'h':
299
+ hour12 = Number(raw);
300
+ break;
301
+ case 'mm':
302
+ case 'm':
303
+ minutes = Number(raw);
304
+ break;
305
+ case 'ss':
306
+ case 's':
307
+ seconds = Number(raw);
308
+ break;
309
+ case 'A':
310
+ case 'a':
311
+ isPM = /ب\.ظ\.|pm/i.test(raw);
312
+ break;
313
+ default:
314
+ break;
315
+ }
316
+ });
317
+ if (hour12 !== null) {
318
+ if (isPM === true && hour12 < 12)
319
+ hours = hour12 + 12;
320
+ else if (isPM === false && hour12 === 12)
321
+ hours = 0;
322
+ else
323
+ hours = hour12;
324
+ }
325
+ if (month < 1 || month > 12 || day < 1 || day > 31)
326
+ return this.getInvalidDate();
327
+ const output = new typescript_1.default();
328
+ output.setFullYear(year, month - 1, day);
329
+ output.setHours(hours, minutes, seconds, 0);
330
+ return output;
331
+ };
332
+ /**
333
+ * @returns Active locale code, or `'fa'` when unset.
334
+ */
335
+ this.getCurrentLocaleCode = () => this.locale || 'fa';
336
+ /**
337
+ * Whether the current locale prefers a 12-hour clock (`en*` -> true).
338
+ * @returns `true` for English locales; otherwise `false`.
339
+ */
340
+ this.is12HourCycleInCurrentLocale = () => {
341
+ const locale = this.getCurrentLocaleCode().toLowerCase();
342
+ return locale.startsWith('en');
343
+ };
344
+ /**
345
+ * Normalizes MUI/localized format aliases to JalaliX tokens
346
+ * (`MMMM`->`MMM`, `dddd`/`ddd`->`WN`, word-boundary `dd`->`WL`).
347
+ * @param format - Format string from formats map or field props.
348
+ * @returns Expanded format string for {@link formatByString} / {@link parse}.
349
+ */
350
+ this.expandFormat = (format) => {
351
+ // JalaliX has no localized meta-tokens (L/LL/LT); keep format as-is aside from MMMM->MMM.
352
+ return format
353
+ .replace(/MMMM/g, 'MMM')
354
+ .replace(/dddd/g, 'WN')
355
+ .replace(/ddd/g, 'WN')
356
+ .replace(/\bdd\b/g, 'WL');
357
+ };
358
+ /**
359
+ * @param value - Value to validate.
360
+ * @returns Whether `value` is a valid {@link JalaliX} / Date.
361
+ */
362
+ this.isValid = (value) => {
363
+ if (value == null)
364
+ return false;
365
+ return typescript_1.default.isValidDate(this.asJalaliX(value));
366
+ };
367
+ /**
368
+ * Formats a date using a named key from {@link formats}.
369
+ * @param value - Date to format.
370
+ * @param formatKey - Key in {@link AdapterFormats} (e.g. `'fullDate'`, `'keyboardDate'`).
371
+ * @returns Formatted string.
372
+ */
373
+ this.format = (value, formatKey) => {
374
+ return this.formatByString(value, this.formats[formatKey]);
375
+ };
376
+ /**
377
+ * Formats a date with an explicit JalaliX format string.
378
+ * Protects weekday tokens (`WN`/`WL`) that would otherwise collide with week-number `W`.
379
+ * @param value - Date to format.
380
+ * @param formatString - Pattern (e.g. `'YYYY/MM/DD'`, `'WN D MMM YYYY'`).
381
+ * @returns Formatted string.
382
+ */
383
+ this.formatByString = (value, formatString) => {
384
+ const date = this.asJalaliX(value);
385
+ const expanded = this.expandFormat(formatString);
386
+ // JalaliX replaces bare `W` (week number) before `WN`/`WL`, so protect weekday tokens.
387
+ const placeholders = [];
388
+ const ph = (text) => {
389
+ const i = placeholders.length;
390
+ placeholders.push(text);
391
+ return `\uE100${i}\uE101`;
392
+ };
393
+ const safe = expanded
394
+ .replace(/WN/g, () => ph(this.weekDayName(date)))
395
+ .replace(/wn/g, () => ph(this.weekDayName(date)))
396
+ .replace(/WL/g, () => ph(this.weekDayLetter(date)))
397
+ .replace(/wl/g, () => ph(this.weekDayLetter(date)));
398
+ let output = date.format(safe);
399
+ placeholders.forEach((text, i) => {
400
+ output = output.replace(new RegExp(`\uE100${i}\uE101`, 'g'), () => text);
401
+ });
402
+ return output;
403
+ };
404
+ /**
405
+ * Localizes digits for field display (Western -> Persian; `,` -> `،`).
406
+ * @param numberToFormat - Digit string from MUI fields.
407
+ * @returns String with Persian numerals.
408
+ */
409
+ this.formatNumber = (numberToFormat) => typescript_1.default.toPersian(numberToFormat).replace(/,/g, '،');
410
+ /**
411
+ * @param value - First date (or `null`).
412
+ * @param comparing - Second date (or `null`).
413
+ * @returns `true` if both null or same epoch ms.
414
+ */
415
+ this.isEqual = (value, comparing) => {
416
+ if (value === null && comparing === null)
417
+ return true;
418
+ if (value === null || comparing === null)
419
+ return false;
420
+ return this.asJalaliX(value).getTime() === this.asJalaliX(comparing).getTime();
421
+ };
422
+ /** @returns Whether both dates share the same Jalali year. */
423
+ this.isSameYear = (value, comparing) => {
424
+ return this.getYear(value) === this.getYear(comparing);
425
+ };
426
+ /** @returns Whether both dates share the same Jalali year and month. */
427
+ this.isSameMonth = (value, comparing) => {
428
+ return this.isSameYear(value, comparing) && this.getMonth(value) === this.getMonth(comparing);
429
+ };
430
+ /** @returns Whether both dates fall on the same Jalali calendar day. */
431
+ this.isSameDay = (value, comparing) => {
432
+ return this.isSameMonth(value, comparing) && this.getDate(value) === this.getDate(comparing);
433
+ };
434
+ /** @returns Whether both dates share the same day and hour. */
435
+ this.isSameHour = (value, comparing) => {
436
+ return this.isSameDay(value, comparing) && this.getHours(value) === this.getHours(comparing);
437
+ };
438
+ /** @returns Whether `value` is strictly after `comparing` by timestamp. */
439
+ this.isAfter = (value, comparing) => {
440
+ return this.asJalaliX(value).getTime() > this.asJalaliX(comparing).getTime();
441
+ };
442
+ /** @returns Whether `value`'s Jalali year is after `comparing`'s. */
443
+ this.isAfterYear = (value, comparing) => {
444
+ return this.getYear(value) > this.getYear(comparing);
445
+ };
446
+ /** @returns Whether `value`'s calendar day is after `comparing`'s. */
447
+ this.isAfterDay = (value, comparing) => {
448
+ return !this.isSameDay(value, comparing) && this.isAfter(this.startOfDay(value), this.startOfDay(comparing));
449
+ };
450
+ /** @returns Whether `value` is strictly before `comparing` by timestamp. */
451
+ this.isBefore = (value, comparing) => {
452
+ return this.asJalaliX(value).getTime() < this.asJalaliX(comparing).getTime();
453
+ };
454
+ /** @returns Whether `value`'s Jalali year is before `comparing`'s. */
455
+ this.isBeforeYear = (value, comparing) => {
456
+ return this.getYear(value) < this.getYear(comparing);
457
+ };
458
+ /** @returns Whether `value`'s calendar day is before `comparing`'s. */
459
+ this.isBeforeDay = (value, comparing) => {
460
+ return !this.isSameDay(value, comparing) && this.isBefore(this.startOfDay(value), this.startOfDay(comparing));
461
+ };
462
+ /**
463
+ * @param value - Date to test.
464
+ * @param range - Inclusive `[start, end]` bounds.
465
+ * @returns Whether `value` lies within the range by timestamp.
466
+ */
467
+ this.isWithinRange = (value, range) => {
468
+ const time = this.asJalaliX(value).getTime();
469
+ return time >= this.asJalaliX(range[0]).getTime() && time <= this.asJalaliX(range[1]).getTime();
470
+ };
471
+ /**
472
+ * @param value - Reference date.
473
+ * @returns Start of that Jalali year (Farvardin 1, 00:00:00.000).
474
+ */
475
+ this.startOfYear = (value) => {
476
+ return this.setJalaliParts(value, this.getYear(value), 0, 1, 0, 0, 0, 0);
477
+ };
478
+ /**
479
+ * @param value - Reference date.
480
+ * @returns Start of that Jalali month (day 1, 00:00:00.000).
481
+ */
482
+ this.startOfMonth = (value) => {
483
+ return this.setJalaliParts(value, this.getYear(value), this.getMonth(value), 1, 0, 0, 0, 0);
484
+ };
485
+ /**
486
+ * Persian week starts on Saturday.
487
+ * @param value - Reference date.
488
+ * @returns Start of that week (Saturday 00:00:00.000).
489
+ */
490
+ this.startOfWeek = (value) => {
491
+ const dayOfWeek = this.getDayOfWeek(value);
492
+ return this.startOfDay(this.addDays(value, 1 - dayOfWeek));
493
+ };
494
+ /**
495
+ * @param value - Reference date.
496
+ * @returns Same calendar day at 00:00:00.000 local wall time.
497
+ */
498
+ this.startOfDay = (value) => {
499
+ return this.withWallTime(value, 0, 0, 0, 0);
500
+ };
501
+ /**
502
+ * @param value - Reference date.
503
+ * @returns End of that Jalali year (last moment of Esfand).
504
+ */
505
+ this.endOfYear = (value) => {
506
+ return this.endOfMonth(this.setJalaliParts(value, this.getYear(value), 11, 1, 12, 0, 0, 0));
507
+ };
508
+ /**
509
+ * @param value - Reference date.
510
+ * @returns End of that Jalali month (23:59:59.999 on the last day).
511
+ */
512
+ this.endOfMonth = (value) => {
513
+ const days = this.getDaysInMonth(value);
514
+ return this.setJalaliParts(value, this.getYear(value), this.getMonth(value), days, 23, 59, 59, 999);
515
+ };
516
+ /**
517
+ * Persian week ends on Friday.
518
+ * @param value - Reference date.
519
+ * @returns End of that week (Friday 23:59:59.999).
520
+ */
521
+ this.endOfWeek = (value) => {
522
+ const dayOfWeek = this.getDayOfWeek(value);
523
+ return this.endOfDay(this.addDays(value, 7 - dayOfWeek));
524
+ };
525
+ /**
526
+ * @param value - Reference date.
527
+ * @returns Same calendar day at 23:59:59.999 local wall time.
528
+ */
529
+ this.endOfDay = (value) => {
530
+ return this.withWallTime(value, 23, 59, 59, 999);
531
+ };
532
+ /**
533
+ * @param value - Source date.
534
+ * @param amount - Years to add (may be negative).
535
+ * @returns New JalaliX with year adjusted.
536
+ */
537
+ this.addYears = (value, amount) => this.setYear(value, this.getYear(value) + amount);
538
+ /**
539
+ * Adds months with day clamping to the target month length.
540
+ * @param value - Source date.
541
+ * @param amount - Months to add (may be negative).
542
+ * @returns New JalaliX with month adjusted.
543
+ */
544
+ this.addMonths = (value, amount) => {
545
+ const current = this.asJalaliX(value);
546
+ const totalMonths = this.getYear(current) * 12 + this.getMonth(current) + amount;
547
+ const year = Math.floor(totalMonths / 12);
548
+ const month = ((totalMonths % 12) + 12) % 12;
549
+ const maxDay = this.getDaysInMonth(this.setJalaliParts(current, year, month, 1, 12));
550
+ const day = Math.min(this.getDate(current), maxDay);
551
+ return this.setJalaliParts(current, year, month, day, current.getHours(), current.getMinutes(), current.getSeconds(), current.getMilliseconds());
552
+ };
553
+ /**
554
+ * @param value - Source date.
555
+ * @param amount - Weeks to add (may be negative).
556
+ * @returns New JalaliX with days shifted by `amount * 7`.
557
+ */
558
+ this.addWeeks = (value, amount) => this.addDays(value, amount * 7);
559
+ /**
560
+ * Adds calendar days (wall time preserved via JalaliX timezone-aware setters).
561
+ * @param value - Source date.
562
+ * @param amount - Days to add (may be negative).
563
+ * @returns New JalaliX with day adjusted.
564
+ */
565
+ this.addDays = (value, amount) => {
566
+ return this.asJalaliX(value).clone().addDays(amount);
567
+ };
568
+ /**
569
+ * @param value - Source date.
570
+ * @param amount - Hours to add (may be negative).
571
+ * @returns New JalaliX with timestamp shifted by hours.
572
+ */
573
+ this.addHours = (value, amount) => {
574
+ const result = this.asJalaliX(value).clone();
575
+ result.setTime(result.getTime() + amount * 3600000);
576
+ return result;
577
+ };
578
+ /**
579
+ * @param value - Source date.
580
+ * @param amount - Minutes to add (may be negative).
581
+ * @returns New JalaliX with timestamp shifted by minutes.
582
+ */
583
+ this.addMinutes = (value, amount) => {
584
+ const result = this.asJalaliX(value).clone();
585
+ result.setTime(result.getTime() + amount * 60000);
586
+ return result;
587
+ };
588
+ /**
589
+ * @param value - Source date.
590
+ * @param amount - Seconds to add (may be negative).
591
+ * @returns New JalaliX with timestamp shifted by seconds.
592
+ */
593
+ this.addSeconds = (value, amount) => {
594
+ const result = this.asJalaliX(value).clone();
595
+ result.setTime(result.getTime() + amount * 1000);
596
+ return result;
597
+ };
598
+ /** @returns Jalali year. */
599
+ this.getYear = (value) => this.asJalaliX(value).getFullYear();
600
+ /**
601
+ * 0-based Jalali month (0 = Farvardin).
602
+ * @returns Month index 0–11.
603
+ */
604
+ this.getMonth = (value) => this.asJalaliX(value).getMonth() - 1;
605
+ /** @returns Day of month (1–31). */
606
+ this.getDate = (value) => this.asJalaliX(value).getDate();
607
+ /** @returns Hours (0–23). */
608
+ this.getHours = (value) => this.asJalaliX(value).getHours();
609
+ /** @returns Minutes (0–59). */
610
+ this.getMinutes = (value) => this.asJalaliX(value).getMinutes();
611
+ /** @returns Seconds (0–59). */
612
+ this.getSeconds = (value) => this.asJalaliX(value).getSeconds();
613
+ /** @returns Milliseconds (0–999). */
614
+ this.getMilliseconds = (value) => this.asJalaliX(value).getMilliseconds();
615
+ /**
616
+ * @param value - Source date.
617
+ * @param year - Jalali year to set.
618
+ * @returns New JalaliX with year replaced (month/day/time preserved).
619
+ */
620
+ this.setYear = (value, year) => {
621
+ const current = this.asJalaliX(value);
622
+ return this.setJalaliParts(current, year, current.getMonth() - 1, current.getDate(), current.getHours(), current.getMinutes(), current.getSeconds(), current.getMilliseconds());
623
+ };
624
+ /**
625
+ * @param value - Source date.
626
+ * @param month - 0-based Jalali month (0 = Farvardin).
627
+ * @returns New JalaliX with month replaced.
628
+ */
629
+ this.setMonth = (value, month) => {
630
+ const current = this.asJalaliX(value);
631
+ return this.setJalaliParts(current, current.getFullYear(), month, current.getDate(), current.getHours(), current.getMinutes(), current.getSeconds(), current.getMilliseconds());
632
+ };
633
+ /**
634
+ * @param value - Source date.
635
+ * @param date - Day of month to set.
636
+ * @returns New JalaliX with day replaced.
637
+ */
638
+ this.setDate = (value, date) => {
639
+ const current = this.asJalaliX(value);
640
+ return this.setJalaliParts(current, current.getFullYear(), current.getMonth() - 1, date, current.getHours(), current.getMinutes(), current.getSeconds(), current.getMilliseconds());
641
+ };
642
+ /**
643
+ * @param value - Source date.
644
+ * @param hours - Hours to set (0–23).
645
+ * @returns New JalaliX with hours replaced.
646
+ */
647
+ this.setHours = (value, hours) => this.withWallTime(value, hours);
648
+ /**
649
+ * @param value - Source date.
650
+ * @param minutes - Minutes to set.
651
+ * @returns New JalaliX with minutes replaced.
652
+ */
653
+ this.setMinutes = (value, minutes) => {
654
+ const current = this.asJalaliX(value);
655
+ return this.withWallTime(current, current.getHours(), minutes);
656
+ };
657
+ /**
658
+ * @param value - Source date.
659
+ * @param seconds - Seconds to set.
660
+ * @returns New JalaliX with seconds replaced.
661
+ */
662
+ this.setSeconds = (value, seconds) => {
663
+ const current = this.asJalaliX(value);
664
+ return this.withWallTime(current, current.getHours(), current.getMinutes(), seconds);
665
+ };
666
+ /**
667
+ * @param value - Source date.
668
+ * @param milliseconds - Milliseconds to set.
669
+ * @returns New JalaliX with milliseconds replaced.
670
+ */
671
+ this.setMilliseconds = (value, milliseconds) => {
672
+ const current = this.asJalaliX(value);
673
+ return this.withWallTime(current, current.getHours(), current.getMinutes(), current.getSeconds(), milliseconds);
674
+ };
675
+ /**
676
+ * Days in the Jalali month of `value` (31 / 30 / 29–30 for Esfand).
677
+ * @param value - Reference date.
678
+ * @returns Day count for that month.
679
+ */
680
+ this.getDaysInMonth = (value) => {
681
+ const month = this.getMonth(value); // 0-based
682
+ if (month <= 5)
683
+ return 31;
684
+ if (month <= 10)
685
+ return 30;
686
+ // Esfand: leap years have 30 days
687
+ const probe = this.asJalaliX(value).clone();
688
+ probe.setFullYear(this.getYear(value), 11, 30);
689
+ return probe.getMonth() === 12 && probe.getDate() === 30 ? 30 : 29;
690
+ };
691
+ /**
692
+ * Builds the month grid as weeks (Saturday-start) for calendar UIs.
693
+ * @param value - Any date in the target month.
694
+ * @returns Array of weeks; each week is seven {@link JalaliX} days.
695
+ */
696
+ this.getWeekArray = (value) => {
697
+ const start = this.startOfWeek(this.startOfMonth(value));
698
+ const end = this.endOfWeek(this.endOfMonth(value));
699
+ let count = 0;
700
+ let current = start;
701
+ const nestedWeeks = [];
702
+ while (!this.isAfter(current, end)) {
703
+ const weekNumber = Math.floor(count / 7);
704
+ nestedWeeks[weekNumber] = nestedWeeks[weekNumber] || [];
705
+ nestedWeeks[weekNumber].push(current);
706
+ current = this.addDays(current, 1);
707
+ count += 1;
708
+ }
709
+ return nestedWeeks;
710
+ };
711
+ /**
712
+ * @param value - Reference date.
713
+ * @returns Jalali week-of-year (1–53).
714
+ */
715
+ this.getWeekNumber = (value) => this.asJalaliX(value).getWeekOfYear();
716
+ /**
717
+ * 1-based day of week with Saturday as the first day (Persian week).
718
+ * @param value - Reference date.
719
+ * @returns 1 (Saturday) … 7 (Friday).
720
+ */
721
+ this.getDayOfWeek = (value) => {
722
+ const day = this.asJalaliX(value).getDay();
723
+ return day === 0 ? 7 : day;
724
+ };
725
+ /**
726
+ * Lists year-start dates from `start` through `end` (inclusive range walk).
727
+ * @param range - `[start, end]` picker dates.
728
+ * @returns One {@link JalaliX} per year at start-of-year.
729
+ */
730
+ this.getYearRange = ([start, end]) => {
731
+ const startDate = this.startOfYear(start);
732
+ const endDate = this.endOfYear(end);
733
+ const years = [];
734
+ let current = startDate;
735
+ while (this.isBefore(current, endDate)) {
736
+ years.push(current);
737
+ current = this.addYears(current, 1);
738
+ }
739
+ return years;
740
+ };
741
+ /**
742
+ * Optional helper for older pickers / custom UIs.
743
+ * @param ampm - `'am'` or `'pm'`.
744
+ * @returns Persian meridiem (`ق.ظ.` / `ب.ظ.`).
745
+ */
746
+ this.getMeridiemText = (ampm) => (ampm === 'am' ? 'ق.ظ.' : 'ب.ظ.');
747
+ if (locale !== undefined)
748
+ this.locale = locale;
749
+ this.formats = Object.assign(Object.assign({}, defaultFormats), formats);
750
+ }
751
+ }
752
+ exports.AdapterJalaliX = AdapterJalaliX;
753
+ function escapeRegExp(value) {
754
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
755
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Release Notes
2
+ All notable changes to this package will be documented in this file.
3
+
4
+ ## 0.2.0
5
+ - Support `@mui/x-date-pickers` **v9** (peer range: ^6 || ^7 || ^8 || ^9)
6
+ - Dev/types aligned with latest MUI X Date Pickers adapter interface
7
+ - Wired timezone APIs to `@jalalix/typescript` ^0.8.0 instant-preserving IANA model
8
+ - Simplified day/time helpers (no `Date.prototype` midnight workarounds)
9
+ - Documented `DateTimeField` `timezone` usage
10
+ - Initial `AdapterJalaliX` for MUI X Date Pickers (`@mui/x-date-pickers` ^6 || ^7 || ^8)
11
+ - Jalali calendar via `@jalalix/typescript` (^0.7.0)
12
+ - Default keyboard formats: `YYYY/MM/DD`, `YYYY/MM/DD HH:mm`
13
+ - Persian month/weekday names and optional Persian digits via `formatNumber`
14
+
15
+ ## 0.1.0
16
+ - Initial commit
package/README.md CHANGED
@@ -1 +1,61 @@
1
- # Jalali X - MUI
1
+ # JalaliX
2
+
3
+ Jalali (Persian/Shamsi) adapter for [MUI X Date Pickers](https://mui.com/x/react-date-pickers/), powered by [`@jalalix/typescript`](https://www.npmjs.com/package/@jalalix/typescript).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @jalalix/mui @jalalix/typescript @mui/x-date-pickers
9
+ ```
10
+
11
+ Peer dependencies: `react` (^17 || ^18 || ^19) and `@mui/x-date-pickers` (^6 || ^7 || ^8 || ^9).
12
+
13
+ ## Usage
14
+
15
+ ```tsx
16
+ import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'
17
+ import { DateTimeField } from '@mui/x-date-pickers/DateTimeField'
18
+ import { DatePicker } from '@mui/x-date-pickers/DatePicker'
19
+ import { AdapterJalaliX, JalaliX } from '@jalalix/mui'
20
+
21
+ export function App() {
22
+ return (
23
+ <LocalizationProvider dateAdapter={AdapterJalaliX} adapterLocale="fa">
24
+ <DateTimeField defaultValue={new JalaliX()} />
25
+ <DatePicker label="تاریخ" />
26
+ </LocalizationProvider>
27
+ )
28
+ }
29
+ ```
30
+
31
+ Keyboard formats default to Jalali-friendly patterns such as `YYYY/MM/DD` and `YYYY/MM/DD HH:mm`. Month names and weekdays render in Persian via JalaliX.
32
+
33
+ ## Timezone
34
+
35
+ `AdapterJalaliX` is timezone-compatible (`isTimezoneCompatible = true`). It delegates to `@jalalix/typescript` **instant-preserving** IANA zones:
36
+
37
+ - `getTimezone(value)` / `setTimezone(value, tz)` use JalaliX
38
+ - Aliases: `default` / `local` / `system` -> host IANA; `utc` / `gmt` -> `UTC`
39
+ - Changing timezone keeps the same Unix instant; wall/Jalali display updates
40
+
41
+ ```tsx
42
+ <LocalizationProvider dateAdapter={AdapterJalaliX}>
43
+ <DateTimeField timezone="Asia/Tehran" />
44
+ </LocalizationProvider>
45
+ ```
46
+
47
+ Requires `@jalalix/typescript` ^0.8.0. Compatible with MUI X Date Pickers **v6–v9** (tested against v9).
48
+
49
+ ## API
50
+
51
+ ```ts
52
+ import { AdapterJalaliX, JalaliX } from '@jalalix/mui'
53
+ import type { JalaliDate, JalaliOptions, JalaliFormatOptions } from '@jalalix/mui'
54
+ ```
55
+
56
+ - `AdapterJalaliX` — `MuiPickersAdapter` implementation (`lib: 'jalalix'`)
57
+ - `JalaliX` — re-export of `@jalalix/typescript`
58
+
59
+ ## License
60
+
61
+ MIT
package/index.d.ts CHANGED
@@ -0,0 +1,4 @@
1
+ /** MUI X Date Pickers adapter for JalaliX — see {@link AdapterJalaliX}. */
2
+ export { AdapterJalaliX } from './AdapterJalaliX';
3
+ export { default as JalaliX } from '@jalalix/typescript';
4
+ export type { JalaliOptions, JalaliFormatOptions, JalaliDate } from './index.types';
package/index.js CHANGED
@@ -1 +1,11 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.JalaliX = exports.AdapterJalaliX = void 0;
7
+ /** MUI X Date Pickers adapter for JalaliX — see {@link AdapterJalaliX}. */
8
+ var AdapterJalaliX_1 = require("./AdapterJalaliX");
9
+ Object.defineProperty(exports, "AdapterJalaliX", { enumerable: true, get: function () { return AdapterJalaliX_1.AdapterJalaliX; } });
10
+ var typescript_1 = require("@jalalix/typescript");
11
+ Object.defineProperty(exports, "JalaliX", { enumerable: true, get: function () { return __importDefault(typescript_1).default; } });
package/index.types.d.ts CHANGED
@@ -0,0 +1,17 @@
1
+ export type JalaliDate = {
2
+ year: number;
3
+ month: number;
4
+ day: number;
5
+ hours: number;
6
+ min: number;
7
+ sec: number;
8
+ ms: number;
9
+ isLeapYear: boolean;
10
+ };
11
+ export type JalaliOptions = {
12
+ timezone?: string;
13
+ };
14
+ export type JalaliFormatOptions = {
15
+ calendar?: 'jalali' | 'gregorian';
16
+ persianDigits?: boolean;
17
+ };
package/index.types.js CHANGED
@@ -1 +1,2 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@jalalix/mui",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "main": "index.js",
6
6
  "module": "index.js",
7
7
  "types": "index.d.ts",
8
- "description": "",
8
+ "description": "Jalali adapter for MUI X Date Pickers using @jalalix/typescript",
9
9
  "author": "Hossein Maklavani <hos.mohamadi@gmail.com> (https://maklavani.dev)",
10
10
  "contributors": [
11
11
  {
@@ -14,14 +14,11 @@
14
14
  "url": "https://maklavani.dev"
15
15
  }
16
16
  ],
17
- "scripts": {
18
- "dev": "nodemon src/index.ts",
19
- "build": "npm run remove && tsc --build tsconfig.json",
20
- "remove": "ts-node scripts/remove.mjs"
21
- },
22
17
  "license": "MIT",
23
18
  "keywords": [
24
19
  "mui",
20
+ "mui-x",
21
+ "date-pickers",
25
22
  "jalali",
26
23
  "jalalix",
27
24
  "persian",
@@ -29,7 +26,8 @@
29
26
  "khorshidi",
30
27
  "calendar",
31
28
  "date",
32
- "typescript"
29
+ "typescript",
30
+ "react"
33
31
  ],
34
32
  "repository": {
35
33
  "type": "git",
@@ -45,21 +43,54 @@
45
43
  "url": "https://github.com/jalalix/mui/issues"
46
44
  },
47
45
  "homepage": "https://jalalix.com",
46
+ "scripts": {
47
+ "dev": "nodemon --exec \"node --experimental-strip-types\" src/index.ts",
48
+ "build": "npm run remove && tsc -p tsconfig.json",
49
+ "remove": "node scripts/remove.mjs",
50
+ "prepublishOnly": "npm run build"
51
+ },
52
+ "dependencies": {
53
+ "@jalalix/typescript": "^0.8.0"
54
+ },
55
+ "peerDependencies": {
56
+ "@mui/x-date-pickers": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0",
57
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "react": {
61
+ "optional": true
62
+ }
63
+ },
64
+ "devDependencies": {
65
+ "@mui/x-date-pickers": "^9.18.0",
66
+ "@types/react": "^19.0.0",
67
+ "nodemon": "^3.1.14",
68
+ "react": "^19.0.0",
69
+ "react-dom": "^19.0.0",
70
+ "rimraf": "^6.1.4",
71
+ "typescript": "^7.0.2"
72
+ },
73
+ "engines": {
74
+ "node": ">=18"
75
+ },
76
+ "exports": {
77
+ ".": {
78
+ "types": "./index.d.ts",
79
+ "require": "./index.js",
80
+ "import": "./index.js",
81
+ "default": "./index.js"
82
+ }
83
+ },
84
+ "sideEffects": false,
48
85
  "files": [
86
+ "AdapterJalaliX.d.ts",
87
+ "AdapterJalaliX.js",
49
88
  "index.d.ts",
50
89
  "index.js",
51
90
  "index.types.d.ts",
52
91
  "index.types.js",
53
92
  "LICENSE",
54
- "README.md"
55
- ],
56
- "dependencies": {
57
- "@jalalix/typescript": "^0.3.0"
58
- },
59
- "devDependencies": {
60
- "nodemon": "^3.1.10",
61
- "rimraf": "^6.0.1",
62
- "ts-node": "^10.9.2",
63
- "typescript": "^5.8.3"
64
- }
93
+ "README.md",
94
+ "CHANGELOG.md"
95
+ ]
65
96
  }