@mui/x-date-pickers 8.29.0 → 8.29.3
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 +176 -1
- package/PickersTextField/PickersFilledInput/PickersFilledInput.js +1 -0
- package/PickersTextField/PickersFilledInput/PickersFilledInput.mjs +1 -0
- package/PickersTextField/PickersInput/PickersInput.js +1 -0
- package/PickersTextField/PickersInput/PickersInput.mjs +1 -0
- package/PickersTextField/PickersInputBase/PickersInputBase.js +20 -20
- package/PickersTextField/PickersInputBase/PickersInputBase.mjs +20 -20
- package/PickersTextField/PickersInputBase/PickersInputBase.types.d.mts +1 -0
- package/PickersTextField/PickersInputBase/PickersInputBase.types.d.ts +1 -0
- package/PickersTextField/PickersInputBase/pickersInputBaseClasses.js +1 -1
- package/PickersTextField/PickersInputBase/pickersInputBaseClasses.mjs +1 -1
- package/PickersTextField/PickersOutlinedInput/PickersOutlinedInput.js +12 -8
- package/PickersTextField/PickersOutlinedInput/PickersOutlinedInput.mjs +12 -8
- package/PickersTextField/PickersTextField.js +4 -1
- package/PickersTextField/PickersTextField.mjs +4 -1
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/internals/hooks/useField/useField.types.d.mts +1 -0
- package/internals/hooks/useField/useField.types.d.ts +1 -0
- package/internals/hooks/useField/useField.utils.d.mts +0 -1
- package/internals/hooks/useField/useField.utils.d.ts +0 -1
- package/internals/hooks/useField/useField.utils.js +2 -2
- package/internals/hooks/useField/useField.utils.mjs +1 -1
- package/internals/hooks/useField/useFieldRootProps.d.mts +1 -0
- package/internals/hooks/useField/useFieldRootProps.d.ts +1 -0
- package/internals/hooks/useField/useFieldRootProps.js +135 -8
- package/internals/hooks/useField/useFieldRootProps.mjs +135 -8
- package/internals/hooks/useField/useFieldSectionContainerProps.js +23 -4
- package/internals/hooks/useField/useFieldSectionContainerProps.mjs +22 -4
- package/internals/hooks/useField/useFieldSectionContentProps.d.mts +2 -2
- package/internals/hooks/useField/useFieldSectionContentProps.d.ts +2 -2
- package/internals/hooks/useField/useFieldSectionContentProps.js +14 -11
- package/internals/hooks/useField/useFieldSectionContentProps.mjs +14 -11
- package/internals/hooks/useField/useFieldV7TextField.js +13 -0
- package/internals/hooks/useField/useFieldV7TextField.mjs +13 -0
- package/package.json +182 -182
|
@@ -24,6 +24,7 @@ interface UseFieldRootPropsReturnValue {
|
|
|
24
24
|
onBlur: React.FocusEventHandler<HTMLDivElement>;
|
|
25
25
|
onFocus: React.FocusEventHandler<HTMLDivElement>;
|
|
26
26
|
onClick: React.MouseEventHandler<HTMLDivElement>;
|
|
27
|
+
onMouseDown: React.MouseEventHandler<HTMLDivElement>;
|
|
27
28
|
onPaste: React.ClipboardEventHandler<HTMLDivElement>;
|
|
28
29
|
onInput: React.FormEventHandler<HTMLDivElement>;
|
|
29
30
|
contentEditable: boolean;
|
|
@@ -50,14 +50,22 @@ function useFieldRootProps(parameters) {
|
|
|
50
50
|
stateResponse
|
|
51
51
|
});
|
|
52
52
|
const containerClickTimeout = (0, _useTimeout.default)();
|
|
53
|
-
const handleClick = (0, _useEventCallback.default)(
|
|
53
|
+
const handleClick = (0, _useEventCallback.default)(() => {
|
|
54
54
|
if (disabled || !domGetters.isReady()) {
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
|
-
setFocused(true);
|
|
58
57
|
if (parsedSelectedSections === 'all') {
|
|
58
|
+
setFocused(true);
|
|
59
59
|
containerClickTimeout.start(0, () => {
|
|
60
|
-
|
|
60
|
+
// `getSelection`/`getRangeAt` can be unset transiently (e.g. focus
|
|
61
|
+
// moved before this 0-tick callback ran). Fall back to the first
|
|
62
|
+
// section in that case.
|
|
63
|
+
const selection = document.getSelection();
|
|
64
|
+
if (!selection || selection.rangeCount === 0) {
|
|
65
|
+
setSelectedSections(sectionOrder.startIndex);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const cursorPosition = selection.getRangeAt(0).startOffset;
|
|
61
69
|
if (cursorPosition === 0) {
|
|
62
70
|
setSelectedSections(sectionOrder.startIndex);
|
|
63
71
|
return;
|
|
@@ -71,15 +79,83 @@ function useFieldRootProps(parameters) {
|
|
|
71
79
|
}
|
|
72
80
|
setSelectedSections(sectionIndex - 1);
|
|
73
81
|
});
|
|
74
|
-
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// For trusted pointer input, `handleMouseDown` already ran and set the
|
|
86
|
+
// section. This branch only matters for `click` events that arrive
|
|
87
|
+
// without a preceding `mousedown` (programmatic `element.click()`, some
|
|
88
|
+
// assistive-technology activations, synthetic event sequences in tests).
|
|
89
|
+
// Fall back to the first section so the field doesn't enter a "focused
|
|
90
|
+
// but no active section" state.
|
|
91
|
+
if (!focused) {
|
|
75
92
|
setFocused(true);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
93
|
+
if (parsedSelectedSections == null) {
|
|
94
|
+
setSelectedSections(sectionOrder.startIndex);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Replaces Chromium's focus delegation with an explicit section focus on
|
|
100
|
+
// every primary mousedown inside the sections container. The CSS gate
|
|
101
|
+
// (`WebkitUserModify: read-only` while not `:focus-within`) can make the
|
|
102
|
+
// section's contenteditable span temporarily non-focusable, in which case
|
|
103
|
+
// Chromium falls back to the sections-container `tabindex=0` and our
|
|
104
|
+
// `handleFocus` then runs the "no active section" fallback, briefly
|
|
105
|
+
// selecting the first section before the click bubble corrects it.
|
|
106
|
+
// Setting the target section here (and letting `syncSelectionToDOM` move
|
|
107
|
+
// focus via `.focus()`, which works even with the user-modify rule
|
|
108
|
+
// active) skips that race entirely.
|
|
109
|
+
const handleMouseDown = (0, _useEventCallback.default)(event => {
|
|
110
|
+
if (disabled || !domGetters.isReady() || parsedSelectedSections === 'all') {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (event.button !== 0) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const target = event.target;
|
|
117
|
+
const sectionListRoot = domGetters.getRoot();
|
|
118
|
+
const isInsideSectionList = sectionListRoot.contains(target);
|
|
119
|
+
// Only the field root is ours outside the sections container: it is the
|
|
120
|
+
// target of a padding click. The adornments keep their own behavior.
|
|
121
|
+
if (!isInsideSectionList && target !== event.currentTarget) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const sectionElement = isInsideSectionList ? target.closest('[data-sectionindex]') : null;
|
|
125
|
+
|
|
126
|
+
// Blank space is the field padding and the area past the sections. Mimic
|
|
127
|
+
// the native date input: first section on entry, keep it afterwards.
|
|
128
|
+
// `preventDefault` is what keeps it, by stopping both the browser blur and
|
|
129
|
+
// Chromium's focus delegation.
|
|
130
|
+
const isBlankSpaceClick = sectionElement == null && (!isInsideSectionList || isPointOutsideSections(sectionListRoot, event.clientX));
|
|
131
|
+
if (isBlankSpaceClick) {
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
if (!focused) {
|
|
134
|
+
setFocused(true);
|
|
80
135
|
setSelectedSections(sectionOrder.startIndex);
|
|
81
136
|
}
|
|
137
|
+
return;
|
|
82
138
|
}
|
|
139
|
+
|
|
140
|
+
// Prefer the visually-containing section (matches Chromium's
|
|
141
|
+
// delegation + section container `onClick`), fall back to the
|
|
142
|
+
// closest-by-distance section for clicks on the container padding.
|
|
143
|
+
const parsedIndex = sectionElement ? Number(sectionElement.dataset.sectionindex) : findClosestSectionIndexToPoint(sectionListRoot, event.clientX);
|
|
144
|
+
// `Number(undefined) === NaN` and `NaN == null === false`, so guard
|
|
145
|
+
// explicitly here even though `data-sectionindex` is set by
|
|
146
|
+
// `useFieldSectionContainerProps` for every section in practice.
|
|
147
|
+
if (parsedIndex == null || !Number.isInteger(parsedIndex)) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
setFocused(true);
|
|
152
|
+
// `mousedown` is now authoritative for pointer section selection. The
|
|
153
|
+
// section container's own `onClick` deduplicates against the resulting
|
|
154
|
+
// `parsedSelectedSections` on the click bubble (see
|
|
155
|
+
// `useFieldSectionContainerProps`), so we always select here and let that
|
|
156
|
+
// guard absorb the redundant call -- matching the pre-PR
|
|
157
|
+
// `onSelectedSectionsChange` invocation count.
|
|
158
|
+
setSelectedSections(parsedIndex);
|
|
83
159
|
});
|
|
84
160
|
const handleInput = (0, _useEventCallback.default)(event => {
|
|
85
161
|
if (!domGetters.isReady() || parsedSelectedSections !== 'all') {
|
|
@@ -148,10 +224,61 @@ function useFieldRootProps(parameters) {
|
|
|
148
224
|
onBlur: handleBlur,
|
|
149
225
|
onFocus: handleFocus,
|
|
150
226
|
onClick: handleClick,
|
|
227
|
+
onMouseDown: handleMouseDown,
|
|
151
228
|
onPaste: handlePaste,
|
|
152
229
|
onInput: handleInput,
|
|
153
230
|
// Other
|
|
154
231
|
contentEditable: parsedSelectedSections === 'all',
|
|
155
232
|
tabIndex: internalPropsWithDefaults.disabled || parsedSelectedSections === 0 ? -1 : 0 // TODO: Try to set to undefined when there is a section selected.
|
|
156
233
|
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Slop on each side of the sections, so a click that just misses the outermost
|
|
238
|
+
* one still selects it. Under half a digit at the default font size.
|
|
239
|
+
*/
|
|
240
|
+
const BLANK_SPACE_TOLERANCE = 4;
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Returns `true` when `clientX` sits past the sections on either side.
|
|
244
|
+
* Horizontal only: a click in the container's vertical padding still belongs to
|
|
245
|
+
* the section above or below it.
|
|
246
|
+
*/
|
|
247
|
+
function isPointOutsideSections(root, clientX) {
|
|
248
|
+
const sections = root.querySelectorAll('[data-sectionindex]');
|
|
249
|
+
if (sections.length === 0) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
let left = Infinity;
|
|
253
|
+
let right = -Infinity;
|
|
254
|
+
for (let i = 0; i < sections.length; i += 1) {
|
|
255
|
+
const rect = sections[i].getBoundingClientRect();
|
|
256
|
+
left = Math.min(left, rect.left);
|
|
257
|
+
right = Math.max(right, rect.right);
|
|
258
|
+
}
|
|
259
|
+
return clientX < left - BLANK_SPACE_TOLERANCE || clientX > right + BLANK_SPACE_TOLERANCE;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Returns the index of the section whose horizontal center is closest to `clientX`.
|
|
264
|
+
* Returns `null` if the field renders no `[role="spinbutton"]` descendants
|
|
265
|
+
* (defensive -- every section content span sets `role="spinbutton"` in practice).
|
|
266
|
+
*/
|
|
267
|
+
function findClosestSectionIndexToPoint(root, clientX) {
|
|
268
|
+
const sections = root.querySelectorAll('[role="spinbutton"]');
|
|
269
|
+
if (sections.length === 0) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
let closestIndex = 0;
|
|
273
|
+
let closestDistance = Infinity;
|
|
274
|
+
for (let i = 0; i < sections.length; i += 1) {
|
|
275
|
+
const rect = sections[i].getBoundingClientRect();
|
|
276
|
+
const center = (rect.left + rect.right) / 2;
|
|
277
|
+
const distance = Math.abs(clientX - center);
|
|
278
|
+
if (distance < closestDistance) {
|
|
279
|
+
closestDistance = distance;
|
|
280
|
+
closestIndex = i;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return closestIndex;
|
|
157
284
|
}
|
|
@@ -44,14 +44,22 @@ export function useFieldRootProps(parameters) {
|
|
|
44
44
|
stateResponse
|
|
45
45
|
});
|
|
46
46
|
const containerClickTimeout = useTimeout();
|
|
47
|
-
const handleClick = useEventCallback(
|
|
47
|
+
const handleClick = useEventCallback(() => {
|
|
48
48
|
if (disabled || !domGetters.isReady()) {
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
-
setFocused(true);
|
|
52
51
|
if (parsedSelectedSections === 'all') {
|
|
52
|
+
setFocused(true);
|
|
53
53
|
containerClickTimeout.start(0, () => {
|
|
54
|
-
|
|
54
|
+
// `getSelection`/`getRangeAt` can be unset transiently (e.g. focus
|
|
55
|
+
// moved before this 0-tick callback ran). Fall back to the first
|
|
56
|
+
// section in that case.
|
|
57
|
+
const selection = document.getSelection();
|
|
58
|
+
if (!selection || selection.rangeCount === 0) {
|
|
59
|
+
setSelectedSections(sectionOrder.startIndex);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const cursorPosition = selection.getRangeAt(0).startOffset;
|
|
55
63
|
if (cursorPosition === 0) {
|
|
56
64
|
setSelectedSections(sectionOrder.startIndex);
|
|
57
65
|
return;
|
|
@@ -65,15 +73,83 @@ export function useFieldRootProps(parameters) {
|
|
|
65
73
|
}
|
|
66
74
|
setSelectedSections(sectionIndex - 1);
|
|
67
75
|
});
|
|
68
|
-
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// For trusted pointer input, `handleMouseDown` already ran and set the
|
|
80
|
+
// section. This branch only matters for `click` events that arrive
|
|
81
|
+
// without a preceding `mousedown` (programmatic `element.click()`, some
|
|
82
|
+
// assistive-technology activations, synthetic event sequences in tests).
|
|
83
|
+
// Fall back to the first section so the field doesn't enter a "focused
|
|
84
|
+
// but no active section" state.
|
|
85
|
+
if (!focused) {
|
|
69
86
|
setFocused(true);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
if (parsedSelectedSections == null) {
|
|
88
|
+
setSelectedSections(sectionOrder.startIndex);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Replaces Chromium's focus delegation with an explicit section focus on
|
|
94
|
+
// every primary mousedown inside the sections container. The CSS gate
|
|
95
|
+
// (`WebkitUserModify: read-only` while not `:focus-within`) can make the
|
|
96
|
+
// section's contenteditable span temporarily non-focusable, in which case
|
|
97
|
+
// Chromium falls back to the sections-container `tabindex=0` and our
|
|
98
|
+
// `handleFocus` then runs the "no active section" fallback, briefly
|
|
99
|
+
// selecting the first section before the click bubble corrects it.
|
|
100
|
+
// Setting the target section here (and letting `syncSelectionToDOM` move
|
|
101
|
+
// focus via `.focus()`, which works even with the user-modify rule
|
|
102
|
+
// active) skips that race entirely.
|
|
103
|
+
const handleMouseDown = useEventCallback(event => {
|
|
104
|
+
if (disabled || !domGetters.isReady() || parsedSelectedSections === 'all') {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (event.button !== 0) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const target = event.target;
|
|
111
|
+
const sectionListRoot = domGetters.getRoot();
|
|
112
|
+
const isInsideSectionList = sectionListRoot.contains(target);
|
|
113
|
+
// Only the field root is ours outside the sections container: it is the
|
|
114
|
+
// target of a padding click. The adornments keep their own behavior.
|
|
115
|
+
if (!isInsideSectionList && target !== event.currentTarget) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const sectionElement = isInsideSectionList ? target.closest('[data-sectionindex]') : null;
|
|
119
|
+
|
|
120
|
+
// Blank space is the field padding and the area past the sections. Mimic
|
|
121
|
+
// the native date input: first section on entry, keep it afterwards.
|
|
122
|
+
// `preventDefault` is what keeps it, by stopping both the browser blur and
|
|
123
|
+
// Chromium's focus delegation.
|
|
124
|
+
const isBlankSpaceClick = sectionElement == null && (!isInsideSectionList || isPointOutsideSections(sectionListRoot, event.clientX));
|
|
125
|
+
if (isBlankSpaceClick) {
|
|
126
|
+
event.preventDefault();
|
|
127
|
+
if (!focused) {
|
|
128
|
+
setFocused(true);
|
|
74
129
|
setSelectedSections(sectionOrder.startIndex);
|
|
75
130
|
}
|
|
131
|
+
return;
|
|
76
132
|
}
|
|
133
|
+
|
|
134
|
+
// Prefer the visually-containing section (matches Chromium's
|
|
135
|
+
// delegation + section container `onClick`), fall back to the
|
|
136
|
+
// closest-by-distance section for clicks on the container padding.
|
|
137
|
+
const parsedIndex = sectionElement ? Number(sectionElement.dataset.sectionindex) : findClosestSectionIndexToPoint(sectionListRoot, event.clientX);
|
|
138
|
+
// `Number(undefined) === NaN` and `NaN == null === false`, so guard
|
|
139
|
+
// explicitly here even though `data-sectionindex` is set by
|
|
140
|
+
// `useFieldSectionContainerProps` for every section in practice.
|
|
141
|
+
if (parsedIndex == null || !Number.isInteger(parsedIndex)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
event.preventDefault();
|
|
145
|
+
setFocused(true);
|
|
146
|
+
// `mousedown` is now authoritative for pointer section selection. The
|
|
147
|
+
// section container's own `onClick` deduplicates against the resulting
|
|
148
|
+
// `parsedSelectedSections` on the click bubble (see
|
|
149
|
+
// `useFieldSectionContainerProps`), so we always select here and let that
|
|
150
|
+
// guard absorb the redundant call -- matching the pre-PR
|
|
151
|
+
// `onSelectedSectionsChange` invocation count.
|
|
152
|
+
setSelectedSections(parsedIndex);
|
|
77
153
|
});
|
|
78
154
|
const handleInput = useEventCallback(event => {
|
|
79
155
|
if (!domGetters.isReady() || parsedSelectedSections !== 'all') {
|
|
@@ -142,10 +218,61 @@ export function useFieldRootProps(parameters) {
|
|
|
142
218
|
onBlur: handleBlur,
|
|
143
219
|
onFocus: handleFocus,
|
|
144
220
|
onClick: handleClick,
|
|
221
|
+
onMouseDown: handleMouseDown,
|
|
145
222
|
onPaste: handlePaste,
|
|
146
223
|
onInput: handleInput,
|
|
147
224
|
// Other
|
|
148
225
|
contentEditable: parsedSelectedSections === 'all',
|
|
149
226
|
tabIndex: internalPropsWithDefaults.disabled || parsedSelectedSections === 0 ? -1 : 0 // TODO: Try to set to undefined when there is a section selected.
|
|
150
227
|
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Slop on each side of the sections, so a click that just misses the outermost
|
|
232
|
+
* one still selects it. Under half a digit at the default font size.
|
|
233
|
+
*/
|
|
234
|
+
const BLANK_SPACE_TOLERANCE = 4;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Returns `true` when `clientX` sits past the sections on either side.
|
|
238
|
+
* Horizontal only: a click in the container's vertical padding still belongs to
|
|
239
|
+
* the section above or below it.
|
|
240
|
+
*/
|
|
241
|
+
function isPointOutsideSections(root, clientX) {
|
|
242
|
+
const sections = root.querySelectorAll('[data-sectionindex]');
|
|
243
|
+
if (sections.length === 0) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
let left = Infinity;
|
|
247
|
+
let right = -Infinity;
|
|
248
|
+
for (let i = 0; i < sections.length; i += 1) {
|
|
249
|
+
const rect = sections[i].getBoundingClientRect();
|
|
250
|
+
left = Math.min(left, rect.left);
|
|
251
|
+
right = Math.max(right, rect.right);
|
|
252
|
+
}
|
|
253
|
+
return clientX < left - BLANK_SPACE_TOLERANCE || clientX > right + BLANK_SPACE_TOLERANCE;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Returns the index of the section whose horizontal center is closest to `clientX`.
|
|
258
|
+
* Returns `null` if the field renders no `[role="spinbutton"]` descendants
|
|
259
|
+
* (defensive -- every section content span sets `role="spinbutton"` in practice).
|
|
260
|
+
*/
|
|
261
|
+
function findClosestSectionIndexToPoint(root, clientX) {
|
|
262
|
+
const sections = root.querySelectorAll('[role="spinbutton"]');
|
|
263
|
+
if (sections.length === 0) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
let closestIndex = 0;
|
|
267
|
+
let closestDistance = Infinity;
|
|
268
|
+
for (let i = 0; i < sections.length; i += 1) {
|
|
269
|
+
const rect = sections[i].getBoundingClientRect();
|
|
270
|
+
const center = (rect.left + rect.right) / 2;
|
|
271
|
+
const distance = Math.abs(clientX - center);
|
|
272
|
+
if (distance < closestDistance) {
|
|
273
|
+
closestDistance = distance;
|
|
274
|
+
closestIndex = i;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return closestIndex;
|
|
151
278
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
3
4
|
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
|
4
5
|
Object.defineProperty(exports, "__esModule", {
|
|
5
6
|
value: true
|
|
6
7
|
});
|
|
7
8
|
exports.useFieldSectionContainerProps = useFieldSectionContainerProps;
|
|
8
9
|
var React = _interopRequireWildcard(require("react"));
|
|
10
|
+
var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
|
|
9
11
|
/**
|
|
10
12
|
* Generate the props to pass to the container element of each section of the field.
|
|
11
13
|
* It is not used by the non-accessible DOM structure (with an <input /> element for editing).
|
|
@@ -16,6 +18,8 @@ var React = _interopRequireWildcard(require("react"));
|
|
|
16
18
|
function useFieldSectionContainerProps(parameters) {
|
|
17
19
|
const {
|
|
18
20
|
stateResponse: {
|
|
21
|
+
// States and derived states
|
|
22
|
+
parsedSelectedSections,
|
|
19
23
|
// Methods to update the states
|
|
20
24
|
setSelectedSections
|
|
21
25
|
},
|
|
@@ -23,16 +27,31 @@ function useFieldSectionContainerProps(parameters) {
|
|
|
23
27
|
disabled = false
|
|
24
28
|
}
|
|
25
29
|
} = parameters;
|
|
26
|
-
|
|
30
|
+
|
|
31
|
+
// A single stable handler shared by every section, rather than a per-index
|
|
32
|
+
// factory. The section index is read from the container's `data-sectionindex`
|
|
33
|
+
// at click time.
|
|
34
|
+
const handleClick = (0, _useEventCallback.default)(event => {
|
|
27
35
|
// The click event on the clear button would propagate to the input, trigger this handler and result in a wrong section selection.
|
|
28
36
|
// We avoid this by checking if the call to this function is actually intended, or a side effect.
|
|
29
37
|
if (disabled || event.isDefaultPrevented()) {
|
|
30
38
|
return;
|
|
31
39
|
}
|
|
40
|
+
const sectionIndex = Number(event.currentTarget.dataset.sectionindex);
|
|
41
|
+
if (!Number.isInteger(sectionIndex)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// For pointer input the `mousedown` handler already selected the section
|
|
46
|
+
// before this click bubbles, so bail to avoid a redundant
|
|
47
|
+
// `onSelectedSectionsChange` invocation.
|
|
48
|
+
if (parsedSelectedSections === sectionIndex) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
32
51
|
setSelectedSections(sectionIndex);
|
|
33
|
-
}
|
|
52
|
+
});
|
|
34
53
|
return React.useCallback(sectionIndex => ({
|
|
35
54
|
'data-sectionindex': sectionIndex,
|
|
36
|
-
onClick:
|
|
37
|
-
}), [
|
|
55
|
+
onClick: handleClick
|
|
56
|
+
}), [handleClick]);
|
|
38
57
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import useEventCallback from '@mui/utils/useEventCallback';
|
|
2
3
|
/**
|
|
3
4
|
* Generate the props to pass to the container element of each section of the field.
|
|
4
5
|
* It is not used by the non-accessible DOM structure (with an <input /> element for editing).
|
|
@@ -9,6 +10,8 @@ import * as React from 'react';
|
|
|
9
10
|
export function useFieldSectionContainerProps(parameters) {
|
|
10
11
|
const {
|
|
11
12
|
stateResponse: {
|
|
13
|
+
// States and derived states
|
|
14
|
+
parsedSelectedSections,
|
|
12
15
|
// Methods to update the states
|
|
13
16
|
setSelectedSections
|
|
14
17
|
},
|
|
@@ -16,16 +19,31 @@ export function useFieldSectionContainerProps(parameters) {
|
|
|
16
19
|
disabled = false
|
|
17
20
|
}
|
|
18
21
|
} = parameters;
|
|
19
|
-
|
|
22
|
+
|
|
23
|
+
// A single stable handler shared by every section, rather than a per-index
|
|
24
|
+
// factory. The section index is read from the container's `data-sectionindex`
|
|
25
|
+
// at click time.
|
|
26
|
+
const handleClick = useEventCallback(event => {
|
|
20
27
|
// The click event on the clear button would propagate to the input, trigger this handler and result in a wrong section selection.
|
|
21
28
|
// We avoid this by checking if the call to this function is actually intended, or a side effect.
|
|
22
29
|
if (disabled || event.isDefaultPrevented()) {
|
|
23
30
|
return;
|
|
24
31
|
}
|
|
32
|
+
const sectionIndex = Number(event.currentTarget.dataset.sectionindex);
|
|
33
|
+
if (!Number.isInteger(sectionIndex)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// For pointer input the `mousedown` handler already selected the section
|
|
38
|
+
// before this click bubbles, so bail to avoid a redundant
|
|
39
|
+
// `onSelectedSectionsChange` invocation.
|
|
40
|
+
if (parsedSelectedSections === sectionIndex) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
25
43
|
setSelectedSections(sectionIndex);
|
|
26
|
-
}
|
|
44
|
+
});
|
|
27
45
|
return React.useCallback(sectionIndex => ({
|
|
28
46
|
'data-sectionindex': sectionIndex,
|
|
29
|
-
onClick:
|
|
30
|
-
}), [
|
|
47
|
+
onClick: handleClick
|
|
48
|
+
}), [handleClick]);
|
|
31
49
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { UseFieldStateReturnValue } from "./useFieldState.mjs";
|
|
2
2
|
import { FieldSection, PickerManager } from "../../../models/index.mjs";
|
|
3
3
|
import { UseFieldDOMGetters, UseFieldInternalProps } from "./useField.types.mjs";
|
|
4
|
-
import { UseFieldCharacterEditingReturnValue } from "./useFieldCharacterEditing.mjs";
|
|
5
|
-
import { PickersSectionElement } from "../../../PickersSectionList/index.mjs";
|
|
4
|
+
import type { UseFieldCharacterEditingReturnValue } from "./useFieldCharacterEditing.mjs";
|
|
5
|
+
import type { PickersSectionElement } from "../../../PickersSectionList/index.mjs";
|
|
6
6
|
/**
|
|
7
7
|
* Generate the props to pass to the content element of each section of the field.
|
|
8
8
|
* It is not used by the non-accessible DOM structure (with an <input /> element for editing).
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { UseFieldStateReturnValue } from "./useFieldState.js";
|
|
2
2
|
import { FieldSection, PickerManager } from "../../../models/index.js";
|
|
3
3
|
import { UseFieldDOMGetters, UseFieldInternalProps } from "./useField.types.js";
|
|
4
|
-
import { UseFieldCharacterEditingReturnValue } from "./useFieldCharacterEditing.js";
|
|
5
|
-
import { PickersSectionElement } from "../../../PickersSectionList/index.js";
|
|
4
|
+
import type { UseFieldCharacterEditingReturnValue } from "./useFieldCharacterEditing.js";
|
|
5
|
+
import type { PickersSectionElement } from "../../../PickersSectionList/index.js";
|
|
6
6
|
/**
|
|
7
7
|
* Generate the props to pass to the content element of each section of the field.
|
|
8
8
|
* It is not used by the non-accessible DOM structure (with an <input /> element for editing).
|
|
@@ -10,6 +10,7 @@ var React = _interopRequireWildcard(require("react"));
|
|
|
10
10
|
var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
|
|
11
11
|
var _hooks = require("../../../hooks");
|
|
12
12
|
var _syncSelectionToDOM = require("./syncSelectionToDOM");
|
|
13
|
+
var _useField = require("./useField.utils");
|
|
13
14
|
/**
|
|
14
15
|
* Generate the props to pass to the content element of each section of the field.
|
|
15
16
|
* It is not used by the non-accessible DOM structure (with an <input /> element for editing).
|
|
@@ -34,6 +35,7 @@ function useFieldSectionContentProps(parameters) {
|
|
|
34
35
|
sectionsValueBoundaries,
|
|
35
36
|
state,
|
|
36
37
|
value,
|
|
38
|
+
localizedDigits,
|
|
37
39
|
// Methods to update the states
|
|
38
40
|
clearActiveSection,
|
|
39
41
|
setCharacterQuery,
|
|
@@ -154,10 +156,10 @@ function useFieldSectionContentProps(parameters) {
|
|
|
154
156
|
onFocus: createFocusHandler(sectionIndex),
|
|
155
157
|
// Aria attributes
|
|
156
158
|
'aria-readonly': readOnly,
|
|
157
|
-
'aria-valuenow': getSectionValueNow(section, adapter),
|
|
159
|
+
'aria-valuenow': getSectionValueNow(section, adapter, localizedDigits),
|
|
158
160
|
'aria-valuemin': sectionBoundaries.minimum,
|
|
159
161
|
'aria-valuemax': sectionBoundaries.maximum,
|
|
160
|
-
'aria-valuetext': section.value ? getSectionValueText(section, adapter) : translations.empty,
|
|
162
|
+
'aria-valuetext': section.value ? getSectionValueText(section, adapter, localizedDigits) : translations.empty,
|
|
161
163
|
'aria-label': translations[section.type],
|
|
162
164
|
'aria-disabled': disabled,
|
|
163
165
|
// Other
|
|
@@ -173,9 +175,9 @@ function useFieldSectionContentProps(parameters) {
|
|
|
173
175
|
children: section.value || section.placeholder,
|
|
174
176
|
inputMode: section.contentType === 'letter' ? 'text' : 'numeric'
|
|
175
177
|
};
|
|
176
|
-
}, [sectionsValueBoundaries, isContainerEditable, disabled, readOnly, isEditable, translations, adapter, handleInput, handlePaste, handleMouseUp, handleDragOver, createFocusHandler, fieldValueManager, value]);
|
|
178
|
+
}, [sectionsValueBoundaries, isContainerEditable, disabled, readOnly, isEditable, translations, adapter, localizedDigits, handleInput, handlePaste, handleMouseUp, handleDragOver, createFocusHandler, fieldValueManager, value]);
|
|
177
179
|
}
|
|
178
|
-
function getSectionValueText(section, adapter) {
|
|
180
|
+
function getSectionValueText(section, adapter, localizedDigits) {
|
|
179
181
|
if (!section.value) {
|
|
180
182
|
return undefined;
|
|
181
183
|
}
|
|
@@ -183,7 +185,7 @@ function getSectionValueText(section, adapter) {
|
|
|
183
185
|
case 'month':
|
|
184
186
|
{
|
|
185
187
|
if (section.contentType === 'digit') {
|
|
186
|
-
const dateWithMonth = adapter.setMonth(adapter.date(), Number(section.value) - 1);
|
|
188
|
+
const dateWithMonth = adapter.setMonth(adapter.date(), Number((0, _useField.removeLocalizedDigits)(section.value, localizedDigits)) - 1);
|
|
187
189
|
return adapter.isValid(dateWithMonth) ? adapter.format(dateWithMonth, 'month') : '';
|
|
188
190
|
}
|
|
189
191
|
const parsedDate = adapter.parse(section.value, section.format);
|
|
@@ -191,7 +193,7 @@ function getSectionValueText(section, adapter) {
|
|
|
191
193
|
}
|
|
192
194
|
case 'day':
|
|
193
195
|
if (section.contentType === 'digit') {
|
|
194
|
-
const dateWithDay = adapter.setDate(adapter.startOfYear(adapter.date()), Number(section.value));
|
|
196
|
+
const dateWithDay = adapter.setDate(adapter.startOfYear(adapter.date()), Number((0, _useField.removeLocalizedDigits)(section.value, localizedDigits)));
|
|
195
197
|
return adapter.isValid(dateWithDay) ? adapter.format(dateWithDay, 'dayOfMonthFull') : '';
|
|
196
198
|
}
|
|
197
199
|
return section.value;
|
|
@@ -202,10 +204,11 @@ function getSectionValueText(section, adapter) {
|
|
|
202
204
|
return undefined;
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
|
-
function getSectionValueNow(section, adapter) {
|
|
207
|
+
function getSectionValueNow(section, adapter, localizedDigits) {
|
|
206
208
|
if (!section.value) {
|
|
207
209
|
return undefined;
|
|
208
210
|
}
|
|
211
|
+
const nonLocalizedValue = (0, _useField.removeLocalizedDigits)(section.value, localizedDigits);
|
|
209
212
|
switch (section.type) {
|
|
210
213
|
case 'weekDay':
|
|
211
214
|
{
|
|
@@ -213,7 +216,7 @@ function getSectionValueNow(section, adapter) {
|
|
|
213
216
|
// TODO: improve by resolving the week day number from a letter week day
|
|
214
217
|
return undefined;
|
|
215
218
|
}
|
|
216
|
-
return Number(
|
|
219
|
+
return Number(nonLocalizedValue);
|
|
217
220
|
}
|
|
218
221
|
case 'meridiem':
|
|
219
222
|
{
|
|
@@ -224,16 +227,16 @@ function getSectionValueNow(section, adapter) {
|
|
|
224
227
|
return undefined;
|
|
225
228
|
}
|
|
226
229
|
case 'day':
|
|
227
|
-
return section.contentType === 'digit-with-letter' ? parseInt(
|
|
230
|
+
return section.contentType === 'digit-with-letter' ? parseInt(nonLocalizedValue, 10) : Number(nonLocalizedValue);
|
|
228
231
|
case 'month':
|
|
229
232
|
{
|
|
230
233
|
if (section.contentType === 'digit') {
|
|
231
|
-
return Number(
|
|
234
|
+
return Number(nonLocalizedValue);
|
|
232
235
|
}
|
|
233
236
|
const parsedDate = adapter.parse(section.value, section.format);
|
|
234
237
|
return parsedDate ? adapter.getMonth(parsedDate) + 1 : undefined;
|
|
235
238
|
}
|
|
236
239
|
default:
|
|
237
|
-
return section.contentType !== 'letter' ? Number(
|
|
240
|
+
return section.contentType !== 'letter' ? Number(nonLocalizedValue) : undefined;
|
|
238
241
|
}
|
|
239
242
|
}
|