@mui/x-date-pickers 8.17.0 → 8.18.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/CHANGELOG.md +100 -0
- package/DatePicker/DatePicker.js +8 -2
- package/DateTimePicker/DateTimePicker.js +8 -2
- package/DateTimePicker/DateTimePickerToolbar.js +2 -1
- package/DesktopDatePicker/DesktopDatePicker.js +8 -2
- package/DesktopDateTimePicker/DesktopDateTimePicker.js +8 -2
- package/DesktopTimePicker/DesktopTimePicker.js +8 -2
- package/MobileDatePicker/MobileDatePicker.js +8 -2
- package/MobileDateTimePicker/MobileDateTimePicker.js +8 -2
- package/MobileTimePicker/MobileTimePicker.js +8 -2
- package/PickersShortcuts/PickersShortcuts.js +2 -1
- package/StaticDatePicker/StaticDatePicker.js +8 -2
- package/StaticDateTimePicker/StaticDateTimePicker.js +8 -2
- package/StaticTimePicker/StaticTimePicker.js +8 -2
- package/TimePicker/TimePicker.js +8 -2
- package/TimePicker/TimePickerToolbar.js +2 -1
- package/esm/DatePicker/DatePicker.js +8 -2
- package/esm/DateTimePicker/DateTimePicker.js +8 -2
- package/esm/DateTimePicker/DateTimePickerToolbar.js +2 -1
- package/esm/DesktopDatePicker/DesktopDatePicker.js +8 -2
- package/esm/DesktopDateTimePicker/DesktopDateTimePicker.js +8 -2
- package/esm/DesktopTimePicker/DesktopTimePicker.js +8 -2
- package/esm/MobileDatePicker/MobileDatePicker.js +8 -2
- package/esm/MobileDateTimePicker/MobileDateTimePicker.js +8 -2
- package/esm/MobileTimePicker/MobileTimePicker.js +8 -2
- package/esm/PickersShortcuts/PickersShortcuts.js +2 -1
- package/esm/StaticDatePicker/StaticDatePicker.js +8 -2
- package/esm/StaticDateTimePicker/StaticDateTimePicker.js +8 -2
- package/esm/StaticTimePicker/StaticTimePicker.js +8 -2
- package/esm/TimePicker/TimePicker.js +8 -2
- package/esm/TimePicker/TimePickerToolbar.js +2 -1
- package/esm/index.js +1 -1
- package/esm/internals/components/PickerProvider.d.ts +8 -0
- package/esm/internals/hooks/useField/useFieldState.js +19 -10
- package/esm/internals/hooks/useField/useFieldV6TextField.js +1 -0
- package/esm/internals/hooks/usePicker/hooks/useValueAndOpenStates.js +14 -2
- package/esm/internals/hooks/usePicker/usePicker.js +13 -5
- package/esm/internals/hooks/usePicker/usePicker.types.d.ts +8 -2
- package/esm/models/pickers.d.ts +8 -0
- package/index.js +1 -1
- package/internals/components/PickerProvider.d.ts +8 -0
- package/internals/hooks/useField/useFieldState.js +19 -10
- package/internals/hooks/useField/useFieldV6TextField.js +1 -0
- package/internals/hooks/usePicker/hooks/useValueAndOpenStates.js +14 -2
- package/internals/hooks/usePicker/usePicker.js +13 -5
- package/internals/hooks/usePicker/usePicker.types.d.ts +8 -2
- package/models/pickers.d.ts +8 -0
- package/package.json +3 -3
|
@@ -275,7 +275,10 @@ MobileDateTimePicker.propTypes = {
|
|
|
275
275
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
276
276
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
277
277
|
* @param {TValue} value The value that was just accepted.
|
|
278
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
278
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this acceptance:
|
|
279
|
+
* - `validationError`: validation result of the current value
|
|
280
|
+
* - `source`: source of the acceptance. One of 'field' | 'picker' | 'unknown'
|
|
281
|
+
* - `shortcut` (optional): the shortcut metadata if the value was accepted via a shortcut selection
|
|
279
282
|
*/
|
|
280
283
|
onAccept: PropTypes.func,
|
|
281
284
|
/**
|
|
@@ -283,7 +286,10 @@ MobileDateTimePicker.propTypes = {
|
|
|
283
286
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
284
287
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
285
288
|
* @param {TValue} value The new value.
|
|
286
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
289
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this change:
|
|
290
|
+
* - `validationError`: validation result of the current value
|
|
291
|
+
* - `source`: source of the change. One of 'field' | 'view' | 'unknown'
|
|
292
|
+
* - `shortcut` (optional): the shortcut metadata if the change was triggered by a shortcut selection
|
|
287
293
|
*/
|
|
288
294
|
onChange: PropTypes.func,
|
|
289
295
|
/**
|
|
@@ -176,7 +176,10 @@ MobileTimePicker.propTypes = {
|
|
|
176
176
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
177
177
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
178
178
|
* @param {TValue} value The value that was just accepted.
|
|
179
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
179
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this acceptance:
|
|
180
|
+
* - `validationError`: validation result of the current value
|
|
181
|
+
* - `source`: source of the acceptance. One of 'field' | 'picker' | 'unknown'
|
|
182
|
+
* - `shortcut` (optional): the shortcut metadata if the value was accepted via a shortcut selection
|
|
180
183
|
*/
|
|
181
184
|
onAccept: PropTypes.func,
|
|
182
185
|
/**
|
|
@@ -184,7 +187,10 @@ MobileTimePicker.propTypes = {
|
|
|
184
187
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
185
188
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
186
189
|
* @param {TValue} value The new value.
|
|
187
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
190
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this change:
|
|
191
|
+
* - `validationError`: validation result of the current value
|
|
192
|
+
* - `source`: source of the change. One of 'field' | 'view' | 'unknown'
|
|
193
|
+
* - `shortcut` (optional): the shortcut metadata if the change was triggered by a shortcut selection
|
|
188
194
|
*/
|
|
189
195
|
onChange: PropTypes.func,
|
|
190
196
|
/**
|
|
@@ -142,7 +142,10 @@ StaticDatePicker.propTypes = {
|
|
|
142
142
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
143
143
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
144
144
|
* @param {TValue} value The value that was just accepted.
|
|
145
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
145
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this acceptance:
|
|
146
|
+
* - `validationError`: validation result of the current value
|
|
147
|
+
* - `source`: source of the acceptance. One of 'field' | 'picker' | 'unknown'
|
|
148
|
+
* - `shortcut` (optional): the shortcut metadata if the value was accepted via a shortcut selection
|
|
146
149
|
*/
|
|
147
150
|
onAccept: PropTypes.func,
|
|
148
151
|
/**
|
|
@@ -150,7 +153,10 @@ StaticDatePicker.propTypes = {
|
|
|
150
153
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
151
154
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
152
155
|
* @param {TValue} value The new value.
|
|
153
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
156
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this change:
|
|
157
|
+
* - `validationError`: validation result of the current value
|
|
158
|
+
* - `source`: source of the change. One of 'field' | 'view' | 'unknown'
|
|
159
|
+
* - `shortcut` (optional): the shortcut metadata if the change was triggered by a shortcut selection
|
|
154
160
|
*/
|
|
155
161
|
onChange: PropTypes.func,
|
|
156
162
|
/**
|
|
@@ -229,7 +229,10 @@ StaticDateTimePicker.propTypes = {
|
|
|
229
229
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
230
230
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
231
231
|
* @param {TValue} value The value that was just accepted.
|
|
232
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
232
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this acceptance:
|
|
233
|
+
* - `validationError`: validation result of the current value
|
|
234
|
+
* - `source`: source of the acceptance. One of 'field' | 'picker' | 'unknown'
|
|
235
|
+
* - `shortcut` (optional): the shortcut metadata if the value was accepted via a shortcut selection
|
|
233
236
|
*/
|
|
234
237
|
onAccept: PropTypes.func,
|
|
235
238
|
/**
|
|
@@ -237,7 +240,10 @@ StaticDateTimePicker.propTypes = {
|
|
|
237
240
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
238
241
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
239
242
|
* @param {TValue} value The new value.
|
|
240
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
243
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this change:
|
|
244
|
+
* - `validationError`: validation result of the current value
|
|
245
|
+
* - `source`: source of the change. One of 'field' | 'view' | 'unknown'
|
|
246
|
+
* - `shortcut` (optional): the shortcut metadata if the change was triggered by a shortcut selection
|
|
241
247
|
*/
|
|
242
248
|
onChange: PropTypes.func,
|
|
243
249
|
/**
|
|
@@ -132,7 +132,10 @@ StaticTimePicker.propTypes = {
|
|
|
132
132
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
133
133
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
134
134
|
* @param {TValue} value The value that was just accepted.
|
|
135
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
135
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this acceptance:
|
|
136
|
+
* - `validationError`: validation result of the current value
|
|
137
|
+
* - `source`: source of the acceptance. One of 'field' | 'picker' | 'unknown'
|
|
138
|
+
* - `shortcut` (optional): the shortcut metadata if the value was accepted via a shortcut selection
|
|
136
139
|
*/
|
|
137
140
|
onAccept: PropTypes.func,
|
|
138
141
|
/**
|
|
@@ -140,7 +143,10 @@ StaticTimePicker.propTypes = {
|
|
|
140
143
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
141
144
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
142
145
|
* @param {TValue} value The new value.
|
|
143
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
146
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this change:
|
|
147
|
+
* - `validationError`: validation result of the current value
|
|
148
|
+
* - `source`: source of the change. One of 'field' | 'view' | 'unknown'
|
|
149
|
+
* - `shortcut` (optional): the shortcut metadata if the change was triggered by a shortcut selection
|
|
144
150
|
*/
|
|
145
151
|
onChange: PropTypes.func,
|
|
146
152
|
/**
|
|
@@ -164,7 +164,10 @@ process.env.NODE_ENV !== "production" ? TimePicker.propTypes = {
|
|
|
164
164
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
165
165
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
166
166
|
* @param {TValue} value The value that was just accepted.
|
|
167
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
167
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this acceptance:
|
|
168
|
+
* - `validationError`: validation result of the current value
|
|
169
|
+
* - `source`: source of the acceptance. One of 'field' | 'picker' | 'unknown'
|
|
170
|
+
* - `shortcut` (optional): the shortcut metadata if the value was accepted via a shortcut selection
|
|
168
171
|
*/
|
|
169
172
|
onAccept: PropTypes.func,
|
|
170
173
|
/**
|
|
@@ -172,7 +175,10 @@ process.env.NODE_ENV !== "production" ? TimePicker.propTypes = {
|
|
|
172
175
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
173
176
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
174
177
|
* @param {TValue} value The new value.
|
|
175
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
178
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this change:
|
|
179
|
+
* - `validationError`: validation result of the current value
|
|
180
|
+
* - `source`: source of the change. One of 'field' | 'view' | 'unknown'
|
|
181
|
+
* - `shortcut` (optional): the shortcut metadata if the change was triggered by a shortcut selection
|
|
176
182
|
*/
|
|
177
183
|
onChange: PropTypes.func,
|
|
178
184
|
/**
|
|
@@ -139,7 +139,8 @@ function TimePickerToolbar(inProps) {
|
|
|
139
139
|
meridiemMode,
|
|
140
140
|
handleMeridiemChange
|
|
141
141
|
} = useMeridiemMode(value, ampm, newValue => setValue(newValue, {
|
|
142
|
-
changeImportance: 'set'
|
|
142
|
+
changeImportance: 'set',
|
|
143
|
+
source: 'view'
|
|
143
144
|
}));
|
|
144
145
|
const formatSection = format => {
|
|
145
146
|
if (!adapter.isValid(value)) {
|
package/esm/index.js
CHANGED
|
@@ -232,6 +232,14 @@ export interface SetValueActionOptions<TError = string | null> {
|
|
|
232
232
|
* It should not be defined if the change does not come from a shortcut.
|
|
233
233
|
*/
|
|
234
234
|
shortcut?: PickersShortcutsItemContext;
|
|
235
|
+
/**
|
|
236
|
+
* Source of the change.
|
|
237
|
+
* Simplified to one of the following values:
|
|
238
|
+
* - 'field' (changes coming from the text field)
|
|
239
|
+
* - 'view' (any interaction inside the picker UI: views, toolbar, action bar, shortcuts, etc.)
|
|
240
|
+
* - 'unknown' (unspecified or third-party triggers)
|
|
241
|
+
*/
|
|
242
|
+
source?: 'field' | 'view' | 'unknown';
|
|
235
243
|
/**
|
|
236
244
|
* Whether the value should call `onChange` and `onAccept` when the value is not controlled and has never been modified.
|
|
237
245
|
* If `true`, the `onChange` and `onAccept` callback will only be fired if the value has been modified (and is not equal to the last published value).
|
|
@@ -68,14 +68,6 @@ export const useFieldState = parameters => {
|
|
|
68
68
|
value,
|
|
69
69
|
onError: internalPropsWithDefaults.onError
|
|
70
70
|
});
|
|
71
|
-
const error = React.useMemo(() => {
|
|
72
|
-
// only override when `error` is undefined.
|
|
73
|
-
// in case of multi input fields, the `error` value is provided externally and will always be defined.
|
|
74
|
-
if (errorProp !== undefined) {
|
|
75
|
-
return errorProp;
|
|
76
|
-
}
|
|
77
|
-
return hasValidationError;
|
|
78
|
-
}, [hasValidationError, errorProp]);
|
|
79
71
|
const localizedDigits = React.useMemo(() => getLocalizedDigits(adapter), [adapter]);
|
|
80
72
|
const sectionsValueBoundaries = React.useMemo(() => getSectionsBoundaries(adapter, localizedDigits, timezone), [adapter, localizedDigits, timezone]);
|
|
81
73
|
const getSectionsFromValue = React.useCallback(valueToAnalyze => fieldValueManager.getSectionsFromValue(valueToAnalyze, date => buildSectionsFromFormat({
|
|
@@ -130,6 +122,22 @@ export const useFieldState = parameters => {
|
|
|
130
122
|
const activeSectionIndex = parsedSelectedSections === 'all' ? 0 : parsedSelectedSections;
|
|
131
123
|
const sectionOrder = React.useMemo(() => getSectionOrder(state.sections, isRtl && !enableAccessibleFieldDOMStructure), [state.sections, isRtl, enableAccessibleFieldDOMStructure]);
|
|
132
124
|
const areAllSectionsEmpty = React.useMemo(() => state.sections.every(section => section.value === ''), [state.sections]);
|
|
125
|
+
|
|
126
|
+
// When the field loses focus (no active section), consider partially filled sections as invalid.
|
|
127
|
+
// This enforces that the field must be entirely filled or entirely empty on blur.
|
|
128
|
+
const hasPartiallyFilledSectionsOnBlur = React.useMemo(() => {
|
|
129
|
+
if (activeSectionIndex != null) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
const filledSections = state.sections.filter(s => s.value !== '');
|
|
133
|
+
return filledSections.length > 0 && state.sections.length - filledSections.length > 0;
|
|
134
|
+
}, [state.sections, activeSectionIndex]);
|
|
135
|
+
const error = React.useMemo(() => {
|
|
136
|
+
if (errorProp !== undefined) {
|
|
137
|
+
return errorProp;
|
|
138
|
+
}
|
|
139
|
+
return hasValidationError || hasPartiallyFilledSectionsOnBlur;
|
|
140
|
+
}, [hasValidationError, hasPartiallyFilledSectionsOnBlur, errorProp]);
|
|
133
141
|
const publishValue = newValue => {
|
|
134
142
|
const context = {
|
|
135
143
|
validationError: validator({
|
|
@@ -278,11 +286,12 @@ export const useFieldState = parameters => {
|
|
|
278
286
|
|
|
279
287
|
/**
|
|
280
288
|
* If the previous date is not null,
|
|
281
|
-
* Then we publish the date as `
|
|
289
|
+
* Then we publish the date as `newActiveDate to prevent error state oscillation`.
|
|
290
|
+
* @link: https://github.com/mui/mui-x/issues/17967
|
|
282
291
|
*/
|
|
283
292
|
if (activeDate != null) {
|
|
284
293
|
setSectionUpdateToApplyOnNextInvalidDate(newSectionValue);
|
|
285
|
-
|
|
294
|
+
publishValue(fieldValueManager.updateDateInValue(value, section, newActiveDate));
|
|
286
295
|
}
|
|
287
296
|
|
|
288
297
|
/**
|
|
@@ -384,6 +384,7 @@ export const useFieldV6TextField = parameters => {
|
|
|
384
384
|
}));
|
|
385
385
|
return _extends({}, forwardedProps, {
|
|
386
386
|
error,
|
|
387
|
+
'aria-invalid': error,
|
|
387
388
|
clearable: Boolean(clearable && !areAllSectionsEmpty && !readOnly && !disabled),
|
|
388
389
|
onBlur: handleContainerBlur,
|
|
389
390
|
onClick: handleInputClick,
|
|
@@ -105,6 +105,7 @@ export function useValueAndOpenStates(parameters) {
|
|
|
105
105
|
skipPublicationIfPristine = false,
|
|
106
106
|
validationError,
|
|
107
107
|
shortcut,
|
|
108
|
+
source,
|
|
108
109
|
shouldClose = changeImportance === 'accept'
|
|
109
110
|
} = options ?? {};
|
|
110
111
|
let shouldFireOnChange;
|
|
@@ -127,8 +128,18 @@ export function useValueAndOpenStates(parameters) {
|
|
|
127
128
|
let cachedContext = null;
|
|
128
129
|
const getContext = () => {
|
|
129
130
|
if (!cachedContext) {
|
|
131
|
+
let inferredSource;
|
|
132
|
+
if (source) {
|
|
133
|
+
inferredSource = source;
|
|
134
|
+
} else if (shortcut) {
|
|
135
|
+
inferredSource = 'view';
|
|
136
|
+
} else {
|
|
137
|
+
// Default to unknown when not explicitly tagged by a picker call site
|
|
138
|
+
inferredSource = 'unknown';
|
|
139
|
+
}
|
|
130
140
|
cachedContext = {
|
|
131
|
-
validationError: validationError == null ? getValidationErrorForNewValue(newValue) : validationError
|
|
141
|
+
validationError: validationError == null ? getValidationErrorForNewValue(newValue) : validationError,
|
|
142
|
+
source: inferredSource
|
|
132
143
|
};
|
|
133
144
|
if (shortcut) {
|
|
134
145
|
cachedContext.shortcut = shortcut;
|
|
@@ -165,7 +176,8 @@ export function useValueAndOpenStates(parameters) {
|
|
|
165
176
|
return;
|
|
166
177
|
}
|
|
167
178
|
setValue(newValue, {
|
|
168
|
-
changeImportance: selectionState === 'finish' && closeOnSelect ? 'accept' : 'set'
|
|
179
|
+
changeImportance: selectionState === 'finish' && closeOnSelect ? 'accept' : 'set',
|
|
180
|
+
source: 'view'
|
|
169
181
|
});
|
|
170
182
|
});
|
|
171
183
|
|
|
@@ -111,15 +111,23 @@ export const usePicker = ({
|
|
|
111
111
|
autoFocus: autoFocusView,
|
|
112
112
|
getStepNavigation
|
|
113
113
|
});
|
|
114
|
-
const clearValue = useEventCallback(() => setValue(valueManager.emptyValue
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
const clearValue = useEventCallback(() => setValue(valueManager.emptyValue, {
|
|
115
|
+
source: 'view'
|
|
116
|
+
}));
|
|
117
|
+
const setValueToToday = useEventCallback(() => setValue(valueManager.getTodayValue(adapter, timezone, valueType), {
|
|
118
|
+
source: 'view'
|
|
119
|
+
}));
|
|
120
|
+
const acceptValueChanges = useEventCallback(() => setValue(value, {
|
|
121
|
+
source: 'view'
|
|
122
|
+
}));
|
|
117
123
|
const cancelValueChanges = useEventCallback(() => setValue(state.lastCommittedValue, {
|
|
118
|
-
skipPublicationIfPristine: true
|
|
124
|
+
skipPublicationIfPristine: true,
|
|
125
|
+
source: 'view'
|
|
119
126
|
}));
|
|
120
127
|
const dismissViews = useEventCallback(() => {
|
|
121
128
|
setValue(value, {
|
|
122
|
-
skipPublicationIfPristine: true
|
|
129
|
+
skipPublicationIfPristine: true,
|
|
130
|
+
source: 'view'
|
|
123
131
|
});
|
|
124
132
|
});
|
|
125
133
|
const {
|
|
@@ -28,7 +28,10 @@ export interface UsePickerBaseProps<TValue extends PickerValidValue, TView exten
|
|
|
28
28
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
29
29
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
30
30
|
* @param {TValue} value The new value.
|
|
31
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
31
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this change:
|
|
32
|
+
* - `validationError`: validation result of the current value
|
|
33
|
+
* - `source`: source of the change. One of 'field' | 'view' | 'unknown'
|
|
34
|
+
* - `shortcut` (optional): the shortcut metadata if the change was triggered by a shortcut selection
|
|
32
35
|
*/
|
|
33
36
|
onChange?: (value: TValue, context: PickerChangeHandlerContext<TError>) => void;
|
|
34
37
|
/**
|
|
@@ -36,7 +39,10 @@ export interface UsePickerBaseProps<TValue extends PickerValidValue, TView exten
|
|
|
36
39
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
37
40
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
38
41
|
* @param {TValue} value The value that was just accepted.
|
|
39
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
42
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this acceptance:
|
|
43
|
+
* - `validationError`: validation result of the current value
|
|
44
|
+
* - `source`: source of the acceptance. One of 'field' | 'picker' | 'unknown'
|
|
45
|
+
* - `shortcut` (optional): the shortcut metadata if the value was accepted via a shortcut selection
|
|
40
46
|
*/
|
|
41
47
|
onAccept?: (value: TValue, context: PickerChangeHandlerContext<TError>) => void;
|
|
42
48
|
/**
|
package/esm/models/pickers.d.ts
CHANGED
|
@@ -2,6 +2,14 @@ import { PickerOrientation, PickerVariant } from "../internals/models/common.js"
|
|
|
2
2
|
import type { PickersShortcutsItemContext } from "../PickersShortcuts/index.js";
|
|
3
3
|
export interface PickerChangeHandlerContext<TError> {
|
|
4
4
|
validationError: TError;
|
|
5
|
+
/**
|
|
6
|
+
* Source of the change that triggered `onChange` or `onAccept`.
|
|
7
|
+
* Simplified to one of the following values:
|
|
8
|
+
* - 'field' (changes coming from the text field)
|
|
9
|
+
* - 'view' (any interaction inside the picker UI: views, toolbar, action bar, shortcuts, etc.)
|
|
10
|
+
* - 'unknown' (unspecified or third-party triggers)
|
|
11
|
+
*/
|
|
12
|
+
source: 'field' | 'view' | 'unknown';
|
|
5
13
|
/**
|
|
6
14
|
* Shortcut causing this `onChange` or `onAccept` call.
|
|
7
15
|
* If the call has not been caused by a shortcut selection, this property will be `undefined`.
|
package/index.js
CHANGED
|
@@ -232,6 +232,14 @@ export interface SetValueActionOptions<TError = string | null> {
|
|
|
232
232
|
* It should not be defined if the change does not come from a shortcut.
|
|
233
233
|
*/
|
|
234
234
|
shortcut?: PickersShortcutsItemContext;
|
|
235
|
+
/**
|
|
236
|
+
* Source of the change.
|
|
237
|
+
* Simplified to one of the following values:
|
|
238
|
+
* - 'field' (changes coming from the text field)
|
|
239
|
+
* - 'view' (any interaction inside the picker UI: views, toolbar, action bar, shortcuts, etc.)
|
|
240
|
+
* - 'unknown' (unspecified or third-party triggers)
|
|
241
|
+
*/
|
|
242
|
+
source?: 'field' | 'view' | 'unknown';
|
|
235
243
|
/**
|
|
236
244
|
* Whether the value should call `onChange` and `onAccept` when the value is not controlled and has never been modified.
|
|
237
245
|
* If `true`, the `onChange` and `onAccept` callback will only be fired if the value has been modified (and is not equal to the last published value).
|
|
@@ -75,14 +75,6 @@ const useFieldState = parameters => {
|
|
|
75
75
|
value,
|
|
76
76
|
onError: internalPropsWithDefaults.onError
|
|
77
77
|
});
|
|
78
|
-
const error = React.useMemo(() => {
|
|
79
|
-
// only override when `error` is undefined.
|
|
80
|
-
// in case of multi input fields, the `error` value is provided externally and will always be defined.
|
|
81
|
-
if (errorProp !== undefined) {
|
|
82
|
-
return errorProp;
|
|
83
|
-
}
|
|
84
|
-
return hasValidationError;
|
|
85
|
-
}, [hasValidationError, errorProp]);
|
|
86
78
|
const localizedDigits = React.useMemo(() => (0, _useField.getLocalizedDigits)(adapter), [adapter]);
|
|
87
79
|
const sectionsValueBoundaries = React.useMemo(() => (0, _useField.getSectionsBoundaries)(adapter, localizedDigits, timezone), [adapter, localizedDigits, timezone]);
|
|
88
80
|
const getSectionsFromValue = React.useCallback(valueToAnalyze => fieldValueManager.getSectionsFromValue(valueToAnalyze, date => (0, _buildSectionsFromFormat.buildSectionsFromFormat)({
|
|
@@ -137,6 +129,22 @@ const useFieldState = parameters => {
|
|
|
137
129
|
const activeSectionIndex = parsedSelectedSections === 'all' ? 0 : parsedSelectedSections;
|
|
138
130
|
const sectionOrder = React.useMemo(() => (0, _useField.getSectionOrder)(state.sections, isRtl && !enableAccessibleFieldDOMStructure), [state.sections, isRtl, enableAccessibleFieldDOMStructure]);
|
|
139
131
|
const areAllSectionsEmpty = React.useMemo(() => state.sections.every(section => section.value === ''), [state.sections]);
|
|
132
|
+
|
|
133
|
+
// When the field loses focus (no active section), consider partially filled sections as invalid.
|
|
134
|
+
// This enforces that the field must be entirely filled or entirely empty on blur.
|
|
135
|
+
const hasPartiallyFilledSectionsOnBlur = React.useMemo(() => {
|
|
136
|
+
if (activeSectionIndex != null) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const filledSections = state.sections.filter(s => s.value !== '');
|
|
140
|
+
return filledSections.length > 0 && state.sections.length - filledSections.length > 0;
|
|
141
|
+
}, [state.sections, activeSectionIndex]);
|
|
142
|
+
const error = React.useMemo(() => {
|
|
143
|
+
if (errorProp !== undefined) {
|
|
144
|
+
return errorProp;
|
|
145
|
+
}
|
|
146
|
+
return hasValidationError || hasPartiallyFilledSectionsOnBlur;
|
|
147
|
+
}, [hasValidationError, hasPartiallyFilledSectionsOnBlur, errorProp]);
|
|
140
148
|
const publishValue = newValue => {
|
|
141
149
|
const context = {
|
|
142
150
|
validationError: validator({
|
|
@@ -285,11 +293,12 @@ const useFieldState = parameters => {
|
|
|
285
293
|
|
|
286
294
|
/**
|
|
287
295
|
* If the previous date is not null,
|
|
288
|
-
* Then we publish the date as `
|
|
296
|
+
* Then we publish the date as `newActiveDate to prevent error state oscillation`.
|
|
297
|
+
* @link: https://github.com/mui/mui-x/issues/17967
|
|
289
298
|
*/
|
|
290
299
|
if (activeDate != null) {
|
|
291
300
|
setSectionUpdateToApplyOnNextInvalidDate(newSectionValue);
|
|
292
|
-
|
|
301
|
+
publishValue(fieldValueManager.updateDateInValue(value, section, newActiveDate));
|
|
293
302
|
}
|
|
294
303
|
|
|
295
304
|
/**
|
|
@@ -392,6 +392,7 @@ const useFieldV6TextField = parameters => {
|
|
|
392
392
|
}));
|
|
393
393
|
return (0, _extends2.default)({}, forwardedProps, {
|
|
394
394
|
error,
|
|
395
|
+
'aria-invalid': error,
|
|
395
396
|
clearable: Boolean(clearable && !areAllSectionsEmpty && !readOnly && !disabled),
|
|
396
397
|
onBlur: handleContainerBlur,
|
|
397
398
|
onClick: handleInputClick,
|
|
@@ -112,6 +112,7 @@ function useValueAndOpenStates(parameters) {
|
|
|
112
112
|
skipPublicationIfPristine = false,
|
|
113
113
|
validationError,
|
|
114
114
|
shortcut,
|
|
115
|
+
source,
|
|
115
116
|
shouldClose = changeImportance === 'accept'
|
|
116
117
|
} = options ?? {};
|
|
117
118
|
let shouldFireOnChange;
|
|
@@ -134,8 +135,18 @@ function useValueAndOpenStates(parameters) {
|
|
|
134
135
|
let cachedContext = null;
|
|
135
136
|
const getContext = () => {
|
|
136
137
|
if (!cachedContext) {
|
|
138
|
+
let inferredSource;
|
|
139
|
+
if (source) {
|
|
140
|
+
inferredSource = source;
|
|
141
|
+
} else if (shortcut) {
|
|
142
|
+
inferredSource = 'view';
|
|
143
|
+
} else {
|
|
144
|
+
// Default to unknown when not explicitly tagged by a picker call site
|
|
145
|
+
inferredSource = 'unknown';
|
|
146
|
+
}
|
|
137
147
|
cachedContext = {
|
|
138
|
-
validationError: validationError == null ? getValidationErrorForNewValue(newValue) : validationError
|
|
148
|
+
validationError: validationError == null ? getValidationErrorForNewValue(newValue) : validationError,
|
|
149
|
+
source: inferredSource
|
|
139
150
|
};
|
|
140
151
|
if (shortcut) {
|
|
141
152
|
cachedContext.shortcut = shortcut;
|
|
@@ -172,7 +183,8 @@ function useValueAndOpenStates(parameters) {
|
|
|
172
183
|
return;
|
|
173
184
|
}
|
|
174
185
|
setValue(newValue, {
|
|
175
|
-
changeImportance: selectionState === 'finish' && closeOnSelect ? 'accept' : 'set'
|
|
186
|
+
changeImportance: selectionState === 'finish' && closeOnSelect ? 'accept' : 'set',
|
|
187
|
+
source: 'view'
|
|
176
188
|
});
|
|
177
189
|
});
|
|
178
190
|
|
|
@@ -118,15 +118,23 @@ const usePicker = ({
|
|
|
118
118
|
autoFocus: autoFocusView,
|
|
119
119
|
getStepNavigation
|
|
120
120
|
});
|
|
121
|
-
const clearValue = (0, _useEventCallback.default)(() => setValue(valueManager.emptyValue
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
const clearValue = (0, _useEventCallback.default)(() => setValue(valueManager.emptyValue, {
|
|
122
|
+
source: 'view'
|
|
123
|
+
}));
|
|
124
|
+
const setValueToToday = (0, _useEventCallback.default)(() => setValue(valueManager.getTodayValue(adapter, timezone, valueType), {
|
|
125
|
+
source: 'view'
|
|
126
|
+
}));
|
|
127
|
+
const acceptValueChanges = (0, _useEventCallback.default)(() => setValue(value, {
|
|
128
|
+
source: 'view'
|
|
129
|
+
}));
|
|
124
130
|
const cancelValueChanges = (0, _useEventCallback.default)(() => setValue(state.lastCommittedValue, {
|
|
125
|
-
skipPublicationIfPristine: true
|
|
131
|
+
skipPublicationIfPristine: true,
|
|
132
|
+
source: 'view'
|
|
126
133
|
}));
|
|
127
134
|
const dismissViews = (0, _useEventCallback.default)(() => {
|
|
128
135
|
setValue(value, {
|
|
129
|
-
skipPublicationIfPristine: true
|
|
136
|
+
skipPublicationIfPristine: true,
|
|
137
|
+
source: 'view'
|
|
130
138
|
});
|
|
131
139
|
});
|
|
132
140
|
const {
|
|
@@ -28,7 +28,10 @@ export interface UsePickerBaseProps<TValue extends PickerValidValue, TView exten
|
|
|
28
28
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
29
29
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
30
30
|
* @param {TValue} value The new value.
|
|
31
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
31
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this change:
|
|
32
|
+
* - `validationError`: validation result of the current value
|
|
33
|
+
* - `source`: source of the change. One of 'field' | 'view' | 'unknown'
|
|
34
|
+
* - `shortcut` (optional): the shortcut metadata if the change was triggered by a shortcut selection
|
|
32
35
|
*/
|
|
33
36
|
onChange?: (value: TValue, context: PickerChangeHandlerContext<TError>) => void;
|
|
34
37
|
/**
|
|
@@ -36,7 +39,10 @@ export interface UsePickerBaseProps<TValue extends PickerValidValue, TView exten
|
|
|
36
39
|
* @template TValue The value type. It will be the same type as `value` or `null`. It can be in `[start, end]` format in case of range value.
|
|
37
40
|
* @template TError The validation error type. It will be either `string` or a `null`. It can be in `[start, end]` format in case of range value.
|
|
38
41
|
* @param {TValue} value The value that was just accepted.
|
|
39
|
-
* @param {FieldChangeHandlerContext<TError>} context
|
|
42
|
+
* @param {FieldChangeHandlerContext<TError>} context Context about this acceptance:
|
|
43
|
+
* - `validationError`: validation result of the current value
|
|
44
|
+
* - `source`: source of the acceptance. One of 'field' | 'picker' | 'unknown'
|
|
45
|
+
* - `shortcut` (optional): the shortcut metadata if the value was accepted via a shortcut selection
|
|
40
46
|
*/
|
|
41
47
|
onAccept?: (value: TValue, context: PickerChangeHandlerContext<TError>) => void;
|
|
42
48
|
/**
|
package/models/pickers.d.ts
CHANGED
|
@@ -2,6 +2,14 @@ import { PickerOrientation, PickerVariant } from "../internals/models/common.js"
|
|
|
2
2
|
import type { PickersShortcutsItemContext } from "../PickersShortcuts/index.js";
|
|
3
3
|
export interface PickerChangeHandlerContext<TError> {
|
|
4
4
|
validationError: TError;
|
|
5
|
+
/**
|
|
6
|
+
* Source of the change that triggered `onChange` or `onAccept`.
|
|
7
|
+
* Simplified to one of the following values:
|
|
8
|
+
* - 'field' (changes coming from the text field)
|
|
9
|
+
* - 'view' (any interaction inside the picker UI: views, toolbar, action bar, shortcuts, etc.)
|
|
10
|
+
* - 'unknown' (unspecified or third-party triggers)
|
|
11
|
+
*/
|
|
12
|
+
source: 'field' | 'view' | 'unknown';
|
|
5
13
|
/**
|
|
6
14
|
* Shortcut causing this `onChange` or `onAccept` call.
|
|
7
15
|
* If the call has not been caused by a shortcut selection, this property will be `undefined`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mui/x-date-pickers",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.18.0",
|
|
4
4
|
"author": "MUI Team",
|
|
5
5
|
"description": "The community edition of the MUI X Date and Time Picker components.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@babel/runtime": "^7.28.4",
|
|
37
|
-
"@mui/utils": "^7.3.
|
|
37
|
+
"@mui/utils": "^7.3.5",
|
|
38
38
|
"@types/react-transition-group": "^4.4.12",
|
|
39
39
|
"clsx": "^2.1.1",
|
|
40
40
|
"prop-types": "^15.8.1",
|
|
41
41
|
"react-transition-group": "^4.4.5",
|
|
42
|
-
"@mui/x-internals": "8.
|
|
42
|
+
"@mui/x-internals": "8.18.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@emotion/react": "^11.9.0",
|