@mui/x-date-pickers 8.0.0-beta.0 → 8.0.0-beta.1

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.
Files changed (165) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/DateCalendar/DayCalendar.d.ts +3 -7
  3. package/DateCalendar/DayCalendar.js +16 -8
  4. package/DateCalendar/index.d.ts +0 -1
  5. package/DateField/useDateField.d.ts +1 -1
  6. package/DateField/useDateField.js +2 -16
  7. package/DatePicker/DatePicker.js +1 -0
  8. package/DateTimeField/useDateTimeField.d.ts +1 -1
  9. package/DateTimeField/useDateTimeField.js +2 -16
  10. package/DateTimePicker/DateTimePicker.js +1 -0
  11. package/DesktopDatePicker/DesktopDatePicker.js +1 -0
  12. package/DesktopDateTimePicker/DesktopDateTimePicker.js +1 -0
  13. package/DesktopTimePicker/DesktopTimePicker.js +1 -0
  14. package/MobileDatePicker/MobileDatePicker.js +1 -0
  15. package/MobileDateTimePicker/MobileDateTimePicker.js +1 -0
  16. package/MobileTimePicker/MobileTimePicker.js +1 -0
  17. package/PickersDay/PickersDay.d.ts +1 -72
  18. package/PickersDay/PickersDay.js +30 -28
  19. package/PickersDay/PickersDay.types.d.ts +114 -0
  20. package/PickersDay/PickersDay.types.js +5 -0
  21. package/PickersDay/index.d.ts +1 -1
  22. package/PickersDay/usePickerDayOwnerState.d.ts +14 -0
  23. package/PickersDay/usePickerDayOwnerState.js +40 -0
  24. package/TimeField/useTimeField.d.ts +1 -1
  25. package/TimeField/useTimeField.js +2 -16
  26. package/TimePicker/TimePicker.js +1 -0
  27. package/esm/DateCalendar/DayCalendar.d.ts +3 -7
  28. package/esm/DateCalendar/DayCalendar.js +16 -8
  29. package/esm/DateCalendar/index.d.ts +0 -1
  30. package/esm/DateField/useDateField.d.ts +1 -1
  31. package/esm/DateField/useDateField.js +3 -17
  32. package/esm/DatePicker/DatePicker.js +1 -0
  33. package/esm/DateTimeField/useDateTimeField.d.ts +1 -1
  34. package/esm/DateTimeField/useDateTimeField.js +3 -17
  35. package/esm/DateTimePicker/DateTimePicker.js +1 -0
  36. package/esm/DesktopDatePicker/DesktopDatePicker.js +1 -0
  37. package/esm/DesktopDateTimePicker/DesktopDateTimePicker.js +1 -0
  38. package/esm/DesktopTimePicker/DesktopTimePicker.js +1 -0
  39. package/esm/MobileDatePicker/MobileDatePicker.js +1 -0
  40. package/esm/MobileDateTimePicker/MobileDateTimePicker.js +1 -0
  41. package/esm/MobileTimePicker/MobileTimePicker.js +1 -0
  42. package/esm/PickersDay/PickersDay.d.ts +1 -72
  43. package/esm/PickersDay/PickersDay.js +30 -28
  44. package/esm/PickersDay/PickersDay.types.d.ts +114 -0
  45. package/esm/PickersDay/PickersDay.types.js +1 -0
  46. package/esm/PickersDay/index.d.ts +1 -1
  47. package/esm/PickersDay/usePickerDayOwnerState.d.ts +14 -0
  48. package/esm/PickersDay/usePickerDayOwnerState.js +32 -0
  49. package/esm/TimeField/useTimeField.d.ts +1 -1
  50. package/esm/TimeField/useTimeField.js +3 -17
  51. package/esm/TimePicker/TimePicker.js +1 -0
  52. package/esm/hooks/useSplitFieldProps.d.ts +1 -1
  53. package/esm/index.js +1 -1
  54. package/esm/internals/components/PickerFieldUI.d.ts +5 -5
  55. package/esm/internals/hooks/useField/buildSectionsFromFormat.d.ts +2 -2
  56. package/esm/internals/hooks/useField/buildSectionsFromFormat.js +9 -9
  57. package/esm/internals/hooks/useField/index.d.ts +1 -1
  58. package/esm/internals/hooks/useField/useField.d.ts +2 -4
  59. package/esm/internals/hooks/useField/useField.js +5 -229
  60. package/esm/internals/hooks/useField/useField.types.d.ts +48 -68
  61. package/esm/internals/hooks/useField/useField.utils.d.ts +2 -5
  62. package/esm/internals/hooks/useField/useField.utils.js +7 -92
  63. package/esm/internals/hooks/useField/useFieldCharacterEditing.d.ts +19 -26
  64. package/esm/internals/hooks/useField/useFieldCharacterEditing.js +42 -60
  65. package/esm/internals/hooks/useField/useFieldRootHandleKeyDown.d.ts +16 -0
  66. package/esm/internals/hooks/useField/useFieldRootHandleKeyDown.js +204 -0
  67. package/esm/internals/hooks/useField/useFieldState.d.ts +23 -13
  68. package/esm/internals/hooks/useField/useFieldState.js +103 -30
  69. package/esm/internals/hooks/useField/useFieldV6TextField.d.ts +3 -3
  70. package/esm/internals/hooks/useField/useFieldV6TextField.js +202 -135
  71. package/esm/internals/hooks/useField/useFieldV7TextField.d.ts +3 -2
  72. package/esm/internals/hooks/useField/useFieldV7TextField.js +217 -153
  73. package/esm/internals/hooks/usePicker/usePicker.types.d.ts +1 -0
  74. package/esm/internals/index.d.ts +2 -1
  75. package/esm/internals/index.js +1 -0
  76. package/esm/managers/useDateManager.js +3 -3
  77. package/esm/managers/useDateTimeManager.js +3 -3
  78. package/esm/managers/useTimeManager.js +3 -3
  79. package/esm/models/fields.d.ts +2 -2
  80. package/esm/models/manager.d.ts +3 -2
  81. package/hooks/useSplitFieldProps.d.ts +1 -1
  82. package/index.js +1 -1
  83. package/internals/components/PickerFieldUI.d.ts +5 -5
  84. package/internals/hooks/useField/buildSectionsFromFormat.d.ts +2 -2
  85. package/internals/hooks/useField/buildSectionsFromFormat.js +9 -9
  86. package/internals/hooks/useField/index.d.ts +1 -1
  87. package/internals/hooks/useField/useField.d.ts +2 -4
  88. package/internals/hooks/useField/useField.js +5 -231
  89. package/internals/hooks/useField/useField.types.d.ts +48 -68
  90. package/internals/hooks/useField/useField.utils.d.ts +2 -5
  91. package/internals/hooks/useField/useField.utils.js +8 -94
  92. package/internals/hooks/useField/useFieldCharacterEditing.d.ts +19 -26
  93. package/internals/hooks/useField/useFieldCharacterEditing.js +41 -59
  94. package/internals/hooks/useField/useFieldRootHandleKeyDown.d.ts +16 -0
  95. package/internals/hooks/useField/useFieldRootHandleKeyDown.js +210 -0
  96. package/internals/hooks/useField/useFieldState.d.ts +23 -13
  97. package/internals/hooks/useField/useFieldState.js +102 -29
  98. package/internals/hooks/useField/useFieldV6TextField.d.ts +3 -3
  99. package/internals/hooks/useField/useFieldV6TextField.js +202 -135
  100. package/internals/hooks/useField/useFieldV7TextField.d.ts +3 -2
  101. package/internals/hooks/useField/useFieldV7TextField.js +218 -154
  102. package/internals/hooks/usePicker/usePicker.types.d.ts +1 -0
  103. package/internals/index.d.ts +2 -1
  104. package/internals/index.js +7 -0
  105. package/managers/useDateManager.js +3 -3
  106. package/managers/useDateTimeManager.js +3 -3
  107. package/managers/useTimeManager.js +3 -3
  108. package/models/fields.d.ts +2 -2
  109. package/models/manager.d.ts +3 -2
  110. package/modern/DateCalendar/DayCalendar.d.ts +3 -7
  111. package/modern/DateCalendar/DayCalendar.js +16 -8
  112. package/modern/DateCalendar/index.d.ts +0 -1
  113. package/modern/DateField/useDateField.d.ts +1 -1
  114. package/modern/DateField/useDateField.js +3 -17
  115. package/modern/DatePicker/DatePicker.js +1 -0
  116. package/modern/DateTimeField/useDateTimeField.d.ts +1 -1
  117. package/modern/DateTimeField/useDateTimeField.js +3 -17
  118. package/modern/DateTimePicker/DateTimePicker.js +1 -0
  119. package/modern/DesktopDatePicker/DesktopDatePicker.js +1 -0
  120. package/modern/DesktopDateTimePicker/DesktopDateTimePicker.js +1 -0
  121. package/modern/DesktopTimePicker/DesktopTimePicker.js +1 -0
  122. package/modern/MobileDatePicker/MobileDatePicker.js +1 -0
  123. package/modern/MobileDateTimePicker/MobileDateTimePicker.js +1 -0
  124. package/modern/MobileTimePicker/MobileTimePicker.js +1 -0
  125. package/modern/PickersDay/PickersDay.d.ts +1 -72
  126. package/modern/PickersDay/PickersDay.js +30 -28
  127. package/modern/PickersDay/PickersDay.types.d.ts +114 -0
  128. package/modern/PickersDay/PickersDay.types.js +1 -0
  129. package/modern/PickersDay/index.d.ts +1 -1
  130. package/modern/PickersDay/usePickerDayOwnerState.d.ts +14 -0
  131. package/modern/PickersDay/usePickerDayOwnerState.js +32 -0
  132. package/modern/TimeField/useTimeField.d.ts +1 -1
  133. package/modern/TimeField/useTimeField.js +3 -17
  134. package/modern/TimePicker/TimePicker.js +1 -0
  135. package/modern/hooks/useSplitFieldProps.d.ts +1 -1
  136. package/modern/index.js +1 -1
  137. package/modern/internals/components/PickerFieldUI.d.ts +5 -5
  138. package/modern/internals/hooks/useField/buildSectionsFromFormat.d.ts +2 -2
  139. package/modern/internals/hooks/useField/buildSectionsFromFormat.js +9 -9
  140. package/modern/internals/hooks/useField/index.d.ts +1 -1
  141. package/modern/internals/hooks/useField/useField.d.ts +2 -4
  142. package/modern/internals/hooks/useField/useField.js +5 -229
  143. package/modern/internals/hooks/useField/useField.types.d.ts +48 -68
  144. package/modern/internals/hooks/useField/useField.utils.d.ts +2 -5
  145. package/modern/internals/hooks/useField/useField.utils.js +7 -92
  146. package/modern/internals/hooks/useField/useFieldCharacterEditing.d.ts +19 -26
  147. package/modern/internals/hooks/useField/useFieldCharacterEditing.js +42 -60
  148. package/modern/internals/hooks/useField/useFieldRootHandleKeyDown.d.ts +16 -0
  149. package/modern/internals/hooks/useField/useFieldRootHandleKeyDown.js +204 -0
  150. package/modern/internals/hooks/useField/useFieldState.d.ts +23 -13
  151. package/modern/internals/hooks/useField/useFieldState.js +103 -30
  152. package/modern/internals/hooks/useField/useFieldV6TextField.d.ts +3 -3
  153. package/modern/internals/hooks/useField/useFieldV6TextField.js +202 -135
  154. package/modern/internals/hooks/useField/useFieldV7TextField.d.ts +3 -2
  155. package/modern/internals/hooks/useField/useFieldV7TextField.js +217 -153
  156. package/modern/internals/hooks/usePicker/usePicker.types.d.ts +1 -0
  157. package/modern/internals/index.d.ts +2 -1
  158. package/modern/internals/index.js +1 -0
  159. package/modern/managers/useDateManager.js +3 -3
  160. package/modern/managers/useDateTimeManager.js +3 -3
  161. package/modern/managers/useTimeManager.js +3 -3
  162. package/modern/models/fields.d.ts +2 -2
  163. package/modern/models/manager.d.ts +3 -2
  164. package/package.json +1 -1
  165. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,10 +1,17 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
