@mui/x-date-pickers 6.0.3 → 6.0.4
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/AdapterDateFnsJalali/index.js +68 -0
- package/AdapterLuxon/index.js +12 -3
- package/CHANGELOG.md +61 -0
- package/DateCalendar/DateCalendar.types.d.ts +1 -1
- package/DateCalendar/PickersCalendarHeader.d.ts +1 -1
- package/DateCalendar/index.d.ts +0 -1
- package/DateField/DateField.types.d.ts +2 -2
- package/DatePicker/DatePickerToolbar.d.ts +1 -1
- package/DatePicker/shared.d.ts +2 -2
- package/DateTimeField/DateTimeField.types.d.ts +2 -2
- package/DateTimePicker/DateTimePickerTabs.d.ts +1 -1
- package/DateTimePicker/DateTimePickerToolbar.d.ts +1 -1
- package/DateTimePicker/shared.d.ts +1 -2
- package/DesktopDatePicker/DesktopDatePicker.types.d.ts +1 -1
- package/DesktopDateTimePicker/DesktopDateTimePicker.types.d.ts +1 -1
- package/DesktopTimePicker/DesktopTimePicker.types.d.ts +1 -1
- package/MobileDatePicker/MobileDatePicker.types.d.ts +1 -1
- package/MobileDateTimePicker/MobileDateTimePicker.types.d.ts +1 -1
- package/MobileTimePicker/MobileTimePicker.types.d.ts +1 -1
- package/PickersLayout/PickersLayout.d.ts +1 -1
- package/PickersLayout/PickersLayout.types.d.ts +1 -1
- package/PickersLayout/index.d.ts +1 -1
- package/PickersLayout/usePickerLayout.d.ts +1 -1
- package/README.md +1 -1
- package/StaticDatePicker/StaticDatePicker.types.d.ts +2 -1
- package/StaticDateTimePicker/StaticDateTimePicker.types.d.ts +2 -1
- package/StaticTimePicker/StaticTimePicker.types.d.ts +2 -1
- package/TimeClock/Clock.d.ts +1 -1
- package/TimeClock/ClockPointer.d.ts +1 -1
- package/TimeClock/TimeClock.types.d.ts +1 -1
- package/TimeClock/index.d.ts +0 -1
- package/TimeField/TimeField.types.d.ts +2 -2
- package/TimePicker/TimePickerToolbar.d.ts +1 -1
- package/TimePicker/shared.d.ts +1 -2
- package/dateViewRenderers/dateViewRenderers.d.ts +1 -1
- package/index.js +1 -1
- package/internals/components/PickersToolbar.d.ts +1 -1
- package/internals/demo/DemoContainer.js +15 -7
- package/internals/hooks/useDesktopPicker/useDesktopPicker.d.ts +1 -1
- package/internals/hooks/useDesktopPicker/useDesktopPicker.types.d.ts +4 -5
- package/internals/hooks/useField/index.d.ts +1 -1
- package/internals/hooks/useField/useField.d.ts +2 -1
- package/internals/hooks/useField/useField.js +9 -7
- package/internals/hooks/useField/useField.types.d.ts +24 -104
- package/internals/hooks/useField/useField.utils.d.ts +7 -5
- package/internals/hooks/useField/useField.utils.js +68 -50
- package/internals/hooks/useField/useFieldCharacterEditing.d.ts +2 -1
- package/internals/hooks/useField/useFieldState.d.ts +3 -1
- package/internals/hooks/useField/useFieldState.js +88 -62
- package/internals/hooks/useIsLandscape.d.ts +1 -1
- package/internals/hooks/useMobilePicker/useMobilePicker.d.ts +1 -1
- package/internals/hooks/useMobilePicker/useMobilePicker.types.d.ts +4 -5
- package/internals/hooks/usePicker/usePicker.d.ts +1 -2
- package/internals/hooks/usePicker/usePicker.types.d.ts +1 -2
- package/internals/hooks/usePicker/usePickerLayoutProps.d.ts +1 -1
- package/internals/hooks/usePicker/usePickerValue.d.ts +2 -1
- package/internals/hooks/usePicker/usePickerViews.d.ts +1 -1
- package/internals/hooks/useStaticPicker/useStaticPicker.d.ts +1 -1
- package/internals/hooks/useStaticPicker/useStaticPicker.types.d.ts +1 -2
- package/internals/hooks/useViews.d.ts +1 -1
- package/internals/hooks/useViews.js +8 -0
- package/internals/hooks/validation/models.d.ts +1 -1
- package/internals/hooks/validation/useDateTimeValidation.d.ts +3 -3
- package/internals/hooks/validation/useDateValidation.d.ts +2 -2
- package/internals/hooks/validation/useTimeValidation.d.ts +2 -2
- package/internals/index.d.ts +3 -7
- package/internals/index.js +1 -1
- package/internals/models/fields.d.ts +2 -20
- package/internals/models/index.d.ts +0 -1
- package/internals/models/index.js +0 -1
- package/internals/models/muiPickersAdapter.d.ts +1 -10
- package/internals/models/props/basePickerProps.d.ts +3 -3
- package/internals/models/props/tabs.d.ts +1 -1
- package/internals/models/props/toolbar.d.ts +1 -1
- package/internals/utils/time-utils.d.ts +2 -1
- package/internals/utils/utils.d.ts +1 -1
- package/internals/utils/utils.js +3 -3
- package/internals/utils/validation.d.ts +1 -1
- package/internals/utils/valueManagers.d.ts +2 -4
- package/internals/utils/valueManagers.js +8 -8
- package/internals/utils/views.d.ts +1 -1
- package/legacy/AdapterDateFnsJalali/index.js +68 -0
- package/legacy/AdapterLuxon/index.js +12 -3
- package/legacy/index.js +1 -1
- package/legacy/internals/demo/DemoContainer.js +12 -6
- package/legacy/internals/hooks/useField/useField.js +10 -8
- package/legacy/internals/hooks/useField/useField.utils.js +76 -54
- package/legacy/internals/hooks/useField/useFieldState.js +94 -67
- package/legacy/internals/hooks/useViews.js +10 -0
- package/legacy/internals/index.js +1 -1
- package/legacy/internals/models/index.js +0 -1
- package/legacy/internals/utils/utils.js +3 -3
- package/legacy/internals/utils/valueManagers.js +10 -12
- package/legacy/locales/daDK.js +91 -0
- package/legacy/locales/nbNO.js +12 -10
- package/legacy/locales/svSE.js +12 -10
- package/legacy/models/index.js +3 -1
- package/locales/daDK.d.ts +51 -0
- package/locales/daDK.js +59 -0
- package/locales/nbNO.js +8 -10
- package/locales/nlNL.d.ts +4 -4
- package/locales/svSE.js +8 -10
- package/locales/utils/pickersLocaleTextApi.d.ts +2 -1
- package/models/fields.d.ts +117 -0
- package/models/fields.js +1 -0
- package/models/index.d.ts +3 -6
- package/models/index.js +3 -1
- package/models/validation.d.ts +8 -0
- package/models/validation.js +1 -0
- package/models/views.js +1 -0
- package/modern/AdapterDateFnsJalali/index.js +68 -0
- package/modern/AdapterLuxon/index.js +12 -3
- package/modern/index.js +1 -1
- package/modern/internals/demo/DemoContainer.js +15 -7
- package/modern/internals/hooks/useField/useField.js +8 -6
- package/modern/internals/hooks/useField/useField.utils.js +68 -50
- package/modern/internals/hooks/useField/useFieldState.js +88 -62
- package/modern/internals/hooks/useViews.js +8 -0
- package/modern/internals/index.js +1 -1
- package/modern/internals/models/index.js +0 -1
- package/modern/internals/utils/utils.js +3 -3
- package/modern/internals/utils/valueManagers.js +8 -8
- package/modern/locales/daDK.js +56 -0
- package/modern/locales/nbNO.js +8 -10
- package/modern/locales/svSE.js +8 -10
- package/modern/models/fields.js +1 -0
- package/modern/models/index.js +3 -1
- package/modern/models/validation.js +1 -0
- package/modern/models/views.js +1 -0
- package/node/AdapterDateFnsJalali/index.js +68 -0
- package/node/AdapterLuxon/index.js +12 -3
- package/node/index.js +1 -1
- package/node/internals/demo/DemoContainer.js +15 -7
- package/node/internals/hooks/useField/useField.js +8 -6
- package/node/internals/hooks/useField/useField.utils.js +71 -52
- package/node/internals/hooks/useField/useFieldState.js +87 -61
- package/node/internals/hooks/useViews.js +8 -0
- package/node/internals/index.js +0 -6
- package/node/internals/models/index.js +0 -11
- package/node/internals/utils/utils.js +3 -3
- package/node/internals/utils/valueManagers.js +7 -7
- package/node/locales/daDK.js +62 -0
- package/node/locales/nbNO.js +8 -10
- package/node/locales/svSE.js +8 -10
- package/node/models/index.js +33 -0
- package/node/models/validation.js +5 -0
- package/node/models/views.js +5 -0
- package/package.json +1 -1
- package/themeAugmentation/props.d.ts +1 -1
- package/timeViewRenderers/timeViewRenderers.d.ts +1 -1
- /package/{internals/models/views.js → legacy/models/fields.js} +0 -0
- /package/legacy/{internals/models/views.js → models/validation.js} +0 -0
- /package/{modern/internals → legacy}/models/views.js +0 -0
- /package/{internals/models → models}/views.d.ts +0 -0
- /package/node/{internals/models/views.js → models/fields.js} +0 -0
|
@@ -147,32 +147,31 @@ export const adjustSectionValue = (utils, section, keyCode, sectionsValueBoundar
|
|
|
147
147
|
}
|
|
148
148
|
return adjustLetterSection();
|
|
149
149
|
};
|
|
150
|
-
const getSectionVisibleValue = (section,
|
|
151
|
-
|
|
150
|
+
export const getSectionVisibleValue = (section, target) => {
|
|
151
|
+
let value = section.value || section.placeholder;
|
|
152
152
|
|
|
153
153
|
// In the input, we add an empty character at the end of each section without leading zeros.
|
|
154
154
|
// This makes sure that `onChange` will always be fired.
|
|
155
155
|
// Otherwise, when your input value equals `1/dd/yyyy` (format `M/DD/YYYY` on DayJs),
|
|
156
156
|
// If you press `1`, on the first section, the new value is also `1/dd/yyyy`,
|
|
157
157
|
// So the browser will not fire the input `onChange`.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return `\u2068${value}\u200e\u2069`;
|
|
158
|
+
const shouldAddInvisibleSpace = ['input-rtl', 'input-ltr'].includes(target) && section.contentType === 'digit' && !section.hasLeadingZeros && value.length === 1;
|
|
159
|
+
if (shouldAddInvisibleSpace) {
|
|
160
|
+
value = `${value}\u200e`;
|
|
162
161
|
}
|
|
163
|
-
if (
|
|
164
|
-
|
|
162
|
+
if (target === 'input-rtl') {
|
|
163
|
+
value = `\u2068${value}\u2069`;
|
|
165
164
|
}
|
|
166
165
|
return value;
|
|
167
166
|
};
|
|
168
167
|
export const cleanString = dirtyString => dirtyString.replace(/[\u2066\u2067\u2068\u2069]/g, '');
|
|
169
|
-
export const addPositionPropertiesToSections = sections => {
|
|
168
|
+
export const addPositionPropertiesToSections = (sections, isRTL) => {
|
|
170
169
|
let position = 0;
|
|
171
|
-
let positionInInput = 1;
|
|
170
|
+
let positionInInput = isRTL ? 1 : 0;
|
|
172
171
|
const newSections = [];
|
|
173
172
|
for (let i = 0; i < sections.length; i += 1) {
|
|
174
173
|
const section = sections[i];
|
|
175
|
-
const renderedValue = getSectionVisibleValue(section,
|
|
174
|
+
const renderedValue = getSectionVisibleValue(section, isRTL ? 'input-rtl' : 'input-ltr');
|
|
176
175
|
const sectionStr = `${section.startSeparator}${renderedValue}${section.endSeparator}`;
|
|
177
176
|
const sectionLength = cleanString(sectionStr).length;
|
|
178
177
|
const sectionLengthInInput = sectionStr.length;
|
|
@@ -317,10 +316,6 @@ export const splitFormatIntoSections = (utils, localeText, format, date) => {
|
|
|
317
316
|
if (token === '') {
|
|
318
317
|
return null;
|
|
319
318
|
}
|
|
320
|
-
const expandedToken = utils.expandFormat(token);
|
|
321
|
-
if (expandedToken !== token) {
|
|
322
|
-
return expandedToken;
|
|
323
|
-
}
|
|
324
319
|
const sectionConfig = getDateSectionConfigFromFormatToken(utils, token);
|
|
325
320
|
const sectionValue = date == null || !utils.isValid(date) ? '' : utils.formatByString(date, token);
|
|
326
321
|
const hasLeadingZeros = doesSectionHaveLeadingZeros(utils, sectionConfig.contentType, sectionConfig.type, token);
|
|
@@ -335,39 +330,50 @@ export const splitFormatIntoSections = (utils, localeText, format, date) => {
|
|
|
335
330
|
}));
|
|
336
331
|
return null;
|
|
337
332
|
};
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
333
|
+
|
|
334
|
+
// Expand the provided format
|
|
335
|
+
let formatExpansionOverflow = 10;
|
|
336
|
+
let prevFormat = format;
|
|
337
|
+
let nextFormat = utils.expandFormat(format);
|
|
338
|
+
while (nextFormat !== prevFormat) {
|
|
339
|
+
prevFormat = nextFormat;
|
|
340
|
+
nextFormat = utils.expandFormat(prevFormat);
|
|
341
|
+
formatExpansionOverflow -= 1;
|
|
342
|
+
if (formatExpansionOverflow < 0) {
|
|
343
|
+
throw new Error('MUI: The format expansion seems to be enter in an infinite loop. Please open an issue with the format passed to the picker component');
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const expandedFormat = nextFormat;
|
|
347
|
+
|
|
348
|
+
// Get start/end indexes of escaped sections
|
|
349
|
+
const escapedParts = getEscapedPartsFromFormat(utils, expandedFormat);
|
|
350
|
+
|
|
351
|
+
// This RegExp test if the beginning of a string correspond to a supported token
|
|
352
|
+
const isTokenStartRegExp = new RegExp(`^(${Object.keys(utils.formatTokenMap).join('|')})`);
|
|
353
|
+
let currentTokenValue = '';
|
|
354
|
+
for (let i = 0; i < expandedFormat.length; i += 1) {
|
|
355
|
+
const escapedPartOfCurrentChar = escapedParts.find(escapeIndex => escapeIndex.start <= i && escapeIndex.end >= i);
|
|
356
|
+
const char = expandedFormat[i];
|
|
357
|
+
const isEscapedChar = escapedPartOfCurrentChar != null;
|
|
358
|
+
const potentialToken = `${currentTokenValue}${expandedFormat.slice(i)}`;
|
|
359
|
+
if (!isEscapedChar && char.match(/([A-Za-z]+)/) && isTokenStartRegExp.test(potentialToken)) {
|
|
360
|
+
currentTokenValue += char;
|
|
361
|
+
} else {
|
|
362
|
+
// If we are on the opening or closing character of an escaped part of the format,
|
|
363
|
+
// Then we ignore this character.
|
|
364
|
+
const isEscapeBoundary = isEscapedChar && escapedPartOfCurrentChar?.start === i || escapedPartOfCurrentChar?.end === i;
|
|
365
|
+
if (!isEscapeBoundary) {
|
|
366
|
+
commitToken(currentTokenValue);
|
|
367
|
+
currentTokenValue = '';
|
|
368
|
+
if (sections.length === 0) {
|
|
369
|
+
startSeparator += char;
|
|
370
|
+
} else {
|
|
371
|
+
sections[sections.length - 1].endSeparator += char;
|
|
362
372
|
}
|
|
363
373
|
}
|
|
364
374
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
splitFormat(expandedToken);
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
splitFormat(format);
|
|
375
|
+
}
|
|
376
|
+
commitToken(currentTokenValue);
|
|
371
377
|
return sections.map(section => {
|
|
372
378
|
const cleanSeparator = separator => {
|
|
373
379
|
let cleanedSeparator = separator;
|
|
@@ -401,22 +407,26 @@ export const getDateFromDateSections = (utils, sections) => {
|
|
|
401
407
|
const shouldSkip = shouldSkipWeekDays && section.type === 'weekDay';
|
|
402
408
|
if (!shouldSkip) {
|
|
403
409
|
sectionFormats.push(section.format);
|
|
404
|
-
sectionValues.push(getSectionVisibleValue(section,
|
|
410
|
+
sectionValues.push(getSectionVisibleValue(section, 'non-input'));
|
|
405
411
|
}
|
|
406
412
|
}
|
|
407
413
|
const formatWithoutSeparator = sectionFormats.join(' ');
|
|
408
414
|
const dateWithoutSeparatorStr = sectionValues.join(' ');
|
|
409
415
|
return utils.parse(dateWithoutSeparatorStr, formatWithoutSeparator);
|
|
410
416
|
};
|
|
411
|
-
export const createDateStrForInputFromSections = sections => {
|
|
412
|
-
const
|
|
417
|
+
export const createDateStrForInputFromSections = (sections, isRTL) => {
|
|
418
|
+
const formattedSections = sections.map(section => `${section.startSeparator}${getSectionVisibleValue(section, isRTL ? 'input-rtl' : 'input-ltr')}${section.endSeparator}`);
|
|
419
|
+
const dateStr = formattedSections.join('');
|
|
420
|
+
if (!isRTL) {
|
|
421
|
+
return dateStr;
|
|
422
|
+
}
|
|
413
423
|
|
|
414
424
|
// \u2066: start left-to-right isolation
|
|
415
425
|
// \u2067: start right-to-left isolation
|
|
416
426
|
// \u2068: start first strong character isolation
|
|
417
427
|
// \u2069: pop isolation
|
|
418
428
|
// wrap into an isolated group such that separators can split the string in smaller ones by adding \u2069\u2068
|
|
419
|
-
return `\u2066${
|
|
429
|
+
return `\u2066${dateStr}\u2069`;
|
|
420
430
|
};
|
|
421
431
|
export const getSectionsBoundaries = utils => {
|
|
422
432
|
const today = utils.date();
|
|
@@ -583,7 +593,15 @@ export const mergeDateIntoReferenceDate = (utils, dateToTransferFrom, sections,
|
|
|
583
593
|
return mergedDate;
|
|
584
594
|
}, referenceDate);
|
|
585
595
|
export const isAndroid = () => navigator.userAgent.toLowerCase().indexOf('android') > -1;
|
|
586
|
-
export const
|
|
596
|
+
export const clampDaySectionIfPossible = (utils, sections, sectionsValueBoundaries) => {
|
|
597
|
+
// We can only clamp the day value if:
|
|
598
|
+
// 1. if all the sections are filled (except the week day section which can be empty)
|
|
599
|
+
// 2. there is a day section
|
|
600
|
+
const canClamp = sections.every(section => section.type === 'weekDay' || section.value !== '') && sections.some(section => section.type === 'day');
|
|
601
|
+
if (!canClamp) {
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
|
|
587
605
|
// We try to generate a valid date representing the start of the month of the invalid date typed by the user.
|
|
588
606
|
const sectionsForStartOfMonth = sections.map(section => {
|
|
589
607
|
if (section.type !== 'day') {
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import useControlled from '@mui/utils/useControlled';
|
|
4
|
+
import { useTheme } from '@mui/material/styles';
|
|
4
5
|
import { useUtils, useLocaleText, useLocalizationContext } from '../useUtils';
|
|
5
|
-
import { addPositionPropertiesToSections, splitFormatIntoSections,
|
|
6
|
+
import { addPositionPropertiesToSections, splitFormatIntoSections, clampDaySectionIfPossible, mergeDateIntoReferenceDate, getSectionsBoundaries, validateSections, getDateFromDateSections } from './useField.utils';
|
|
6
7
|
export const useFieldState = params => {
|
|
7
8
|
const utils = useUtils();
|
|
8
9
|
const localeText = useLocaleText();
|
|
9
10
|
const adapter = useLocalizationContext();
|
|
11
|
+
const theme = useTheme();
|
|
12
|
+
const isRTL = theme.direction === 'rtl';
|
|
10
13
|
const {
|
|
11
14
|
valueManager,
|
|
12
15
|
fieldValueManager,
|
|
@@ -25,14 +28,14 @@ export const useFieldState = params => {
|
|
|
25
28
|
const firstDefaultValue = React.useRef(defaultValue);
|
|
26
29
|
const valueFromTheOutside = valueProp ?? firstDefaultValue.current ?? valueManager.emptyValue;
|
|
27
30
|
const sectionsValueBoundaries = React.useMemo(() => getSectionsBoundaries(utils), [utils]);
|
|
28
|
-
const
|
|
31
|
+
const getSectionsFromValue = React.useCallback((value, fallbackSections = null) => fieldValueManager.getSectionsFromValue(utils, value, fallbackSections, isRTL, date => splitFormatIntoSections(utils, localeText, format, date)), [fieldValueManager, format, localeText, isRTL, utils]);
|
|
32
|
+
const placeholder = React.useMemo(() => fieldValueManager.getValueStrFromSections(getSectionsFromValue(valueManager.emptyValue), isRTL), [fieldValueManager, getSectionsFromValue, valueManager.emptyValue, isRTL]);
|
|
29
33
|
const [state, setState] = React.useState(() => {
|
|
30
|
-
const sections =
|
|
34
|
+
const sections = getSectionsFromValue(valueFromTheOutside);
|
|
31
35
|
validateSections(sections, valueType);
|
|
32
36
|
return {
|
|
33
37
|
sections,
|
|
34
38
|
value: valueFromTheOutside,
|
|
35
|
-
placeholder,
|
|
36
39
|
referenceValue: fieldValueManager.updateReferenceValue(utils, valueFromTheOutside, valueManager.getTodayValue(utils)),
|
|
37
40
|
tempValueStrAndroid: null
|
|
38
41
|
};
|
|
@@ -78,11 +81,11 @@ export const useFieldState = params => {
|
|
|
78
81
|
}, [selectedSections, state.sections]);
|
|
79
82
|
const publishValue = ({
|
|
80
83
|
value,
|
|
81
|
-
referenceValue
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
referenceValue,
|
|
85
|
+
sections
|
|
86
|
+
}) => {
|
|
84
87
|
setState(prevState => _extends({}, prevState, {
|
|
85
|
-
sections
|
|
88
|
+
sections,
|
|
86
89
|
value,
|
|
87
90
|
referenceValue,
|
|
88
91
|
tempValueStrAndroid: null
|
|
@@ -106,7 +109,7 @@ export const useFieldState = params => {
|
|
|
106
109
|
value: newSectionValue,
|
|
107
110
|
modified: true
|
|
108
111
|
});
|
|
109
|
-
return addPositionPropertiesToSections(newSections);
|
|
112
|
+
return addPositionPropertiesToSections(newSections, isRTL);
|
|
110
113
|
};
|
|
111
114
|
const clearValue = () => {
|
|
112
115
|
if (valueManager.areValuesEqual(utils, state.value, valueManager.emptyValue)) {
|
|
@@ -114,8 +117,9 @@ export const useFieldState = params => {
|
|
|
114
117
|
}
|
|
115
118
|
publishValue({
|
|
116
119
|
value: valueManager.emptyValue,
|
|
117
|
-
referenceValue: state.referenceValue
|
|
118
|
-
|
|
120
|
+
referenceValue: state.referenceValue,
|
|
121
|
+
sections: getSectionsFromValue(valueManager.emptyValue)
|
|
122
|
+
});
|
|
119
123
|
};
|
|
120
124
|
const clearActiveSection = () => {
|
|
121
125
|
if (selectedSectionIndexes == null) {
|
|
@@ -126,16 +130,17 @@ export const useFieldState = params => {
|
|
|
126
130
|
return;
|
|
127
131
|
}
|
|
128
132
|
const activeDateManager = fieldValueManager.getActiveDateManager(utils, state, activeSection);
|
|
129
|
-
const
|
|
130
|
-
const nonEmptySectionCountBefore = activeDateSections.filter(section => section.value !== '').length;
|
|
133
|
+
const nonEmptySectionCountBefore = activeDateManager.getSections(state.sections).filter(section => section.value !== '').length;
|
|
131
134
|
const isTheOnlyNonEmptySection = nonEmptySectionCountBefore === 1;
|
|
132
135
|
const newSections = setSectionValue(selectedSectionIndexes.startIndex, '');
|
|
133
136
|
const newActiveDate = isTheOnlyNonEmptySection ? null : utils.date(new Date(''));
|
|
134
|
-
const
|
|
135
|
-
if ((newActiveDate != null && !utils.isValid(newActiveDate)) !== (activeDateManager.
|
|
136
|
-
publishValue(
|
|
137
|
+
const newValues = activeDateManager.getNewValuesFromNewActiveDate(newActiveDate);
|
|
138
|
+
if ((newActiveDate != null && !utils.isValid(newActiveDate)) !== (activeDateManager.date != null && !utils.isValid(activeDateManager.date))) {
|
|
139
|
+
publishValue(_extends({}, newValues, {
|
|
140
|
+
sections: newSections
|
|
141
|
+
}));
|
|
137
142
|
} else {
|
|
138
|
-
setState(prevState => _extends({}, prevState,
|
|
143
|
+
setState(prevState => _extends({}, prevState, newValues, {
|
|
139
144
|
sections: newSections,
|
|
140
145
|
tempValueStrAndroid: null
|
|
141
146
|
}));
|
|
@@ -154,7 +159,8 @@ export const useFieldState = params => {
|
|
|
154
159
|
const newReferenceValue = fieldValueManager.updateReferenceValue(utils, newValue, state.referenceValue);
|
|
155
160
|
publishValue({
|
|
156
161
|
value: newValue,
|
|
157
|
-
referenceValue: newReferenceValue
|
|
162
|
+
referenceValue: newReferenceValue,
|
|
163
|
+
sections: getSectionsFromValue(newValue, state.sections)
|
|
158
164
|
});
|
|
159
165
|
};
|
|
160
166
|
const updateSectionValue = ({
|
|
@@ -162,73 +168,92 @@ export const useFieldState = params => {
|
|
|
162
168
|
newSectionValue,
|
|
163
169
|
shouldGoToNextSection
|
|
164
170
|
}) => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
} else {
|
|
178
|
-
setState(prevState => _extends({}, prevState, values, {
|
|
179
|
-
sections: sections ?? state.sections,
|
|
180
|
-
tempValueStrAndroid: null
|
|
181
|
-
}));
|
|
182
|
-
}
|
|
183
|
-
};
|
|
171
|
+
/**
|
|
172
|
+
* 1. Decide which section should be focused
|
|
173
|
+
*/
|
|
174
|
+
if (shouldGoToNextSection && selectedSectionIndexes && selectedSectionIndexes.startIndex < state.sections.length - 1) {
|
|
175
|
+
setSelectedSections(selectedSectionIndexes.startIndex + 1);
|
|
176
|
+
} else if (selectedSectionIndexes && selectedSectionIndexes.startIndex !== selectedSectionIndexes.endIndex) {
|
|
177
|
+
setSelectedSections(selectedSectionIndexes.startIndex);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 2. Try to build a valid date from the new section value
|
|
182
|
+
*/
|
|
184
183
|
const activeDateManager = fieldValueManager.getActiveDateManager(utils, state, activeSection);
|
|
185
184
|
const newSections = setSectionValue(selectedSectionIndexes.startIndex, newSectionValue);
|
|
186
|
-
const
|
|
187
|
-
let newActiveDate = getDateFromDateSections(utils,
|
|
185
|
+
const newActiveDateSections = activeDateManager.getSections(newSections);
|
|
186
|
+
let newActiveDate = getDateFromDateSections(utils, newActiveDateSections);
|
|
187
|
+
let shouldRegenSections = false;
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
189
|
+
/**
|
|
190
|
+
* If the date is invalid,
|
|
191
|
+
* Then we can try to clamp the day section to see if that produces a valid date.
|
|
192
|
+
* This can be useful if the month has fewer days than the day value currently provided.
|
|
193
|
+
*/
|
|
194
|
+
if (!utils.isValid(newActiveDate)) {
|
|
195
|
+
const clampedSections = clampDaySectionIfPossible(utils, newActiveDateSections, sectionsValueBoundaries);
|
|
196
|
+
if (clampedSections != null) {
|
|
197
|
+
shouldRegenSections = true;
|
|
198
|
+
newActiveDate = getDateFromDateSections(utils, clampedSections);
|
|
195
199
|
}
|
|
196
200
|
}
|
|
201
|
+
let values;
|
|
202
|
+
let shouldPublish;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* If the new date is valid,
|
|
206
|
+
* Then we merge the value of the modified sections into the reference date.
|
|
207
|
+
* This makes sure that we don't lose some information of the initial date (like the time on a date field).
|
|
208
|
+
*/
|
|
197
209
|
if (newActiveDate != null && utils.isValid(newActiveDate)) {
|
|
198
|
-
const mergedDate = mergeDateIntoReferenceDate(utils, newActiveDate,
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
210
|
+
const mergedDate = mergeDateIntoReferenceDate(utils, newActiveDate, newActiveDateSections, activeDateManager.referenceDate, true);
|
|
211
|
+
values = activeDateManager.getNewValuesFromNewActiveDate(mergedDate);
|
|
212
|
+
shouldPublish = true;
|
|
213
|
+
} else {
|
|
214
|
+
values = activeDateManager.getNewValuesFromNewActiveDate(newActiveDate);
|
|
215
|
+
shouldPublish = (newActiveDate != null && !utils.isValid(newActiveDate)) !== (activeDateManager.date != null && !utils.isValid(activeDateManager.date));
|
|
203
216
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* If the value has been modified (to clamp the day).
|
|
220
|
+
* Then we need to re-generate the sections to make sure they also have this change.
|
|
221
|
+
*/
|
|
222
|
+
const sections = shouldRegenSections ? getSectionsFromValue(values.value, state.sections) : newSections;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Publish or update the internal state with the new value and sections.
|
|
226
|
+
*/
|
|
227
|
+
if (shouldPublish) {
|
|
228
|
+
return publishValue(_extends({}, values, {
|
|
229
|
+
sections
|
|
230
|
+
}));
|
|
231
|
+
}
|
|
232
|
+
return setState(prevState => _extends({}, prevState, values, {
|
|
233
|
+
sections,
|
|
234
|
+
tempValueStrAndroid: null
|
|
235
|
+
}));
|
|
209
236
|
};
|
|
210
237
|
const setTempAndroidValueStr = tempValueStrAndroid => setState(prev => _extends({}, prev, {
|
|
211
238
|
tempValueStrAndroid
|
|
212
239
|
}));
|
|
213
240
|
React.useEffect(() => {
|
|
214
241
|
if (!valueManager.areValuesEqual(utils, state.value, valueFromTheOutside)) {
|
|
215
|
-
const sections = fieldValueManager.getSectionsFromValue(utils, localeText, null, valueFromTheOutside, format);
|
|
216
242
|
setState(prevState => _extends({}, prevState, {
|
|
217
243
|
value: valueFromTheOutside,
|
|
218
244
|
referenceValue: fieldValueManager.updateReferenceValue(utils, valueFromTheOutside, prevState.referenceValue),
|
|
219
|
-
sections
|
|
245
|
+
sections: getSectionsFromValue(valueFromTheOutside)
|
|
220
246
|
}));
|
|
221
247
|
}
|
|
222
248
|
}, [valueFromTheOutside]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
223
249
|
|
|
224
250
|
React.useEffect(() => {
|
|
225
|
-
const sections =
|
|
251
|
+
const sections = getSectionsFromValue(state.value);
|
|
226
252
|
validateSections(sections, valueType);
|
|
227
253
|
setState(prevState => _extends({}, prevState, {
|
|
228
|
-
sections
|
|
229
|
-
placeholder
|
|
254
|
+
sections
|
|
230
255
|
}));
|
|
231
|
-
}, [format, utils.locale
|
|
256
|
+
}, [format, utils.locale]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
232
257
|
|
|
233
258
|
return {
|
|
234
259
|
state,
|
|
@@ -239,6 +264,7 @@ export const useFieldState = params => {
|
|
|
239
264
|
updateSectionValue,
|
|
240
265
|
updateValueFromValueStr,
|
|
241
266
|
setTempAndroidValueStr,
|
|
242
|
-
sectionsValueBoundaries
|
|
267
|
+
sectionsValueBoundaries,
|
|
268
|
+
placeholder
|
|
243
269
|
};
|
|
244
270
|
};
|
|
@@ -24,6 +24,8 @@ export function useViews({
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
const previousOpenTo = React.useRef(openTo);
|
|
28
|
+
const previousViews = React.useRef(views);
|
|
27
29
|
const defaultView = React.useRef(views.includes(openTo) ? openTo : views[0]);
|
|
28
30
|
const [view, setView] = useControlled({
|
|
29
31
|
name: 'useViews',
|
|
@@ -38,6 +40,12 @@ export function useViews({
|
|
|
38
40
|
controlled: inFocusedView,
|
|
39
41
|
default: defaultFocusedView.current
|
|
40
42
|
});
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
// Update the current view when `openTo` or `views` props change
|
|
45
|
+
if (previousOpenTo.current && previousOpenTo.current !== openTo || previousViews.current && previousViews.current.some(previousView => !views.includes(previousView))) {
|
|
46
|
+
setView(views.includes(openTo) ? openTo : views[0]);
|
|
47
|
+
}
|
|
48
|
+
}, [openTo, setView, view, views]);
|
|
41
49
|
const viewIndex = views.indexOf(view);
|
|
42
50
|
const previousView = views[viewIndex - 1] ?? null;
|
|
43
51
|
const nextView = views[viewIndex + 1] ?? null;
|
|
@@ -9,7 +9,7 @@ export { pickersArrowSwitcherClasses } from './components/PickersArrowSwitcher/p
|
|
|
9
9
|
export { pickersPopperClasses } from './components/pickersPopperClasses';
|
|
10
10
|
export { PickersToolbarButton } from './components/PickersToolbarButton';
|
|
11
11
|
export { DAY_MARGIN, DIALOG_WIDTH } from './constants/dimensions';
|
|
12
|
-
export { useField, createDateStrForInputFromSections, addPositionPropertiesToSections
|
|
12
|
+
export { useField, createDateStrForInputFromSections, addPositionPropertiesToSections } from './hooks/useField';
|
|
13
13
|
export { usePicker } from './hooks/usePicker';
|
|
14
14
|
export { useStaticPicker } from './hooks/useStaticPicker';
|
|
15
15
|
export { useLocalizationContext, useDefaultDates, useUtils, useLocaleText, useNow } from './hooks/useUtils';
|
|
@@ -5,7 +5,7 @@ export function arrayIncludes(array, itemOrItems) {
|
|
|
5
5
|
}
|
|
6
6
|
return array.indexOf(itemOrItems) !== -1;
|
|
7
7
|
}
|
|
8
|
-
export const onSpaceOrEnter = (innerFn,
|
|
8
|
+
export const onSpaceOrEnter = (innerFn, externalEvent) => event => {
|
|
9
9
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
10
10
|
innerFn(event);
|
|
11
11
|
|
|
@@ -13,8 +13,8 @@ export const onSpaceOrEnter = (innerFn, onFocus) => event => {
|
|
|
13
13
|
event.preventDefault();
|
|
14
14
|
event.stopPropagation();
|
|
15
15
|
}
|
|
16
|
-
if (
|
|
17
|
-
|
|
16
|
+
if (externalEvent) {
|
|
17
|
+
externalEvent(event);
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
export const executeInTheNextEventLoopTick = fn => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { areDatesEqual, replaceInvalidDateByNull } from './date-utils';
|
|
2
|
-
import { addPositionPropertiesToSections, createDateStrForInputFromSections
|
|
2
|
+
import { addPositionPropertiesToSections, createDateStrForInputFromSections } from '../hooks/useField/useField.utils';
|
|
3
3
|
export const singleItemValueManager = {
|
|
4
4
|
emptyValue: null,
|
|
5
5
|
getTodayValue: utils => utils.date(),
|
|
@@ -10,19 +10,19 @@ export const singleItemValueManager = {
|
|
|
10
10
|
};
|
|
11
11
|
export const singleItemFieldValueManager = {
|
|
12
12
|
updateReferenceValue: (utils, value, prevReferenceValue) => value == null || !utils.isValid(value) ? prevReferenceValue : value,
|
|
13
|
-
getSectionsFromValue: (utils,
|
|
13
|
+
getSectionsFromValue: (utils, date, prevSections, isRTL, getSectionsFromDate) => {
|
|
14
14
|
const shouldReUsePrevDateSections = !utils.isValid(date) && !!prevSections;
|
|
15
15
|
if (shouldReUsePrevDateSections) {
|
|
16
16
|
return prevSections;
|
|
17
17
|
}
|
|
18
|
-
return addPositionPropertiesToSections(
|
|
18
|
+
return addPositionPropertiesToSections(getSectionsFromDate(date), isRTL);
|
|
19
19
|
},
|
|
20
|
-
getValueStrFromSections:
|
|
21
|
-
getActiveDateSections: sections => sections,
|
|
20
|
+
getValueStrFromSections: createDateStrForInputFromSections,
|
|
22
21
|
getActiveDateManager: (utils, state) => ({
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
date: state.value,
|
|
23
|
+
referenceDate: state.referenceValue,
|
|
24
|
+
getSections: sections => sections,
|
|
25
|
+
getNewValuesFromNewActiveDate: newActiveDate => ({
|
|
26
26
|
value: newActiveDate,
|
|
27
27
|
referenceValue: newActiveDate == null || !utils.isValid(newActiveDate) ? state.referenceValue : newActiveDate
|
|
28
28
|
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getPickersLocalization } from './utils/getPickersLocalization';
|
|
2
|
+
|
|
3
|
+
// maps TimeView to its translation
|
|
4
|
+
const timeViews = {
|
|
5
|
+
hours: 'Timer',
|
|
6
|
+
minutes: 'Minutter',
|
|
7
|
+
seconds: 'Sekunder'
|
|
8
|
+
};
|
|
9
|
+
const daDKPickers = {
|
|
10
|
+
// Calendar navigation
|
|
11
|
+
previousMonth: 'Forrige måned',
|
|
12
|
+
nextMonth: 'Næste måned',
|
|
13
|
+
// View navigation
|
|
14
|
+
openPreviousView: 'åben forrige visning',
|
|
15
|
+
openNextView: 'åben næste visning',
|
|
16
|
+
calendarViewSwitchingButtonAriaLabel: view => view === 'year' ? 'årsvisning er åben, skift til kalendervisning' : 'kalendervisning er åben, skift til årsvisning',
|
|
17
|
+
// DateRange placeholders
|
|
18
|
+
start: 'Start',
|
|
19
|
+
end: 'Slut',
|
|
20
|
+
// Action bar
|
|
21
|
+
cancelButtonLabel: 'Annuller',
|
|
22
|
+
clearButtonLabel: 'Ryd',
|
|
23
|
+
okButtonLabel: 'OK',
|
|
24
|
+
todayButtonLabel: 'I dag',
|
|
25
|
+
// Toolbar titles
|
|
26
|
+
datePickerToolbarTitle: 'Vælg dato',
|
|
27
|
+
dateTimePickerToolbarTitle: 'Vælg dato & tidspunkt',
|
|
28
|
+
timePickerToolbarTitle: 'Vælg tidspunkt',
|
|
29
|
+
dateRangePickerToolbarTitle: 'Vælg datointerval',
|
|
30
|
+
// Clock labels
|
|
31
|
+
clockLabelText: (view, time, adapter) => `Vælg ${timeViews[view] ?? view}. ${time === null ? 'Intet tidspunkt valgt' : `Valgte tidspunkt er ${adapter.format(time, 'fullTime')}`}`,
|
|
32
|
+
hoursClockNumberText: hours => `${hours} timer`,
|
|
33
|
+
minutesClockNumberText: minutes => `${minutes} minutter`,
|
|
34
|
+
secondsClockNumberText: seconds => `${seconds} sekunder`,
|
|
35
|
+
// Calendar labels
|
|
36
|
+
calendarWeekNumberHeaderLabel: 'Ugenummer',
|
|
37
|
+
calendarWeekNumberHeaderText: '#',
|
|
38
|
+
calendarWeekNumberAriaLabelText: weekNumber => `Uge ${weekNumber}`,
|
|
39
|
+
calendarWeekNumberText: weekNumber => `${weekNumber}`,
|
|
40
|
+
// Open picker labels
|
|
41
|
+
openDatePickerDialogue: (value, utils) => value !== null && utils.isValid(value) ? `Vælg dato, valgte dato er ${utils.format(value, 'fullDate')}` : 'Vælg dato',
|
|
42
|
+
openTimePickerDialogue: (value, utils) => value !== null && utils.isValid(value) ? `Vælg tidspunkt, valgte tidspunkt er ${utils.format(value, 'fullTime')}` : 'Vælg tidspunkt',
|
|
43
|
+
// Table labels
|
|
44
|
+
timeTableLabel: 'vælg tidspunkt',
|
|
45
|
+
dateTableLabel: 'vælg dato',
|
|
46
|
+
// Field section placeholders
|
|
47
|
+
fieldYearPlaceholder: params => 'Y'.repeat(params.digitAmount),
|
|
48
|
+
fieldMonthPlaceholder: params => params.contentType === 'letter' ? 'MMMM' : 'MM',
|
|
49
|
+
fieldDayPlaceholder: () => 'DD',
|
|
50
|
+
fieldWeekDayPlaceholder: params => params.contentType === 'letter' ? 'EEEE' : 'EE',
|
|
51
|
+
fieldHoursPlaceholder: () => 'hh',
|
|
52
|
+
fieldMinutesPlaceholder: () => 'mm',
|
|
53
|
+
fieldSecondsPlaceholder: () => 'ss',
|
|
54
|
+
fieldMeridiemPlaceholder: () => 'aa'
|
|
55
|
+
};
|
|
56
|
+
export const daDK = getPickersLocalization(daDKPickers);
|
package/modern/locales/nbNO.js
CHANGED
|
@@ -16,22 +16,20 @@ const nbNOPickers = {
|
|
|
16
16
|
okButtonLabel: 'OK',
|
|
17
17
|
todayButtonLabel: 'I dag',
|
|
18
18
|
// Toolbar titles
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
datePickerToolbarTitle: 'Velg dato',
|
|
20
|
+
dateTimePickerToolbarTitle: 'Velg dato & klokkeslett',
|
|
21
|
+
timePickerToolbarTitle: 'Velg klokkeslett',
|
|
22
|
+
dateRangePickerToolbarTitle: 'Velg datoperiode',
|
|
24
23
|
// Clock labels
|
|
25
24
|
clockLabelText: (view, time, adapter) => `Velg ${view}. ${time === null ? 'Ingen tid valgt' : `Valgt tid er ${adapter.format(time, 'fullTime')}`}`,
|
|
26
25
|
hoursClockNumberText: hours => `${hours} timer`,
|
|
27
26
|
minutesClockNumberText: minutes => `${minutes} minutter`,
|
|
28
27
|
secondsClockNumberText: seconds => `${seconds} sekunder`,
|
|
29
28
|
// Calendar labels
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
calendarWeekNumberHeaderLabel: 'Ukenummer',
|
|
30
|
+
calendarWeekNumberHeaderText: '#',
|
|
31
|
+
calendarWeekNumberAriaLabelText: weekNumber => `Uke ${weekNumber}`,
|
|
32
|
+
calendarWeekNumberText: weekNumber => `${weekNumber}`,
|
|
35
33
|
// Open picker labels
|
|
36
34
|
openDatePickerDialogue: (value, utils) => value !== null && utils.isValid(value) ? `Velg dato, valgt dato er ${utils.format(value, 'fullDate')}` : 'Velg dato',
|
|
37
35
|
openTimePickerDialogue: (value, utils) => value !== null && utils.isValid(value) ? `Velg tid, valgt tid er ${utils.format(value, 'fullTime')}` : 'Velg tid',
|