@react-stately/datepicker 3.12.0 → 3.14.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/dist/main.js.map +1 -1
- package/dist/module.js.map +1 -1
- package/dist/placeholders.main.js +1 -1
- package/dist/placeholders.main.js.map +1 -1
- package/dist/placeholders.mjs +1 -1
- package/dist/placeholders.module.js +1 -1
- package/dist/placeholders.module.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/useDateFieldState.main.js +70 -19
- package/dist/useDateFieldState.main.js.map +1 -1
- package/dist/useDateFieldState.mjs +71 -20
- package/dist/useDateFieldState.module.js +71 -20
- package/dist/useDateFieldState.module.js.map +1 -1
- package/dist/utils.main.js.map +1 -1
- package/dist/utils.module.js.map +1 -1
- package/package.json +9 -9
- package/src/index.ts +1 -0
- package/src/placeholders.ts +2 -2
- package/src/useDateFieldState.ts +78 -26
- package/src/utils.ts +1 -1
package/src/placeholders.ts
CHANGED
|
@@ -57,7 +57,7 @@ const placeholders = new LocalizedStringDictionary({
|
|
|
57
57
|
ia: {year: 'aaaa', month: 'mm', day: 'dd'},
|
|
58
58
|
id: {year: 'tttt', month: 'bb', day: 'hh'},
|
|
59
59
|
it: {year: 'aaaa', month: 'mm', day: 'gg'},
|
|
60
|
-
ja: {year: '
|
|
60
|
+
ja: {year: '年', month: '月', day: '日'},
|
|
61
61
|
ka: {year: 'წწწწ', month: 'თთ', day: 'რრ'},
|
|
62
62
|
kk: {year: 'жжжж', month: 'аа', day: 'кк'},
|
|
63
63
|
kn: {year: 'ವವವವ', month: 'ಮಿಮೀ', day: 'ದಿದಿ'},
|
|
@@ -93,7 +93,7 @@ const placeholders = new LocalizedStringDictionary({
|
|
|
93
93
|
'zh-TW': {year: '年', month: '月', day: '日'}
|
|
94
94
|
}, 'en');
|
|
95
95
|
|
|
96
|
-
export function getPlaceholder(field: string, value: string, locale: string) {
|
|
96
|
+
export function getPlaceholder(field: string, value: string, locale: string): string {
|
|
97
97
|
// Use the actual placeholder value for the era and day period fields.
|
|
98
98
|
if (field === 'era' || field === 'dayPeriod') {
|
|
99
99
|
return value;
|
package/src/useDateFieldState.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {Calendar, DateFormatter, getMinimumDayInMonth, getMinimumMonthInYear, GregorianCalendar, toCalendar} from '@internationalized/date';
|
|
13
|
+
import {Calendar, CalendarIdentifier, DateFormatter, getMinimumDayInMonth, getMinimumMonthInYear, GregorianCalendar, isEqualCalendar, toCalendar} from '@internationalized/date';
|
|
14
14
|
import {convertValue, createPlaceholderDate, FieldOptions, FormatterOptions, getFormatOptions, getValidationResult, useDefaultProps} from './utils';
|
|
15
15
|
import {DatePickerProps, DateValue, Granularity, MappedDateValue} from '@react-types/datepicker';
|
|
16
16
|
import {FormValidationState, useFormValidationState} from '@react-stately/form';
|
|
@@ -137,7 +137,7 @@ export interface DateFieldStateOptions<T extends DateValue = DateValue> extends
|
|
|
137
137
|
* `@internationalized/date` package, or manually implemented to include support for
|
|
138
138
|
* only certain calendars.
|
|
139
139
|
*/
|
|
140
|
-
createCalendar: (name:
|
|
140
|
+
createCalendar: (name: CalendarIdentifier) => Calendar
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/**
|
|
@@ -168,7 +168,7 @@ export function useDateFieldState<T extends DateValue = DateValue>(props: DateFi
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
let defaultFormatter = useMemo(() => new DateFormatter(locale), [locale]);
|
|
171
|
-
let calendar = useMemo(() => createCalendar(defaultFormatter.resolvedOptions().calendar), [createCalendar, defaultFormatter]);
|
|
171
|
+
let calendar = useMemo(() => createCalendar(defaultFormatter.resolvedOptions().calendar as CalendarIdentifier), [createCalendar, defaultFormatter]);
|
|
172
172
|
|
|
173
173
|
let [value, setDate] = useControlledState<DateValue | null, MappedDateValue<T> | null>(
|
|
174
174
|
props.value,
|
|
@@ -217,10 +217,10 @@ export function useDateFieldState<T extends DateValue = DateValue>(props: DateFi
|
|
|
217
217
|
let clearedSegment = useRef<string | null>(null);
|
|
218
218
|
|
|
219
219
|
// Reset placeholder when calendar changes
|
|
220
|
-
let
|
|
220
|
+
let lastCalendar = useRef(calendar);
|
|
221
221
|
useEffect(() => {
|
|
222
|
-
if (calendar
|
|
223
|
-
|
|
222
|
+
if (!isEqualCalendar(calendar, lastCalendar.current)) {
|
|
223
|
+
lastCalendar.current = calendar;
|
|
224
224
|
setPlaceholderDate(placeholder =>
|
|
225
225
|
Object.keys(validSegments).length > 0
|
|
226
226
|
? toCalendar(placeholder, calendar)
|
|
@@ -268,26 +268,9 @@ export function useDateFieldState<T extends DateValue = DateValue>(props: DateFi
|
|
|
268
268
|
};
|
|
269
269
|
|
|
270
270
|
let dateValue = useMemo(() => displayValue.toDate(timeZone), [displayValue, timeZone]);
|
|
271
|
-
let segments = useMemo(() =>
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
let isEditable = EDITABLE_SEGMENTS[segment.type];
|
|
275
|
-
if (segment.type === 'era' && calendar.getEras().length === 1) {
|
|
276
|
-
isEditable = false;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
let isPlaceholder = EDITABLE_SEGMENTS[segment.type] && !validSegments[segment.type];
|
|
280
|
-
let placeholder = EDITABLE_SEGMENTS[segment.type] ? getPlaceholder(segment.type, segment.value, locale) : null;
|
|
281
|
-
return {
|
|
282
|
-
type: TYPE_MAPPING[segment.type] || segment.type,
|
|
283
|
-
text: isPlaceholder ? placeholder : segment.value,
|
|
284
|
-
...getSegmentLimits(displayValue, segment.type, resolvedOptions),
|
|
285
|
-
isPlaceholder,
|
|
286
|
-
placeholder,
|
|
287
|
-
isEditable
|
|
288
|
-
} as DateSegment;
|
|
289
|
-
})
|
|
290
|
-
, [dateValue, validSegments, dateFormatter, resolvedOptions, displayValue, calendar, locale]);
|
|
271
|
+
let segments = useMemo(() =>
|
|
272
|
+
processSegments(dateValue, validSegments, dateFormatter, resolvedOptions, displayValue, calendar, locale, granularity),
|
|
273
|
+
[dateValue, validSegments, dateFormatter, resolvedOptions, displayValue, calendar, locale, granularity]);
|
|
291
274
|
|
|
292
275
|
// When the era field appears, mark it valid if the year field is already valid.
|
|
293
276
|
// If the era field disappears, remove it from the valid segments.
|
|
@@ -399,6 +382,8 @@ export function useDateFieldState<T extends DateValue = DateValue>(props: DateFi
|
|
|
399
382
|
} else if (!isPM && shouldBePM) {
|
|
400
383
|
value = displayValue.set({hour: displayValue.hour + 12});
|
|
401
384
|
}
|
|
385
|
+
} else if (part === 'hour' && 'hour' in displayValue && displayValue.hour >= 12 && validSegments.dayPeriod) {
|
|
386
|
+
value = displayValue.set({hour: placeholder['hour'] + 12});
|
|
402
387
|
} else if (part in displayValue) {
|
|
403
388
|
value = displayValue.set({[part]: placeholder[part]});
|
|
404
389
|
}
|
|
@@ -423,6 +408,73 @@ export function useDateFieldState<T extends DateValue = DateValue>(props: DateFi
|
|
|
423
408
|
};
|
|
424
409
|
}
|
|
425
410
|
|
|
411
|
+
function processSegments(dateValue, validSegments, dateFormatter, resolvedOptions, displayValue, calendar, locale, granularity) : DateSegment[] {
|
|
412
|
+
let timeValue = ['hour', 'minute', 'second'];
|
|
413
|
+
let segments = dateFormatter.formatToParts(dateValue);
|
|
414
|
+
let processedSegments: DateSegment[] = [];
|
|
415
|
+
for (let segment of segments) {
|
|
416
|
+
let isEditable = EDITABLE_SEGMENTS[segment.type];
|
|
417
|
+
if (segment.type === 'era' && calendar.getEras().length === 1) {
|
|
418
|
+
isEditable = false;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
let isPlaceholder = EDITABLE_SEGMENTS[segment.type] && !validSegments[segment.type];
|
|
422
|
+
let placeholder = EDITABLE_SEGMENTS[segment.type] ? getPlaceholder(segment.type, segment.value, locale) : null;
|
|
423
|
+
|
|
424
|
+
let dateSegment = {
|
|
425
|
+
type: TYPE_MAPPING[segment.type] || segment.type,
|
|
426
|
+
text: isPlaceholder ? placeholder : segment.value,
|
|
427
|
+
...getSegmentLimits(displayValue, segment.type, resolvedOptions),
|
|
428
|
+
isPlaceholder,
|
|
429
|
+
placeholder,
|
|
430
|
+
isEditable
|
|
431
|
+
} as DateSegment;
|
|
432
|
+
|
|
433
|
+
// There is an issue in RTL languages where time fields render (minute:hour) instead of (hour:minute).
|
|
434
|
+
// To force an LTR direction on the time field since, we wrap the time segments in LRI (left-to-right) isolate unicode. See https://www.w3.org/International/questions/qa-bidi-unicode-controls.
|
|
435
|
+
// These unicode characters will be added to the array of processed segments as literals and will mark the start and end of the embedded direction change.
|
|
436
|
+
if (segment.type === 'hour') {
|
|
437
|
+
// This marks the start of the embedded direction change.
|
|
438
|
+
processedSegments.push({
|
|
439
|
+
type: 'literal',
|
|
440
|
+
text: '\u2066',
|
|
441
|
+
...getSegmentLimits(displayValue, 'literal', resolvedOptions),
|
|
442
|
+
isPlaceholder: false,
|
|
443
|
+
placeholder: '',
|
|
444
|
+
isEditable: false
|
|
445
|
+
});
|
|
446
|
+
processedSegments.push(dateSegment);
|
|
447
|
+
// This marks the end of the embedded direction change in the case that the granularity it set to "hour".
|
|
448
|
+
if (segment.type === granularity) {
|
|
449
|
+
processedSegments.push({
|
|
450
|
+
type: 'literal',
|
|
451
|
+
text: '\u2069',
|
|
452
|
+
...getSegmentLimits(displayValue, 'literal', resolvedOptions),
|
|
453
|
+
isPlaceholder: false,
|
|
454
|
+
placeholder: '',
|
|
455
|
+
isEditable: false
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
} else if (timeValue.includes(segment.type) && segment.type === granularity) {
|
|
459
|
+
processedSegments.push(dateSegment);
|
|
460
|
+
// This marks the end of the embedded direction change.
|
|
461
|
+
processedSegments.push({
|
|
462
|
+
type: 'literal',
|
|
463
|
+
text: '\u2069',
|
|
464
|
+
...getSegmentLimits(displayValue, 'literal', resolvedOptions),
|
|
465
|
+
isPlaceholder: false,
|
|
466
|
+
placeholder: '',
|
|
467
|
+
isEditable: false
|
|
468
|
+
});
|
|
469
|
+
} else {
|
|
470
|
+
// We only want to "wrap" the unicode around segments that are hour, minute, or second. If they aren't, just process as normal.
|
|
471
|
+
processedSegments.push(dateSegment);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return processedSegments;
|
|
476
|
+
}
|
|
477
|
+
|
|
426
478
|
function getSegmentLimits(date: DateValue, type: string, options: Intl.ResolvedDateTimeFormatOptions) {
|
|
427
479
|
switch (type) {
|
|
428
480
|
case 'era': {
|
package/src/utils.ts
CHANGED
|
@@ -86,7 +86,7 @@ export function getRangeValidationResult(
|
|
|
86
86
|
maxValue: DateValue | null | undefined,
|
|
87
87
|
isDateUnavailable: ((v: DateValue) => boolean) | undefined,
|
|
88
88
|
options: FormatterOptions
|
|
89
|
-
) {
|
|
89
|
+
): ValidationResult {
|
|
90
90
|
let startValidation = getValidationResult(
|
|
91
91
|
value?.start ?? null,
|
|
92
92
|
minValue,
|