3
  import { useRtl } from '@mui/system/RtlProvider';
4
+ import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
4
5
  import useEventCallback from '@mui/utils/useEventCallback';
6
+ import useTimeout from '@mui/utils/useTimeout';
5
7
  import useForkRef from '@mui/utils/useForkRef';
8
+ import { useSplitFieldProps } from "../../../hooks/index.js";
6
9
  import { getActiveElement } from "../../utils/utils.js";
7
10
  import { getSectionVisibleValue, isAndroid } from "./useField.utils.js";
11
+ import { useFieldCharacterEditing } from "./useFieldCharacterEditing.js";
12
+ import { useFieldRootHandleKeyDown } from "./useFieldRootHandleKeyDown.js";
13
+ import { useFieldState } from "./useFieldState.js";
14
+ import { useFieldInternalPropsWithDefaults } from "./useFieldInternalPropsWithDefaults.js";
8
15
  const cleanString = dirtyString => dirtyString.replace(/[\u2066\u2067\u2068\u2069]/g, '');
9
16
  export const addPositionPropertiesToSections = (sections, localizedDigits, isRtl) => {
10
17
  let position = 0;
@@ -33,116 +40,82 @@ export const addPositionPropertiesToSections = (sections, localizedDigits, isRtl
33
40
  }
34
41
  return newSections;
35
42
  };
