mui-temporal-pickers 1.0.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/LICENSE +22 -0
- package/README.md +130 -0
- package/dist/adapters/adapter-formats.d.ts +2 -0
- package/dist/adapters/adapter-formats.js +25 -0
- package/dist/adapters/base.d.ts +97 -0
- package/dist/adapters/base.js +216 -0
- package/dist/adapters/operations.d.ts +70 -0
- package/dist/adapters/operations.js +120 -0
- package/dist/adapters/plain-date-time.d.ts +12 -0
- package/dist/adapters/plain-date-time.js +46 -0
- package/dist/adapters/plain-date.d.ts +6 -0
- package/dist/adapters/plain-date.js +46 -0
- package/dist/adapters/plain-time.d.ts +6 -0
- package/dist/adapters/plain-time.js +46 -0
- package/dist/adapters/zoned-date-time.d.ts +12 -0
- package/dist/adapters/zoned-date-time.js +55 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/locale/format/formatter.d.ts +12 -0
- package/dist/locale/format/formatter.js +377 -0
- package/dist/locale/format/tokenizer.d.ts +9 -0
- package/dist/locale/format/tokenizer.js +83 -0
- package/dist/locale/format/tokens.d.ts +8 -0
- package/dist/locale/format/tokens.js +70 -0
- package/dist/locale/specs.d.ts +9 -0
- package/dist/locale/specs.js +30 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +5 -0
- package/dist/providers/plain-date-time.d.ts +5 -0
- package/dist/providers/plain-date-time.js +8 -0
- package/dist/providers/plain-date.d.ts +5 -0
- package/dist/providers/plain-date.js +8 -0
- package/dist/providers/plain-time.d.ts +5 -0
- package/dist/providers/plain-time.js +8 -0
- package/dist/providers/root.d.ts +13 -0
- package/dist/providers/root.js +7 -0
- package/dist/providers/zoned-date-time.d.ts +5 -0
- package/dist/providers/zoned-date-time.js +8 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2025, Ben Scholzen 'DASPRiD'
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
list of conditions and the following disclaimer.
|
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
and/or other materials provided with the distribution.
|
|
12
|
+
|
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
15
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
16
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
17
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
18
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
19
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
20
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
21
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
22
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Temporal Pickers for MUI X
|
|
2
|
+
|
|
3
|
+
[](https://github.com/dasprid/mui-temporal-pickers/actions/workflows/release.yml)
|
|
4
|
+
|
|
5
|
+
A small utility library providing [Temporal](https://tc39.es/proposal-temporal/) support for
|
|
6
|
+
[MUI X Date and Time Pickers](https://mui.com/x/react-date-pickers/).
|
|
7
|
+
|
|
8
|
+
This package bridges the gap between MUI X and the TC39 Temporal API by introducing context-based providers that adapt
|
|
9
|
+
pickers to work seamlessly with `Temporal.PlainDate`, `Temporal.PlainTime`, `Temporal.PlainDateTime`, and
|
|
10
|
+
`Temporal.ZonedDateTime`.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- Plug-and-play support for Temporal types in MUI X pickers
|
|
15
|
+
- Supports all major temporal types: `PlainDate`, `PlainTime`, `PlainDateTime`, `ZonedDateTime`
|
|
16
|
+
- Global locale, format, and text customization via root provider
|
|
17
|
+
- Fully tree-shakable and composable
|
|
18
|
+
- Built-in localization fallback to `en-US`
|
|
19
|
+
|
|
20
|
+
## Getting Started
|
|
21
|
+
|
|
22
|
+
Install via your favorite package manager:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install mui-temporal-pickers
|
|
26
|
+
# or
|
|
27
|
+
pnpm add mui-temporal-pickers
|
|
28
|
+
# or
|
|
29
|
+
yarn add mui-temporal-pickers
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Try the demo
|
|
33
|
+
|
|
34
|
+
This repository includes a working demo app. You can run it locally using:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm demo
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This will launch a Vite-powered playground with all picker types wired up.
|
|
41
|
+
|
|
42
|
+
## Using the Temporal Polyfill
|
|
43
|
+
|
|
44
|
+
If you want to use the [Temporal API](https://tc39.es/proposal-temporal/) in environments where it is not yet natively
|
|
45
|
+
supported, you can include the [temporal-polyfill](https://www.npmjs.com/package/temporal-polyfill).
|
|
46
|
+
|
|
47
|
+
Once you have it installed, you can enable it:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
if (typeof Temporal === "undefined") {
|
|
51
|
+
await import("temporal-polyfill/global");
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This will globally patch the environment, adding the Temporal API on `globalThis.Temporal`.
|
|
56
|
+
|
|
57
|
+
### TypeScript support for Temporal
|
|
58
|
+
|
|
59
|
+
To get proper TypeScript typings for the polyfill (and the Temporal API in general), you need to install the
|
|
60
|
+
[temporal-spec](https://www.npmjs.com/package/temporal-spec) types package.
|
|
61
|
+
|
|
62
|
+
Then create a `temporal.d.ts` file in your project (e.g., in your `src` folder or your `types` folder) with the
|
|
63
|
+
following content:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import "temporal-spec/global";
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This will augment the TypeScript global scope with Temporal API types, so your editor and compiler understand `Temporal`
|
|
70
|
+
properly.
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
### 1. (Optional) Wrap your app with TemporalRootProvider
|
|
75
|
+
|
|
76
|
+
This allows you to configure global `locale`, `dateFormats`, and `localeText`, similar to MUI's `LocalizationProvider`.
|
|
77
|
+
You can omit this if you don't need customization.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { TemporalRootProvider } from 'mui-temporal-pickers';
|
|
81
|
+
|
|
82
|
+
<TemporalRootProvider locale="de-DE">
|
|
83
|
+
<App />
|
|
84
|
+
</TemporalRootProvider>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 2. Wrap pickers with the appropriate Temporal provider
|
|
88
|
+
|
|
89
|
+
You must wrap each picker (or group of same-kind pickers) in a provider that matches the Temporal type being used.
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { TemporalPlainDateProvider } from 'mui-temporal-pickers';
|
|
93
|
+
import { DatePicker, TimePicker, DateTimePicker } from '@mui/x-date-pickers';
|
|
94
|
+
|
|
95
|
+
// Example: PlainDate
|
|
96
|
+
<TemporalPlainDateProvider>
|
|
97
|
+
<DatePicker
|
|
98
|
+
label="PlainDate"
|
|
99
|
+
value={Temporal.PlainDate.from('2025-06-19')}
|
|
100
|
+
onChange={(val) => console.log(val?.toString())}
|
|
101
|
+
/>
|
|
102
|
+
</TemporalPlainDateProvider>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Pickers **must be wrapped** in the matching provider to work correctly. Mixed types or mismatched types will result in
|
|
106
|
+
runtime errors.
|
|
107
|
+
|
|
108
|
+
## Caveats
|
|
109
|
+
|
|
110
|
+
### Limited formatting token support
|
|
111
|
+
|
|
112
|
+
Due to how `Intl.DateTimeFormat` works, text field formatting only supports numeric tokens (like `yyyy`, `MM`, `dd`,
|
|
113
|
+
`HH`, `mm`). Literal characters (like slashes or dashes) are fine.
|
|
114
|
+
|
|
115
|
+
The only non-numeric token allowed is **AM/PM**, which is not currently localized and always displays as `"AM"` /
|
|
116
|
+
`"PM"`.
|
|
117
|
+
|
|
118
|
+
### No runtime validation of input types
|
|
119
|
+
|
|
120
|
+
For performance reasons, this library **does not validate** that the value passed to a picker matches the expected
|
|
121
|
+
Temporal type. Passing an incorrect type (e.g., `PlainDate` to a time picker) will lead to downstream errors from MUI
|
|
122
|
+
or the Temporal API.
|
|
123
|
+
|
|
124
|
+
Use with care and ensure values match the context's expectation.
|
|
125
|
+
|
|
126
|
+
## Package Goals
|
|
127
|
+
|
|
128
|
+
- Stay minimal: no runtime conversions or unnecessary wrappers
|
|
129
|
+
- Keep compatibility with MUI X moving forward
|
|
130
|
+
- Encourage adoption of Temporal without losing MUI's power
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const defaultFormats = {
|
|
2
|
+
year: "yyyy",
|
|
3
|
+
month: "MMMM",
|
|
4
|
+
monthShort: "MMM",
|
|
5
|
+
dayOfMonth: "d",
|
|
6
|
+
// Full day of the month format (i.e. 3rd) is not supported
|
|
7
|
+
// Falling back to regular format
|
|
8
|
+
dayOfMonthFull: "d",
|
|
9
|
+
weekday: "cccc",
|
|
10
|
+
weekdayShort: "ccc",
|
|
11
|
+
hours24h: "HH",
|
|
12
|
+
hours12h: "hh",
|
|
13
|
+
meridiem: "a",
|
|
14
|
+
minutes: "mm",
|
|
15
|
+
seconds: "ss",
|
|
16
|
+
fullDate: "lfd",
|
|
17
|
+
keyboardDate: "lkd",
|
|
18
|
+
shortDate: "lsd",
|
|
19
|
+
normalDate: "lnd",
|
|
20
|
+
normalDateWithWeekday: "lndw",
|
|
21
|
+
fullTime12h: "lfta",
|
|
22
|
+
fullTime24h: "lftd",
|
|
23
|
+
keyboardDateTime12h: "lkdta",
|
|
24
|
+
keyboardDateTime24h: "lkdtd",
|
|
25
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { AdapterFormats, AdapterOptions, DateBuilderReturnType, FieldFormatTokenMap, MuiPickersAdapter, PickersTimezone } from "@mui/x-date-pickers";
|
|
2
|
+
import { LocaleSpecs } from "../locale/specs.js";
|
|
3
|
+
import { type AdapterComparisonOperations, type AdapterConversionOperations, type AdapterDateOperations, type AdapterTimeOperations, type AdapterTimezoneOperations } from "./operations.js";
|
|
4
|
+
export type ValidTemporal = Temporal.PlainTime | Temporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime;
|
|
5
|
+
export type ValidTimeTemporal = Temporal.PlainTime | Temporal.PlainDateTime | Temporal.ZonedDateTime;
|
|
6
|
+
export type ValidDateTemporal = Temporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime;
|
|
7
|
+
declare module "@mui/x-date-pickers/models" {
|
|
8
|
+
interface PickerValidDateLookup {
|
|
9
|
+
temporal: ValidTemporal;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
type AdapterTemporalOptions<T extends ValidTemporal> = {
|
|
13
|
+
conversionOperations: AdapterConversionOperations<T>;
|
|
14
|
+
comparisonOperations: AdapterComparisonOperations<T>;
|
|
15
|
+
dateOperations?: AdapterDateOperations<T>;
|
|
16
|
+
timeOperations?: AdapterTimeOperations<T>;
|
|
17
|
+
timezoneOperations?: AdapterTimezoneOperations<T>;
|
|
18
|
+
};
|
|
19
|
+
export declare abstract class AdapterTemporalBase<TTemporal extends ValidTemporal> implements MuiPickersAdapter<Intl.Locale> {
|
|
20
|
+
readonly isMUIAdapter = true;
|
|
21
|
+
readonly isTimezoneCompatible = true;
|
|
22
|
+
readonly lib = "temporal";
|
|
23
|
+
readonly locale: Intl.Locale;
|
|
24
|
+
readonly formats: AdapterFormats;
|
|
25
|
+
readonly escapedCharacters: {
|
|
26
|
+
start: string;
|
|
27
|
+
end: string;
|
|
28
|
+
};
|
|
29
|
+
abstract readonly formatTokenMap: FieldFormatTokenMap;
|
|
30
|
+
protected readonly localeSpecs: LocaleSpecs;
|
|
31
|
+
protected readonly conversionOperations: AdapterConversionOperations<TTemporal>;
|
|
32
|
+
protected readonly comparisonOperations: AdapterComparisonOperations<TTemporal>;
|
|
33
|
+
protected readonly dateOperations: AdapterDateOperations<TTemporal>;
|
|
34
|
+
protected readonly timeOperations: AdapterTimeOperations<TTemporal>;
|
|
35
|
+
protected readonly timezoneOperations: AdapterTimezoneOperations<TTemporal>;
|
|
36
|
+
protected constructor({ locale, formats, conversionOperations, comparisonOperations, dateOperations, timeOperations, timezoneOperations, }: AdapterOptions<Intl.Locale | string, never> & AdapterTemporalOptions<TTemporal>);
|
|
37
|
+
date<T extends string | null | undefined>(value?: T, timezone?: PickersTimezone): DateBuilderReturnType<T>;
|
|
38
|
+
toJsDate(value: TTemporal): Date;
|
|
39
|
+
parse(value: string, format: string): TTemporal | null;
|
|
40
|
+
isEqual(value: TTemporal | null, comparing: TTemporal | null): boolean;
|
|
41
|
+
isSameYear(value: TTemporal, comparing: TTemporal): boolean;
|
|
42
|
+
isSameMonth(value: TTemporal, comparing: TTemporal): boolean;
|
|
43
|
+
isSameDay(value: TTemporal, comparing: TTemporal): boolean;
|
|
44
|
+
isSameHour(value: TTemporal, comparing: TTemporal): boolean;
|
|
45
|
+
isAfter(value: TTemporal, comparing: TTemporal): boolean;
|
|
46
|
+
isAfterYear(value: TTemporal, comparing: TTemporal): boolean;
|
|
47
|
+
isAfterDay(value: TTemporal, comparing: TTemporal): boolean;
|
|
48
|
+
isBefore(value: TTemporal, comparing: TTemporal): boolean;
|
|
49
|
+
isBeforeYear(value: TTemporal, comparing: TTemporal): boolean;
|
|
50
|
+
isBeforeDay(value: TTemporal, comparing: TTemporal): boolean;
|
|
51
|
+
getInvalidDate(): TTemporal;
|
|
52
|
+
getTimezone(value: TTemporal | null): PickersTimezone;
|
|
53
|
+
setTimezone(value: TTemporal, timezone: PickersTimezone): TTemporal;
|
|
54
|
+
getCurrentLocaleCode(): string;
|
|
55
|
+
is12HourCycleInCurrentLocale(): boolean;
|
|
56
|
+
expandFormat(format: string): string;
|
|
57
|
+
isValid(value: TTemporal | null): value is TTemporal;
|
|
58
|
+
format(value: TTemporal, formatKey: keyof AdapterFormats): string;
|
|
59
|
+
formatByString(value: TTemporal, format: string): string;
|
|
60
|
+
formatNumber(numberToFormat: string): string;
|
|
61
|
+
isWithinRange(value: TTemporal, [start, end]: [TTemporal, TTemporal]): boolean;
|
|
62
|
+
startOfYear(value: TTemporal): TTemporal;
|
|
63
|
+
startOfMonth(value: TTemporal): TTemporal;
|
|
64
|
+
startOfWeek(value: TTemporal): TTemporal;
|
|
65
|
+
startOfDay(value: TTemporal): TTemporal;
|
|
66
|
+
endOfYear(value: TTemporal): TTemporal;
|
|
67
|
+
endOfMonth(value: TTemporal): TTemporal;
|
|
68
|
+
endOfWeek(value: TTemporal): TTemporal;
|
|
69
|
+
endOfDay(value: TTemporal): TTemporal;
|
|
70
|
+
addYears(value: TTemporal, amount: number): TTemporal;
|
|
71
|
+
addMonths(value: TTemporal, amount: number): TTemporal;
|
|
72
|
+
addWeeks(value: TTemporal, amount: number): TTemporal;
|
|
73
|
+
addDays(value: TTemporal, amount: number): TTemporal;
|
|
74
|
+
addHours(value: TTemporal, amount: number): TTemporal;
|
|
75
|
+
addMinutes(value: TTemporal, amount: number): TTemporal;
|
|
76
|
+
addSeconds(value: TTemporal, amount: number): TTemporal;
|
|
77
|
+
getYear(value: TTemporal): number;
|
|
78
|
+
getMonth(value: TTemporal): number;
|
|
79
|
+
getDate(value: TTemporal): number;
|
|
80
|
+
getHours(value: TTemporal): number;
|
|
81
|
+
getMinutes(value: TTemporal): number;
|
|
82
|
+
getSeconds(value: TTemporal): number;
|
|
83
|
+
getMilliseconds(value: TTemporal): number;
|
|
84
|
+
getDaysInMonth(value: TTemporal): number;
|
|
85
|
+
getWeekNumber(value: TTemporal): number;
|
|
86
|
+
getDayOfWeek(value: TTemporal): number;
|
|
87
|
+
setYear(value: TTemporal, year: number): TTemporal;
|
|
88
|
+
setMonth(value: TTemporal, month: number): TTemporal;
|
|
89
|
+
setDate(value: TTemporal, date: number): TTemporal;
|
|
90
|
+
setHours(value: TTemporal, hours: number): TTemporal;
|
|
91
|
+
setMinutes(value: TTemporal, minutes: number): TTemporal;
|
|
92
|
+
setSeconds(value: TTemporal, seconds: number): TTemporal;
|
|
93
|
+
setMilliseconds(value: TTemporal, milliseconds: number): TTemporal;
|
|
94
|
+
getWeekArray(value: TTemporal): TTemporal[][];
|
|
95
|
+
getYearRange(range: [TTemporal, TTemporal]): TTemporal[];
|
|
96
|
+
}
|
|
97
|
+
export {};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { LocaleSpecs } from "../locale/specs.js";
|
|
2
|
+
import { defaultFormats } from "./adapter-formats.js";
|
|
3
|
+
import { noopAdapterDateOperations, noopAdapterTimeOperations, noopAdapterTimezoneOperations, } from "./operations.js";
|
|
4
|
+
export class AdapterTemporalBase {
|
|
5
|
+
isMUIAdapter = true;
|
|
6
|
+
isTimezoneCompatible = true;
|
|
7
|
+
lib = "temporal";
|
|
8
|
+
locale;
|
|
9
|
+
formats;
|
|
10
|
+
escapedCharacters = { start: "'", end: "'" };
|
|
11
|
+
localeSpecs;
|
|
12
|
+
conversionOperations;
|
|
13
|
+
comparisonOperations;
|
|
14
|
+
dateOperations;
|
|
15
|
+
timeOperations;
|
|
16
|
+
timezoneOperations;
|
|
17
|
+
constructor({ locale = new Intl.Locale("en-US"), formats, conversionOperations, comparisonOperations, dateOperations = noopAdapterDateOperations, timeOperations = noopAdapterTimeOperations, timezoneOperations = noopAdapterTimezoneOperations, }) {
|
|
18
|
+
this.locale = typeof locale === "string" ? new Intl.Locale(locale) : locale;
|
|
19
|
+
this.localeSpecs = LocaleSpecs.get(this.locale);
|
|
20
|
+
this.formats = { ...defaultFormats, ...formats };
|
|
21
|
+
this.conversionOperations = conversionOperations;
|
|
22
|
+
this.comparisonOperations = comparisonOperations;
|
|
23
|
+
this.dateOperations = dateOperations;
|
|
24
|
+
this.timeOperations = timeOperations;
|
|
25
|
+
this.timezoneOperations = timezoneOperations;
|
|
26
|
+
}
|
|
27
|
+
date(value, timezone) {
|
|
28
|
+
return this.conversionOperations.date(value, timezone);
|
|
29
|
+
}
|
|
30
|
+
toJsDate(value) {
|
|
31
|
+
return this.conversionOperations.toJsDate(value);
|
|
32
|
+
}
|
|
33
|
+
parse(value, format) {
|
|
34
|
+
return this.conversionOperations.parse(value, format, this.localeSpecs);
|
|
35
|
+
}
|
|
36
|
+
isEqual(value, comparing) {
|
|
37
|
+
if (value === null && comparing === null) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (value === null || comparing === null) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return this.comparisonOperations.isEqual(value, comparing);
|
|
44
|
+
}
|
|
45
|
+
isSameYear(value, comparing) {
|
|
46
|
+
return this.comparisonOperations.isSameYear(value, comparing);
|
|
47
|
+
}
|
|
48
|
+
isSameMonth(value, comparing) {
|
|
49
|
+
return this.comparisonOperations.isSameMonth(value, comparing);
|
|
50
|
+
}
|
|
51
|
+
isSameDay(value, comparing) {
|
|
52
|
+
return this.comparisonOperations.isSameDay(value, comparing);
|
|
53
|
+
}
|
|
54
|
+
isSameHour(value, comparing) {
|
|
55
|
+
return this.comparisonOperations.isSameHour(value, comparing);
|
|
56
|
+
}
|
|
57
|
+
isAfter(value, comparing) {
|
|
58
|
+
return this.comparisonOperations.isAfter(value, comparing);
|
|
59
|
+
}
|
|
60
|
+
isAfterYear(value, comparing) {
|
|
61
|
+
return this.comparisonOperations.isAfterYear(value, comparing);
|
|
62
|
+
}
|
|
63
|
+
isAfterDay(value, comparing) {
|
|
64
|
+
return this.comparisonOperations.isAfterDay(value, comparing);
|
|
65
|
+
}
|
|
66
|
+
isBefore(value, comparing) {
|
|
67
|
+
return this.comparisonOperations.isBefore(value, comparing);
|
|
68
|
+
}
|
|
69
|
+
isBeforeYear(value, comparing) {
|
|
70
|
+
return this.comparisonOperations.isBeforeYear(value, comparing);
|
|
71
|
+
}
|
|
72
|
+
isBeforeDay(value, comparing) {
|
|
73
|
+
return this.comparisonOperations.isBeforeDay(value, comparing);
|
|
74
|
+
}
|
|
75
|
+
getInvalidDate() {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
getTimezone(value) {
|
|
79
|
+
return this.timezoneOperations.getTimezone(value);
|
|
80
|
+
}
|
|
81
|
+
setTimezone(value, timezone) {
|
|
82
|
+
return this.timezoneOperations.setTimezone(value, timezone);
|
|
83
|
+
}
|
|
84
|
+
getCurrentLocaleCode() {
|
|
85
|
+
return this.locale.toString();
|
|
86
|
+
}
|
|
87
|
+
is12HourCycleInCurrentLocale() {
|
|
88
|
+
return this.localeSpecs.hour12;
|
|
89
|
+
}
|
|
90
|
+
expandFormat(format) {
|
|
91
|
+
return this.localeSpecs.formatter.expandFormat(format);
|
|
92
|
+
}
|
|
93
|
+
isValid(value) {
|
|
94
|
+
return value !== null;
|
|
95
|
+
}
|
|
96
|
+
format(value, formatKey) {
|
|
97
|
+
return this.formatByString(value, this.formats[formatKey]);
|
|
98
|
+
}
|
|
99
|
+
formatByString(value, format) {
|
|
100
|
+
if (format === "s" && value instanceof Temporal.PlainDate) {
|
|
101
|
+
// This is MUI-X collecting localized digits. It allows us to return "0" here, which makes it fall back to
|
|
102
|
+
// non-localized digits!
|
|
103
|
+
return "0";
|
|
104
|
+
}
|
|
105
|
+
return this.localeSpecs.formatter.format(value, format);
|
|
106
|
+
}
|
|
107
|
+
formatNumber(numberToFormat) {
|
|
108
|
+
return numberToFormat;
|
|
109
|
+
}
|
|
110
|
+
isWithinRange(value, [start, end]) {
|
|
111
|
+
return !(this.comparisonOperations.isBefore(value, start) ||
|
|
112
|
+
this.comparisonOperations.isAfter(value, end));
|
|
113
|
+
}
|
|
114
|
+
startOfYear(value) {
|
|
115
|
+
return this.dateOperations.startOfYear(value);
|
|
116
|
+
}
|
|
117
|
+
startOfMonth(value) {
|
|
118
|
+
return this.dateOperations.startOfMonth(value);
|
|
119
|
+
}
|
|
120
|
+
startOfWeek(value) {
|
|
121
|
+
return this.dateOperations.startOfWeek(value, this.locale);
|
|
122
|
+
}
|
|
123
|
+
startOfDay(value) {
|
|
124
|
+
return this.timeOperations.startOfDay(value);
|
|
125
|
+
}
|
|
126
|
+
endOfYear(value) {
|
|
127
|
+
return this.dateOperations.endOfYear(value);
|
|
128
|
+
}
|
|
129
|
+
endOfMonth(value) {
|
|
130
|
+
return this.dateOperations.endOfMonth(value);
|
|
131
|
+
}
|
|
132
|
+
endOfWeek(value) {
|
|
133
|
+
return this.dateOperations.endOfWeek(value, this.locale);
|
|
134
|
+
}
|
|
135
|
+
endOfDay(value) {
|
|
136
|
+
return this.timeOperations.endOfDay(value);
|
|
137
|
+
}
|
|
138
|
+
addYears(value, amount) {
|
|
139
|
+
return this.dateOperations.addYears(value, amount);
|
|
140
|
+
}
|
|
141
|
+
addMonths(value, amount) {
|
|
142
|
+
return this.dateOperations.addMonths(value, amount);
|
|
143
|
+
}
|
|
144
|
+
addWeeks(value, amount) {
|
|
145
|
+
return this.dateOperations.addWeeks(value, amount);
|
|
146
|
+
}
|
|
147
|
+
addDays(value, amount) {
|
|
148
|
+
return this.dateOperations.addDays(value, amount);
|
|
149
|
+
}
|
|
150
|
+
addHours(value, amount) {
|
|
151
|
+
return this.timeOperations.addHours(value, amount);
|
|
152
|
+
}
|
|
153
|
+
addMinutes(value, amount) {
|
|
154
|
+
return this.timeOperations.addMinutes(value, amount);
|
|
155
|
+
}
|
|
156
|
+
addSeconds(value, amount) {
|
|
157
|
+
return this.timeOperations.addSeconds(value, amount);
|
|
158
|
+
}
|
|
159
|
+
getYear(value) {
|
|
160
|
+
return this.dateOperations.getYear(value);
|
|
161
|
+
}
|
|
162
|
+
getMonth(value) {
|
|
163
|
+
return this.dateOperations.getMonth(value);
|
|
164
|
+
}
|
|
165
|
+
getDate(value) {
|
|
166
|
+
return this.dateOperations.getDate(value);
|
|
167
|
+
}
|
|
168
|
+
getHours(value) {
|
|
169
|
+
return this.timeOperations.getHours(value);
|
|
170
|
+
}
|
|
171
|
+
getMinutes(value) {
|
|
172
|
+
return this.timeOperations.getMinutes(value);
|
|
173
|
+
}
|
|
174
|
+
getSeconds(value) {
|
|
175
|
+
return this.timeOperations.getSeconds(value);
|
|
176
|
+
}
|
|
177
|
+
getMilliseconds(value) {
|
|
178
|
+
return this.timeOperations.getMilliseconds(value);
|
|
179
|
+
}
|
|
180
|
+
getDaysInMonth(value) {
|
|
181
|
+
return this.dateOperations.getDaysInMonth(value);
|
|
182
|
+
}
|
|
183
|
+
getWeekNumber(value) {
|
|
184
|
+
return this.dateOperations.getWeekNumber(value, this.locale);
|
|
185
|
+
}
|
|
186
|
+
getDayOfWeek(value) {
|
|
187
|
+
return this.dateOperations.getDayOfWeek(value);
|
|
188
|
+
}
|
|
189
|
+
setYear(value, year) {
|
|
190
|
+
return this.dateOperations.setYear(value, year);
|
|
191
|
+
}
|
|
192
|
+
setMonth(value, month) {
|
|
193
|
+
return this.dateOperations.setMonth(value, month);
|
|
194
|
+
}
|
|
195
|
+
setDate(value, date) {
|
|
196
|
+
return this.dateOperations.setDate(value, date);
|
|
197
|
+
}
|
|
198
|
+
setHours(value, hours) {
|
|
199
|
+
return this.timeOperations.setHours(value, hours);
|
|
200
|
+
}
|
|
201
|
+
setMinutes(value, minutes) {
|
|
202
|
+
return this.timeOperations.setMinutes(value, minutes);
|
|
203
|
+
}
|
|
204
|
+
setSeconds(value, seconds) {
|
|
205
|
+
return this.timeOperations.setSeconds(value, seconds);
|
|
206
|
+
}
|
|
207
|
+
setMilliseconds(value, milliseconds) {
|
|
208
|
+
return this.timeOperations.setMilliseconds(value, milliseconds);
|
|
209
|
+
}
|
|
210
|
+
getWeekArray(value) {
|
|
211
|
+
return this.dateOperations.getWeekArray(value, this);
|
|
212
|
+
}
|
|
213
|
+
getYearRange(range) {
|
|
214
|
+
return this.dateOperations.getYearRange(range, this);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { DateBuilderReturnType, PickersTimezone } from "@mui/x-date-pickers";
|
|
2
|
+
import type { LocaleSpecs } from "../locale/specs.js";
|
|
3
|
+
import type { AdapterTemporalBase, ValidDateTemporal, ValidTemporal, ValidTimeTemporal } from "./base.js";
|
|
4
|
+
export type AdapterConversionOperations<T extends ValidTemporal> = {
|
|
5
|
+
date: <T extends string | null | undefined>(value?: T, timezone?: PickersTimezone) => DateBuilderReturnType<T>;
|
|
6
|
+
toJsDate: (value: T) => Date;
|
|
7
|
+
parse: (value: string, format: string, localeSpecs: LocaleSpecs) => T | null;
|
|
8
|
+
};
|
|
9
|
+
export type AdapterComparisonOperations<T extends ValidTemporal> = {
|
|
10
|
+
isEqual: (value: T, comparing: T) => boolean;
|
|
11
|
+
isSameYear: (value: T, comparing: T) => boolean;
|
|
12
|
+
isSameMonth: (value: T, comparing: T) => boolean;
|
|
13
|
+
isSameDay: (value: T, comparing: T) => boolean;
|
|
14
|
+
isSameHour: (value: T, comparing: T) => boolean;
|
|
15
|
+
isAfter: (value: T, comparing: T) => boolean;
|
|
16
|
+
isAfterYear: (value: T, comparing: T) => boolean;
|
|
17
|
+
isAfterDay: (value: T, comparing: T) => boolean;
|
|
18
|
+
isBefore: (value: T, comparing: T) => boolean;
|
|
19
|
+
isBeforeYear: (value: T, comparing: T) => boolean;
|
|
20
|
+
isBeforeDay: (value: T, comparing: T) => boolean;
|
|
21
|
+
};
|
|
22
|
+
export type AdapterDateOperations<T extends ValidTemporal> = {
|
|
23
|
+
startOfYear: (value: T) => T;
|
|
24
|
+
startOfMonth: (value: T) => T;
|
|
25
|
+
startOfWeek: (value: T, locale: Intl.Locale) => T;
|
|
26
|
+
endOfYear: (value: T) => T;
|
|
27
|
+
endOfMonth: (value: T) => T;
|
|
28
|
+
endOfWeek: (value: T, locale: Intl.Locale) => T;
|
|
29
|
+
addYears: (value: T, amount: number) => T;
|
|
30
|
+
addMonths: (value: T, amount: number) => T;
|
|
31
|
+
addWeeks: (value: T, amount: number) => T;
|
|
32
|
+
addDays: (value: T, amount: number) => T;
|
|
33
|
+
getYear: (value: T) => number;
|
|
34
|
+
getMonth: (value: T) => number;
|
|
35
|
+
getDate: (value: T) => number;
|
|
36
|
+
getDaysInMonth: (value: T) => number;
|
|
37
|
+
getWeekNumber: (value: T, locale: Intl.Locale) => number;
|
|
38
|
+
getDayOfWeek: (value: T) => number;
|
|
39
|
+
setYear: (value: T, year: number) => T;
|
|
40
|
+
setMonth: (value: T, month: number) => T;
|
|
41
|
+
setDate: (value: T, date: number) => T;
|
|
42
|
+
getWeekArray: (value: T, adapter: AdapterTemporalBase<T>) => T[][];
|
|
43
|
+
getYearRange: ([start, end]: [T, T], adapter: AdapterTemporalBase<T>) => T[];
|
|
44
|
+
};
|
|
45
|
+
export declare const noopAdapterDateOperations: AdapterDateOperations<ValidTemporal>;
|
|
46
|
+
export declare const defaultAdapterDateOperations: AdapterDateOperations<ValidDateTemporal>;
|
|
47
|
+
export type AdapterTimeOperations<T extends ValidTemporal> = {
|
|
48
|
+
startOfDay: (value: T) => T;
|
|
49
|
+
endOfDay: (value: T) => T;
|
|
50
|
+
addHours: (value: T, amount: number) => T;
|
|
51
|
+
addMinutes: (value: T, amount: number) => T;
|
|
52
|
+
addSeconds: (value: T, amount: number) => T;
|
|
53
|
+
setHours: (value: T, hours: number) => T;
|
|
54
|
+
setMinutes: (value: T, minutes: number) => T;
|
|
55
|
+
setSeconds: (value: T, seconds: number) => T;
|
|
56
|
+
setMilliseconds: (value: T, milliseconds: number) => T;
|
|
57
|
+
getHours: (value: T) => number;
|
|
58
|
+
getMinutes: (value: T) => number;
|
|
59
|
+
getSeconds: (value: T) => number;
|
|
60
|
+
getMilliseconds: (value: T) => number;
|
|
61
|
+
};
|
|
62
|
+
export declare const noopAdapterTimeOperations: AdapterTimeOperations<ValidTemporal>;
|
|
63
|
+
export declare const defaultAdapterTimeOperations: AdapterTimeOperations<ValidTimeTemporal>;
|
|
64
|
+
export type AdapterTimezoneOperations<T extends ValidTemporal> = {
|
|
65
|
+
getTimezone: (value: T | null) => PickersTimezone;
|
|
66
|
+
setTimezone: (value: T, timezone: PickersTimezone) => T;
|
|
67
|
+
};
|
|
68
|
+
export declare const noopAdapterTimezoneOperations: AdapterTimezoneOperations<ValidTemporal>;
|
|
69
|
+
export declare const resolveTimeZoneId: (timezone?: PickersTimezone) => string;
|
|
70
|
+
export declare const defaultAdapterTimezoneOperations: AdapterTimezoneOperations<Temporal.ZonedDateTime>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { firstDayOfMonth, firstDayOfWeek, firstDayOfYear, lastDayOfMonth, lastDayOfWeek, lastDayOfYear, localeAwareWeekNumber, } from "temporal-extra";
|
|
2
|
+
export const noopAdapterDateOperations = {
|
|
3
|
+
startOfYear: (value) => value,
|
|
4
|
+
startOfMonth: (value) => value,
|
|
5
|
+
startOfWeek: (value) => value,
|
|
6
|
+
endOfYear: (value) => value,
|
|
7
|
+
endOfMonth: (value) => value,
|
|
8
|
+
endOfWeek: (value) => value,
|
|
9
|
+
addYears: (value) => value,
|
|
10
|
+
addMonths: (value) => value,
|
|
11
|
+
addWeeks: (value) => value,
|
|
12
|
+
addDays: (value) => value,
|
|
13
|
+
getYear: () => 2000,
|
|
14
|
+
getMonth: () => 1,
|
|
15
|
+
getDate: () => 1,
|
|
16
|
+
getDaysInMonth: () => 30,
|
|
17
|
+
getWeekNumber: () => 1,
|
|
18
|
+
getDayOfWeek: () => 1,
|
|
19
|
+
setYear: (value) => value,
|
|
20
|
+
setMonth: (value) => value,
|
|
21
|
+
setDate: (value) => value,
|
|
22
|
+
getWeekArray: () => [],
|
|
23
|
+
getYearRange: () => [],
|
|
24
|
+
};
|
|
25
|
+
export const defaultAdapterDateOperations = {
|
|
26
|
+
startOfYear: (value) => firstDayOfYear(value),
|
|
27
|
+
startOfMonth: (value) => firstDayOfMonth(value),
|
|
28
|
+
startOfWeek: (value, locale) => firstDayOfWeek(value, locale),
|
|
29
|
+
endOfYear: (value) => lastDayOfYear(value),
|
|
30
|
+
endOfMonth: (value) => lastDayOfMonth(value),
|
|
31
|
+
endOfWeek: (value, locale) => lastDayOfWeek(value, locale),
|
|
32
|
+
addYears: (value, amount) => value.add({ years: amount }),
|
|
33
|
+
addMonths: (value, amount) => value.add({ months: amount }),
|
|
34
|
+
addWeeks: (value, amount) => value.add({ weeks: amount }),
|
|
35
|
+
addDays: (value, amount) => value.add({ days: amount }),
|
|
36
|
+
getYear: (value) => value.year,
|
|
37
|
+
getMonth: (value) => value.month,
|
|
38
|
+
getDate: (value) => value.day,
|
|
39
|
+
getDaysInMonth: (value) => value.daysInMonth,
|
|
40
|
+
getWeekNumber: (value, locale) => localeAwareWeekNumber(value, locale),
|
|
41
|
+
getDayOfWeek: (value) => value.dayOfWeek,
|
|
42
|
+
setYear: (value, year) => value.with({ year }),
|
|
43
|
+
setMonth: (value, month) => value.with({ month }),
|
|
44
|
+
setDate: (value, date) => value.with({ day: date }),
|
|
45
|
+
getWeekArray: (value, adapter) => {
|
|
46
|
+
const start = adapter.startOfWeek(adapter.startOfMonth(value));
|
|
47
|
+
const end = adapter.endOfWeek(adapter.endOfMonth(value));
|
|
48
|
+
let count = 0;
|
|
49
|
+
let current = start;
|
|
50
|
+
const nestedWeeks = [];
|
|
51
|
+
while (!adapter.isAfter(current, end)) {
|
|
52
|
+
const weekNumber = Math.floor(count / 7);
|
|
53
|
+
nestedWeeks[weekNumber] = nestedWeeks[weekNumber] ?? [];
|
|
54
|
+
nestedWeeks[weekNumber].push(current);
|
|
55
|
+
current = adapter.addDays(current, 1);
|
|
56
|
+
count += 1;
|
|
57
|
+
}
|
|
58
|
+
return nestedWeeks;
|
|
59
|
+
},
|
|
60
|
+
getYearRange: ([start, end], adapter) => {
|
|
61
|
+
const startDate = adapter.startOfYear(start);
|
|
62
|
+
const endDate = adapter.endOfYear(end);
|
|
63
|
+
const years = [];
|
|
64
|
+
let current = startDate;
|
|
65
|
+
while (adapter.isBeforeYear(current, endDate)) {
|
|
66
|
+
years.push(current);
|
|
67
|
+
current = adapter.addYears(current, 1);
|
|
68
|
+
}
|
|
69
|
+
return years;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
export const noopAdapterTimeOperations = {
|
|
73
|
+
startOfDay: (value) => value,
|
|
74
|
+
endOfDay: (value) => value,
|
|
75
|
+
addHours: (value) => value,
|
|
76
|
+
addMinutes: (value) => value,
|
|
77
|
+
addSeconds: (value) => value,
|
|
78
|
+
setHours: (value) => value,
|
|
79
|
+
setMinutes: (value) => value,
|
|
80
|
+
setSeconds: (value) => value,
|
|
81
|
+
setMilliseconds: (value) => value,
|
|
82
|
+
getHours: () => 0,
|
|
83
|
+
getMinutes: () => 0,
|
|
84
|
+
getSeconds: () => 0,
|
|
85
|
+
getMilliseconds: () => 0,
|
|
86
|
+
};
|
|
87
|
+
export const defaultAdapterTimeOperations = {
|
|
88
|
+
startOfDay: (value) => value.with({ hour: 0, minute: 0, second: 0, millisecond: 0 }),
|
|
89
|
+
endOfDay: (value) => value.with({
|
|
90
|
+
hour: 23,
|
|
91
|
+
minute: 59,
|
|
92
|
+
second: 59,
|
|
93
|
+
millisecond: 999,
|
|
94
|
+
}),
|
|
95
|
+
addHours: (value, amount) => value.add({ hours: amount }),
|
|
96
|
+
addMinutes: (value, amount) => value.add({ minutes: amount }),
|
|
97
|
+
addSeconds: (value, amount) => value.add({ seconds: amount }),
|
|
98
|
+
setHours: (value, hours) => value.with({ hour: hours }),
|
|
99
|
+
setMinutes: (value, minutes) => value.with({ minute: minutes }),
|
|
100
|
+
setSeconds: (value, seconds) => value.with({ second: seconds }),
|
|
101
|
+
setMilliseconds: (value, milliseconds) => value.with({ millisecond: milliseconds }),
|
|
102
|
+
getHours: (value) => value.hour,
|
|
103
|
+
getMinutes: (value) => value.minute,
|
|
104
|
+
getSeconds: (value) => value.second,
|
|
105
|
+
getMilliseconds: (value) => value.millisecond,
|
|
106
|
+
};
|
|
107
|
+
export const noopAdapterTimezoneOperations = {
|
|
108
|
+
getTimezone: () => "default",
|
|
109
|
+
setTimezone: (value) => value,
|
|
110
|
+
};
|
|
111
|
+
export const resolveTimeZoneId = (timezone) => {
|
|
112
|
+
if (!timezone || timezone === "default" || timezone === "system") {
|
|
113
|
+
return Temporal.Now.timeZoneId();
|
|
114
|
+
}
|
|
115
|
+
return timezone;
|
|
116
|
+
};
|
|
117
|
+
export const defaultAdapterTimezoneOperations = {
|
|
118
|
+
getTimezone: (value) => (value === null ? "default" : value.timeZoneId),
|
|
119
|
+
setTimezone: (value, timezone) => value.withTimeZone(resolveTimeZoneId(timezone)),
|
|
120
|
+
};
|