Skip to content

Commit 91173c3

Browse files
committed
wrap time segments in lri, wrap fields in unicode isolate
1 parent 2b5fb13 commit 91173c3

File tree

3 files changed

+67
-47
lines changed

3 files changed

+67
-47
lines changed

packages/@react-aria/datepicker/src/useDateField.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ export function useDateField<T extends DateValue>(props: AriaDateFieldOptions<T>
181181
if (props.onKeyUp) {
182182
props.onKeyUp(e);
183183
}
184+
},
185+
style: {
186+
unicodeBidi: 'isolate'
184187
}
185188
}),
186189
inputProps,

packages/@react-stately/datepicker/src/useDateFieldState.ts

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -268,53 +268,9 @@ export function useDateFieldState<T extends DateValue = DateValue>(props: DateFi
268268
};
269269

270270
let dateValue = useMemo(() => displayValue.toDate(timeZone), [displayValue, timeZone]);
271-
let timeValue = ['hour', 'minute', 'second'];
272-
let dateSegments = ['day', 'month', 'year'];
273-
// TODO: I don't like this but not sure what to do...
274-
let rtlLocale = ['ar-DZ', 'ar-AE', 'ar-EG', 'ar-SA'];
275-
let segments = useMemo(() =>
276-
dateFormatter.formatToParts(dateValue)
277-
.map(segment => {
278-
let isEditable = EDITABLE_SEGMENTS[segment.type];
279-
if (segment.type === 'era' && calendar.getEras().length === 1) {
280-
isEditable = false;
281-
}
282-
283-
let isPlaceholder = EDITABLE_SEGMENTS[segment.type] && !validSegments[segment.type];
284-
let placeholder = EDITABLE_SEGMENTS[segment.type] ? getPlaceholder(segment.type, segment.value, locale) : null;
285-
286-
let value = segment.value;
287-
let place = placeholder;
288-
if (dateSegments.length === 3 && dateSegments.includes(segment.type) && !rtlLocale.includes(locale)) {
289-
value = `\u2066${value}`;
290-
place = `\u2066${place}`;
291-
} else if (segment.type === 'hour') {
292-
value = `\u2066${value}`;
293-
place = `\u2066${place}`;
294-
// Ideally the unicode (\u2069) would be placed at the end but that seems to cause some issues
295-
// with the background when the rightmost character is focused in Hebrew.
296-
} else if (dateSegments.length === 1 && dateSegments.includes(segment.type) && !rtlLocale.includes(locale)) {
297-
value = `\u2069${value}`;
298-
place = `\u2069${place}`;
299-
} else if (timeValue.includes(granularity) && segment.type === granularity) {
300-
value = `\u2069${value}`;
301-
place = `\u2069${place}`;
302-
}
303-
304-
if (dateSegments.includes(segment.type)) {
305-
dateSegments = dateSegments.filter(item => item !== segment.type);
306-
};
307-
308-
return {
309-
type: TYPE_MAPPING[segment.type] || segment.type,
310-
text: isPlaceholder ? place : value,
311-
...getSegmentLimits(displayValue, segment.type, resolvedOptions),
312-
isPlaceholder,
313-
placeholder: place,
314-
isEditable
315-
} as DateSegment;
316-
})
317-
, [dateValue, validSegments, dateFormatter, resolvedOptions, displayValue, calendar, locale, granularity, timeValue]);
271+
let segments = useMemo(() =>
272+
processSegments(dateValue, validSegments, dateFormatter, resolvedOptions, displayValue, calendar, locale, granularity),
273+
[dateValue, validSegments, dateFormatter, resolvedOptions, displayValue, calendar, locale, granularity]);
318274

319275
// When the era field appears, mark it valid if the year field is already valid.
320276
// If the era field disappears, remove it from the valid segments.
@@ -450,6 +406,66 @@ export function useDateFieldState<T extends DateValue = DateValue>(props: DateFi
450406
};
451407
}
452408

409+
function processSegments(dateValue, validSegments, dateFormatter, resolvedOptions, displayValue, calendar, locale, granularity) : DateSegment[] {
410+
let timeValue = ['hour', 'minute', 'second'];
411+
let segments = dateFormatter.formatToParts(dateValue);
412+
let processedSegments: DateSegment[] = [];
413+
for (let segment of segments) {
414+
let isEditable = EDITABLE_SEGMENTS[segment.type];
415+
if (segment.type === 'era' && calendar.getEras().length === 1) {
416+
isEditable = false;
417+
}
418+
419+
let isPlaceholder = EDITABLE_SEGMENTS[segment.type] && !validSegments[segment.type];
420+
let placeholder = EDITABLE_SEGMENTS[segment.type] ? getPlaceholder(segment.type, segment.value, locale) : null;
421+
422+
let dateSegment = {
423+
type: TYPE_MAPPING[segment.type] || segment.type,
424+
text: isPlaceholder ? placeholder : segment.value,
425+
...getSegmentLimits(displayValue, segment.type, resolvedOptions),
426+
isPlaceholder,
427+
placeholder,
428+
isEditable
429+
} as DateSegment;
430+
431+
if (segment.type === 'hour') {
432+
processedSegments.push({
433+
type: 'literal',
434+
text: '\u2066',
435+
...getSegmentLimits(displayValue, 'literal', resolvedOptions),
436+
isPlaceholder: false,
437+
placeholder: '',
438+
isEditable: false
439+
});
440+
processedSegments.push(dateSegment);
441+
if (segment.type === granularity) {
442+
processedSegments.push({
443+
type: 'literal',
444+
text: '\u2069',
445+
...getSegmentLimits(displayValue, 'literal', resolvedOptions),
446+
isPlaceholder: false,
447+
placeholder: '',
448+
isEditable: false
449+
});
450+
}
451+
} else if (timeValue.includes(granularity) && segment.type === granularity) {
452+
processedSegments.push(dateSegment);
453+
processedSegments.push({
454+
type: 'literal',
455+
text: '\u2069',
456+
...getSegmentLimits(displayValue, 'literal', resolvedOptions),
457+
isPlaceholder: false,
458+
placeholder: '',
459+
isEditable: false
460+
});
461+
} else {
462+
processedSegments.push(dateSegment);
463+
}
464+
}
465+
466+
return processedSegments;
467+
}
468+
453469
function getSegmentLimits(date: DateValue, type: string, options: Intl.ResolvedDateTimeFormatOptions) {
454470
switch (type) {
455471
case 'era': {

packages/react-aria-components/src/DateField.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ export const DateSegment = /*#__PURE__*/ (forwardRef as forwardRefType)(function
344344
<span
345345
{...mergeProps(filterDOMProps(otherProps as any), segmentProps, focusProps, hoverProps)}
346346
{...renderProps}
347+
style={segmentProps.style}
347348
ref={domRef}
348349
data-placeholder={segment.isPlaceholder || undefined}
349350
data-invalid={state.isInvalid || undefined}

0 commit comments

Comments
 (0)