36
- export const useFieldV6TextField = params => {
43
+ export const useFieldV6TextField = parameters => {
37
44
  const isRtl = useRtl();
38
- const focusTimeoutRef = React.useRef(undefined);
39
- const selectionSyncTimeoutRef = React.useRef(undefined);
45
+ const focusTimeout = useTimeout();
46
+ const selectionSyncTimeout = useTimeout();
40
47
  const {
41
- forwardedProps: {
42
- onFocus,
43
- onClick,
44
- onPaste,
45
- onBlur,
46
- inputRef: inputRefProp,
47
- placeholder: inPlaceholder
48
- },
49
- internalProps: {
50
- readOnly = false,
51
- disabled = false,
52
- focused
53
- },
54
- parsedSelectedSections,
48
+ props,
49
+ manager,
50
+ skipContextFieldRefAssignment,
51
+ manager: {
52
+ valueType,
53
+ internal_valueManager: valueManager,
54
+ internal_fieldValueManager: fieldValueManager,
55
+ internal_useOpenPickerButtonAriaLabel: useOpenPickerButtonAriaLabel
56
+ }
57
+ } = parameters;
58
+ const {
59
+ internalProps,
60
+ forwardedProps
61
+ } = useSplitFieldProps(props, valueType);
62
+ const internalPropsWithDefaults = useFieldInternalPropsWithDefaults({
63
+ manager,
64
+ internalProps,
65
+ skipContextFieldRefAssignment
66
+ });
67
+ const {
68
+ onFocus,
69
+ onClick,
70
+ onPaste,
71
+ onBlur,
72
+ onKeyDown,
73
+ onClear,
74
+ clearable,
75
+ inputRef: inputRefProp,
76
+ placeholder: inPlaceholder
77
+ } = forwardedProps;
78
+ const {
79
+ readOnly = false,
80
+ disabled = false,
81
+ autoFocus = false,
82
+ focused,
83
+ unstableFieldRef
84
+ } = internalPropsWithDefaults;
85
+ const inputRef = React.useRef(null);
86
+ const handleRef = useForkRef(inputRefProp, inputRef);
87
+ const stateResponse = useFieldState({
88
+ manager,
89
+ internalPropsWithDefaults,
90
+ forwardedProps
91
+ });
92
+ const {
93
+ // States and derived states
55
94
  activeSectionIndex,
95
+ areAllSectionsEmpty,
96
+ error,
97
+ localizedDigits,
98
+ parsedSelectedSections,
99
+ sectionOrder,
56
100
  state,
57
- fieldValueManager,
58
- valueManager,
59
- applyCharacterEditing,
60
- resetCharacterQuery,
61
- updateSectionValue,
62
- updateValueFromValueStr,
63
- clearActiveSection,
101
+ value,
102
+ // Methods to update the states
64
103
  clearValue,
65
- setTempAndroidValueStr,
104
+ clearActiveSection,
105
+ setCharacterQuery,
66
106
  setSelectedSections,
67
- getSectionsFromValue,
68
- areAllSectionsEmpty,
69
- localizedDigits
70
- } = params;
71
- const inputRef = React.useRef(null);
72
- const handleRef = useForkRef(inputRefProp, inputRef);
107
+ setTempAndroidValueStr,
108
+ updateSectionValue,
109
+ updateValueFromValueStr,
110
+ // Utilities methods
111
+ getSectionsFromValue
112
+ } = stateResponse;
113
+ const applyCharacterEditing = useFieldCharacterEditing({
114
+ stateResponse
115
+ });
116
+ const openPickerAriaLabel = useOpenPickerButtonAriaLabel(value);
73
117
  const sections = React.useMemo(() => addPositionPropertiesToSections(state.sections, localizedDigits, isRtl), [state.sections, localizedDigits, isRtl]);
74
- const interactions = React.useMemo(() => ({
75
- syncSelectionToDOM: () => {
76
- if (!inputRef.current) {
77
- return;
78
- }
79
- if (parsedSelectedSections == null) {
80
- if (inputRef.current.scrollLeft) {
81
- // Ensure that input content is not marked as selected.
82
- // setting selection range to 0 causes issues in Safari.
83
- // https://bugs.webkit.org/show_bug.cgi?id=224425
84
- inputRef.current.scrollLeft = 0;
85
- }
86
- return;
87
- }
88
-
89
- // On multi input range pickers we want to update selection range only for the active input
90
- // This helps to avoid the focus jumping on Safari https://github.com/mui/mui-x/issues/9003
91
- // because WebKit implements the `setSelectionRange` based on the spec: https://bugs.webkit.org/show_bug.cgi?id=224425
92
- if (inputRef.current !== getActiveElement(document)) {
93
- return;
94
- }
95
-
96
- // Fix scroll jumping on iOS browser: https://github.com/mui/mui-x/issues/8321
97
- const currentScrollTop = inputRef.current.scrollTop;
98
- if (parsedSelectedSections === 'all') {
99
- inputRef.current.select();
100
- } else {
101
- const selectedSection = sections[parsedSelectedSections];
102
- const selectionStart = selectedSection.type === 'empty' ? selectedSection.startInInput - selectedSection.startSeparator.length : selectedSection.startInInput;
103
- const selectionEnd = selectedSection.type === 'empty' ? selectedSection.endInInput + selectedSection.endSeparator.length : selectedSection.endInInput;
104
- if (selectionStart !== inputRef.current.selectionStart || selectionEnd !== inputRef.current.selectionEnd) {
105
- if (inputRef.current === getActiveElement(document)) {
106
- inputRef.current.setSelectionRange(selectionStart, selectionEnd);
107
- }
108
- }
109
- clearTimeout(selectionSyncTimeoutRef.current);
110
- selectionSyncTimeoutRef.current = setTimeout(() => {
111
- // handle case when the selection is not updated correctly
112
- // could happen on Android
113
- if (inputRef.current && inputRef.current === getActiveElement(document) &&
114
- // The section might loose all selection, where `selectionStart === selectionEnd`
115
- // https://github.com/mui/mui-x/pull/13652
116
- inputRef.current.selectionStart === inputRef.current.selectionEnd && (inputRef.current.selectionStart !== selectionStart || inputRef.current.selectionEnd !== selectionEnd)) {
117
- interactions.syncSelectionToDOM();
118
- }
119
- });
120
- }
121
-
122
- // Even reading this variable seems to do the trick, but also setting it just to make use of it
123
- inputRef.current.scrollTop = currentScrollTop;
124
- },
125
- getActiveSectionIndexFromDOM: () => {
126
- const browserStartIndex = inputRef.current.selectionStart ?? 0;
127
- const browserEndIndex = inputRef.current.selectionEnd ?? 0;
128
- if (browserStartIndex === 0 && browserEndIndex === 0) {
129
- return null;
130
- }
131
- const nextSectionIndex = browserStartIndex <= sections[0].startInInput ? 1 // Special case if browser index is in invisible characters at the beginning.
132
- : sections.findIndex(section => section.startInInput - section.startSeparator.length > browserStartIndex);
133
- return nextSectionIndex === -1 ? sections.length - 1 : nextSectionIndex - 1;
134
- },
135
- focusField: (newSelectedSection = 0) => {
136
- if (getActiveElement(document) === inputRef.current) {
137
- return;
138
- }
139
- inputRef.current?.focus();
140
- setSelectedSections(newSelectedSection);
141
- },
142
- setSelectedSections: newSelectedSections => setSelectedSections(newSelectedSections),
143
- isFieldFocused: () => inputRef.current === getActiveElement(document)
144
- }), [inputRef, parsedSelectedSections, sections, setSelectedSections]);
145
- const syncSelectionFromDOM = () => {
118
+ function syncSelectionFromDOM() {
146
119
  const browserStartIndex = inputRef.current.selectionStart ?? 0;
147
120
  let nextSectionIndex;
148
121
  if (browserStartIndex <= sections[0].startInInput) {
@@ -156,13 +129,19 @@ export const useFieldV6TextField = params => {
156
129
  }
157
130
  const sectionIndex = nextSectionIndex === -1 ? sections.length - 1 : nextSectionIndex - 1;
158
131
  setSelectedSections(sectionIndex);
159
- };
132
+ }
133
+ function focusField(newSelectedSection = 0) {
134
+ if (getActiveElement(document) === inputRef.current) {
135
+ return;
136
+ }
137
+ inputRef.current?.focus();
138
+ setSelectedSections(newSelectedSection);
139
+ }
160
140
  const handleInputFocus = useEventCallback(event => {
161
141
  onFocus?.(event);
162
142
  // The ref is guaranteed to be resolved at this point.
163
143
  const input = inputRef.current;
164
- clearTimeout(focusTimeoutRef.current);
165
- focusTimeoutRef.current = setTimeout(() => {
144
+ focusTimeout.start(0, () => {
166
145
  // The ref changed, the component got remounted, the focus event is no longer relevant.
167
146
  if (!input || input !== inputRef.current) {
168
147
  return;
@@ -204,7 +183,7 @@ export const useFieldV6TextField = params => {
204
183
  const digitsAndLetterOnly = /^(([a-zA-Z]+)|)([0-9]+)(([a-zA-Z]+)|)$/.test(pastedValue);
205
184
  const isValidPastedValue = activeSection.contentType === 'letter' && lettersOnly || activeSection.contentType === 'digit' && digitsOnly || activeSection.contentType === 'digit-with-letter' && digitsAndLetterOnly;
206
185
  if (isValidPastedValue) {
207
- resetCharacterQuery();
186
+ setCharacterQuery(null);
208
187
  updateSectionValue({
209
188
  section: activeSection,
210
189
  newSectionValue: pastedValue,
@@ -218,7 +197,7 @@ export const useFieldV6TextField = params => {
218
197
  return;
219
198
  }
220
199
  }
221
- resetCharacterQuery();
200
+ setCharacterQuery(null);
222
201
  updateValueFromValueStr(pastedValue);
223
202
  });
224
203
  const handleContainerBlur = useEventCallback(event => {
@@ -231,7 +210,6 @@ export const useFieldV6TextField = params => {
231
210
  }
232
211
  const targetValue = event.target.value;
233
212
  if (targetValue === '') {
234
- resetCharacterQuery();
235
213
  clearValue();
236
214
  return;
237
215
  }
@@ -281,7 +259,6 @@ export const useFieldV6TextField = params => {
281
259
  if (isAndroid()) {
282
260
  setTempAndroidValueStr(valueStr);
283
261
  }
284
- resetCharacterQuery();
285
262
  clearActiveSection();
286
263
  return;
287
264
  }
@@ -290,6 +267,26 @@ export const useFieldV6TextField = params => {
290
267
  sectionIndex: activeSectionIndex
291
268
  });
292
269
  });
270
+ const handleClear = useEventCallback((event, ...args) => {
271
+ event.preventDefault();
272
+ onClear?.(event, ...args);
273
+ clearValue();
274
+ if (!isFieldFocused(inputRef)) {
275
+ // setSelectedSections is called internally
276
+ focusField(0);
277
+ } else {
278
+ setSelectedSections(sectionOrder.startIndex);
279
+ }
280
+ });
281
+ const handleContainerKeyDown = useFieldRootHandleKeyDown({
282
+ manager,
283
+ internalPropsWithDefaults,
284
+ stateResponse
285
+ });
286
+ const wrappedHandleContainerKeyDown = useEventCallback(event => {
287
+ onKeyDown?.(event);
288
+ handleContainerKeyDown(event);
289
+ });
293
290
  const placeholder = React.useMemo(() => {
294
291
  if (inPlaceholder !== undefined) {
295
292
  return inPlaceholder;
@@ -302,12 +299,60 @@ export const useFieldV6TextField = params => {
302
299
  if (inputRef.current && inputRef.current === getActiveElement(document)) {
303
300
  setSelectedSections('all');
304
301
  }
305
- return () => {
306
- clearTimeout(focusTimeoutRef.current);
307
- clearTimeout(selectionSyncTimeoutRef.current);
308
- };
309
302
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
310
303
 
304
+ useEnhancedEffect(() => {
305
+ function syncSelectionToDOM() {
306
+ if (!inputRef.current) {
307
+ return;
308
+ }
309
+ if (parsedSelectedSections == null) {
310
+ if (inputRef.current.scrollLeft) {
311
+ // Ensure that input content is not marked as selected.
312
+ // setting selection range to 0 causes issues in Safari.
313
+ // https://bugs.webkit.org/show_bug.cgi?id=224425
314
+ inputRef.current.scrollLeft = 0;
315
+ }
316
+ return;
317
+ }
318
+
319
+ // On multi input range pickers we want to update selection range only for the active input
320
+ // This helps to avoid the focus jumping on Safari https://github.com/mui/mui-x/issues/9003
321
+ // because WebKit implements the `setSelectionRange` based on the spec: https://bugs.webkit.org/show_bug.cgi?id=224425
322
+ if (inputRef.current !== getActiveElement(document)) {
323
+ return;
324
+ }
325
+
326
+ // Fix scroll jumping on iOS browser: https://github.com/mui/mui-x/issues/8321
327
+ const currentScrollTop = inputRef.current.scrollTop;
328
+ if (parsedSelectedSections === 'all') {
329
+ inputRef.current.select();
330
+ } else {
331
+ const selectedSection = sections[parsedSelectedSections];
332
+ const selectionStart = selectedSection.type === 'empty' ? selectedSection.startInInput - selectedSection.startSeparator.length : selectedSection.startInInput;
333
+ const selectionEnd = selectedSection.type === 'empty' ? selectedSection.endInInput + selectedSection.endSeparator.length : selectedSection.endInInput;
334
+ if (selectionStart !== inputRef.current.selectionStart || selectionEnd !== inputRef.current.selectionEnd) {
335
+ if (inputRef.current === getActiveElement(document)) {
336
+ inputRef.current.setSelectionRange(selectionStart, selectionEnd);
337
+ }
338
+ }
339
+ selectionSyncTimeout.start(0, () => {
340
+ // handle case when the selection is not updated correctly
341
+ // could happen on Android
342
+ if (inputRef.current && inputRef.current === getActiveElement(document) &&
343
+ // The section might loose all selection, where `selectionStart === selectionEnd`
344
+ // https://github.com/mui/mui-x/pull/13652
345
+ inputRef.current.selectionStart === inputRef.current.selectionEnd && (inputRef.current.selectionStart !== selectionStart || inputRef.current.selectionEnd !== selectionEnd)) {
346
+ syncSelectionToDOM();
347
+ }
348
+ });
349
+ }
350
+
351
+ // Even reading this variable seems to do the trick, but also setting it just to make use of it
352
+ inputRef.current.scrollTop = currentScrollTop;
353
+ }
354
+ syncSelectionToDOM();
355
+ });
311
356
  const inputMode = React.useMemo(() => {
312
357
  if (activeSectionIndex == null) {
313
358
  return 'text';
@@ -319,24 +364,46 @@ export const useFieldV6TextField = params => {
319
364
  }, [activeSectionIndex, state.sections]);
320
365
  const inputHasFocus = inputRef.current && inputRef.current === getActiveElement(document);
321
366
  const shouldShowPlaceholder = !inputHasFocus && areAllSectionsEmpty;
322
- return {
323
- interactions,
324
- returnedValue: {
325
- // Forwarded
326
- readOnly,
327
- onBlur: handleContainerBlur,
328
- onClick: handleInputClick,
329
- onFocus: handleInputFocus,
330
- onPaste: handleInputPaste,
331
- inputRef: handleRef,
332
- // Additional
333
- enableAccessibleFieldDOMStructure: false,
334
- placeholder,
335
- inputMode,
336
- autoComplete: 'off',
337
- value: shouldShowPlaceholder ? '' : valueStr,
338
- onChange: handleInputChange,
339
- focused
340
- }
341
- };
342
- };
367
+ React.useImperativeHandle(unstableFieldRef, () => ({
368
+ getSections: () => state.sections,
369
+ getActiveSectionIndex: () => {
370
+ const browserStartIndex = inputRef.current.selectionStart ?? 0;
371
+ const browserEndIndex = inputRef.current.selectionEnd ?? 0;
372
+ if (browserStartIndex === 0 && browserEndIndex === 0) {
373
+ return null;
374
+ }
375
+ const nextSectionIndex = browserStartIndex <= sections[0].startInInput ? 1 // Special case if browser index is in invisible characters at the beginning.
376
+ : sections.findIndex(section => section.startInInput - section.startSeparator.length > browserStartIndex);
377
+ return nextSectionIndex === -1 ? sections.length - 1 : nextSectionIndex - 1;
378
+ },
379
+ setSelectedSections: newSelectedSections => setSelectedSections(newSelectedSections),
380
+ focusField,
381
+ isFieldFocused: () => isFieldFocused(inputRef)
382
+ }));
383
+ return _extends({}, forwardedProps, {
384
+ error,
385
+ clearable: Boolean(clearable && !areAllSectionsEmpty && !readOnly && !disabled),
386
+ onBlur: handleContainerBlur,
387
+ onClick: handleInputClick,
388
+ onFocus: handleInputFocus,
389
+ onPaste: handleInputPaste,
390
+ onKeyDown: wrappedHandleContainerKeyDown,
391
+ onClear: handleClear,
392
+ inputRef: handleRef,
393
+ // Additional
394
+ enableAccessibleFieldDOMStructure: false,
395
+ placeholder,
396
+ inputMode,
397
+ autoComplete: 'off',
398
+ value: shouldShowPlaceholder ? '' : valueStr,
399
+ onChange: handleInputChange,
400
+ focused,
401
+ disabled,
402
+ readOnly,
403
+ autoFocus,
404
+ openPickerAriaLabel
405
+ });
406
+ };
407
+ function isFieldFocused(inputRef) {
408
+ return inputRef.current === getActiveElement(document);
409
+ }
@@ -1,2 +1,3 @@
1
- import { UseFieldTextField } from "./useField.types.js";
2
- export declare const useFieldV7TextField: UseFieldTextField<true>;
1
+ import { UseFieldParameters, UseFieldProps, UseFieldReturnValue } from "./useField.types.js";
2
+ import { PickerValidValue } from "../../models/index.js";
3
+ export declare const useFieldV7TextField: <TValue extends PickerValidValue, TError, TValidationProps extends {}, TProps extends UseFieldProps<true>>(parameters: UseFieldParameters<TValue, true, TError, TValidationProps, TProps>) => UseFieldReturnValue<true, TProps>;