// Copyright (c) Meta Platforms, Inc. and affiliates. 'use client'; /** * @file TouchDateTimeField.tsx * @input Uses React, Field, BottomSheet, SegmentedControl, DateInput month picker primitives, Wheel, StyleX intrinsic flex layout * @output Exports TouchDateTimeField — the coarse-pointer surface behind DateTimeInput * @position Internal component; consumed by DateTimeInput.tsx * * The touch half of `DateTimeInput`, holding `DateTimeInput`'s whole prop * contract so the desktop and touch surfaces are interchangeable. The desktop * only props `timeIncrement` and `timeOptionInterval` remain accepted but are * intentionally ignored here: time is selected with wheels instead of a typed * field or preset list. * * SYNC: When modified, update these files to stay in sync: * - /packages/core/src/DateTimeInput/DateTimeInput.tsx * - /packages/core/src/DateTimeInput/DateTimeInput.doc.mjs * - /packages/core/src/DateTimeInput/DateTimeInputTouch.test.tsx * - /apps/storybook/stories/DateTimeInput.stories.tsx * - /packages/cli/assets/templates/blocks/components/DateTimeInput/ */ import { use, useCallback, useEffect, useId, useMemo, useOptimistic, useRef, useState, useTransition } from 'react'; import * as stylex from "../style-runtime.js"; import { BottomSheet } from "../BottomSheet/index.js"; import { Button } from "../Button/index.js"; import { useCalendarConstraints, type ISODateString } from "../Calendar/index.js"; import { Field, InputClearButton, inputWrapperStyles, inputStatusBorderStyles, inputStatusHoverShadowStyles, inputStatusFocusWithinStyles } from "../Field/index.js"; import { useInputStatusIcon, useMergedRefs } from "../hooks/index.js"; import { useResolvedRequired } from "../hooks/useResolvedRequired.js"; import { Icon } from "../Icon/index.js"; import { IconButton } from "../IconButton/index.js"; import { useTranslator, InternationalizationContext } from "../i18n/index.js"; import { SegmentedControl, SegmentedControlItem } from "../SegmentedControl/index.js"; import { useSize } from "../SizeContext/SizeContext.js"; import { Spinner } from "../Spinner/index.js"; import { useTooltip } from "../Tooltip/index.js"; import "../theme/tokens.stylex.js"; import { colorVars, fontWeightVars, radiusVars, sizeVars, spacingVars, typeScaleVars, typographyVars } from "../theme/tokens.stylex.js"; import { focusOutlineStyles } from "../utils/focusOutline.stylex.js"; import { rtlStyles } from "../utils/rtlStyles.js"; import { themeProps } from "../utils/themeProps.js"; import { normalizeDayOfWeek } from "../utils/dateTypes.js"; import { formatDisplayTime12h, formatDisplayTime24h, formatISOTime, isImeKeyEvent, isRenderable, isTimeInRange, clampTime, mergeProps, parseISOTime, type ISOTimeString } from "../utils/index.js"; import { DATE_FORMAT_LONG, DATE_FORMAT_MONTH_YEAR, DATE_FORMAT_WEEKDAY_ONLY, plainDateFormat, plainDateFromISO, plainDateToday, type PlainDate } from "../utils/plainDate.js"; import { MonthScroller, type MonthScrollerHandle } from "../DateInput/MonthScroller.js"; import { MonthYearWheels } from "../DateInput/MonthYearWheels.js"; import { Wheel, type WheelOption } from "../DateInput/Wheel.js"; import "../DateInput/tokens.stylex.js"; import { dateInputTouchGeometry, dateInputTouchSizes } from "../DateInput/tokens.stylex.js"; import { DEFAULT_MONTH_REACH, clampIndex, fromMonthIndex, monthIndexOf } from "../DateInput/monthGeometry.js"; import type { DateTimeInputProps, ISODateTimeString } from "./DateTimeInput.js"; function splitDateTime(dt: ISODateTimeString | undefined): { date: ISODateString | undefined; time: ISOTimeString | undefined; } { if (!dt) { return { date: undefined, time: undefined }; } const tIndex = dt.indexOf('T'); if (tIndex === -1) { return { date: dt as unknown as ISODateString, time: undefined }; } return { date: dt.slice(0, tIndex) as ISODateString, time: dt.slice(tIndex + 1) as ISOTimeString }; } function combineDateTime(date: ISODateString | undefined, time: ISOTimeString | undefined): ISODateTimeString | undefined { if (!date || !time) { return undefined; } return `${date}T${time}` as ISODateTimeString; } function getDefaultTime(hasSeconds: boolean): ISOTimeString { const now = new Date(); return formatISOTime({ hour: now.getHours(), minute: now.getMinutes(), second: now.getSeconds() }, hasSeconds); } const sizeStyles = { sm: { kZKoxP: "x6k0iem", $$css: true as const }, md: { kZKoxP: "x1ueg155", $$css: true as const }, lg: { kZKoxP: "xssyfek", $$css: true as const } }; const HORIZONTAL_SEGMENT_BASIS = 196; const styles = { iconButton: { k1xSpc: "x78zum5", kGNEyG: "x6s0dn4", kjj79g: "xl56j7k", kmVPX3: "x1717udv", kogj98: "x1ghz6dp", kMzoRj: "xc342km", ksu8eU: "xng3xce", kWkggS: "xjbqb8w", kkrTdU: "x1ypdohk x16khyan", kaIpWk: "xh6dtrn", $$css: true as const }, iconButtonDisabled: { kkrTdU: "xt0e3qv", $$css: true as const }, touchRow: { k1xSpc: "x78zum5", kwnvtZ: "x1a02dak", kzqmXN: "xh8yej3", k7Eaqz: "xeuugli", kOIVth: "x1txdalj", $$css: true as const }, touchDateWrapper: { kUk6DE: "x98rzlu", kCS8Yb: "xrjwirl", k7Eaqz: "xeuugli", $$css: true as const }, touchTimeWrapper: { kUk6DE: "x98rzlu", kCS8Yb: "xrjwirl", k7Eaqz: "xeuugli", $$css: true as const }, touchTitle: { k1xSpc: "x78zum5", kGNEyG: "x6s0dn4", kOIVth: "xzye2dw", k7Eaqz: "xeuugli", kg3NbH: "xf314gf", kMzoRj: "xc342km", ksu8eU: "xng3xce", kaIpWk: "xh6dtrn", kWkggS: "xjbqb8w", kMwMTN: "x1tgivj0", kMv6JI: "x9ynric", kGuDYH: "x18juvz8", k63SB2: "x2mo6ok", kkrTdU: "x1ypdohk x16khyan", $$css: true as const }, touchArrow: { kAzted: "xygd9yz", k7Eaqz: "xngvp7v", $$css: true as const }, touchArrowUnavailable: { k33iCy: "xlshs6z", $$css: true as const }, touchArrowIcon: { k1xSpc: "x3nfvp2", $$css: true as const }, touchResetButton: { kAzted: "xygd9yz", $$css: true as const } }; const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/; function parseValidISODate(date: ISODateString | undefined): PlainDate | null { if (date == null || !ISO_DATE.test(date)) { return null; } try { return plainDateFromISO(date); } catch { return null; } } function normalizeISOTime(time: ISOTimeString | undefined, hasSeconds: boolean): ISOTimeString | undefined { if (time === undefined) { return undefined; } const parsed = parseISOTime(time); return parsed ? formatISOTime(parsed, hasSeconds) : undefined; } function timeToSeconds(time: ISOTimeString | undefined): number | null { if (time === undefined) { return null; } const parsed = parseISOTime(time); return parsed == null ? null : parsed.hour * 3600 + parsed.minute * 60 + parsed.second; } function twoDigits(value: number): string { return String(value).padStart(2, '0'); } function hour12From24(hour: number): number { return hour === 0 ? 12 : hour > 12 ? hour - 12 : hour; } function hour24From12(hour: number, meridiem: number): number { if (meridiem === 0) { return hour === 12 ? 0 : hour; } return hour === 12 ? 12 : hour + 12; } function rangeOverlaps(startSecond: number, endSecond: number, min: ISOTimeString | undefined, max: ISOTimeString | undefined): boolean { const minSecond = timeToSeconds(min); const maxSecond = timeToSeconds(max); return (minSecond == null || endSecond >= minSecond) && (maxSecond == null || startSecond <= maxSecond); } type TouchDateTimePanel = 'date' | 'time'; export function TouchDateTimeField({ label, isLabelHidden = false, description, isOptional = false, isRequired = false, isDisabled = false, disabledMessage, value, onChange, changeAction, isLoading = false, min, max, dateConstraints, hasSeconds = false, hourFormat = '12h', // Desktop-only: mobile uses wheels instead of typed arrow stepping or a preset list. timeIncrement: _timeIncrement, timeOptionInterval: _timeOptionInterval, hasClear = false, placeholder: placeholderFromProps, timePlaceholder: timePlaceholderFromProps, timeLabel, size: sizeProp, status, labelTooltip, // Desktop-only: the touch Date panel shows one swipe-paged month at a time. numberOfMonths: _numberOfMonths, weekStartsOn: weekStartsOnProp = 0, width, xstyle, className, style, ref, ...rest }: DateTimeInputProps) { const t = useTranslator(); const isEffectivelyRequired = useResolvedRequired({ isRequired, isOptional }); const { locale } = use(InternationalizationContext); const placeholder = placeholderFromProps ?? t('@astryx.dateTimeInput.placeholder'); const timePlaceholder = timePlaceholderFromProps ?? t('@astryx.dateTimeInput.timePlaceholder'); const resolvedTimeLabel = timeLabel ?? t('@astryx.dateTimeInput.timeSuffix', { label }); const size = useSize(sizeProp, 'md'); const dateInputId = useId(); const timeInputId = useId(); const descriptionID = useId(); const statusMessageID = useId(); const dateInputRef = useRef(null); const timeInputRef = useRef(null); const timeSegmentRef = useRef(null); const mergedDateInputRef = useMergedRefs(ref, dateInputRef); const scrollerHandleRef = useRef(null); const [, startTransition] = useTransition(); const [optimisticValue, setOptimisticValue] = useOptimistic(value); const isPendingChange = optimisticValue !== value; const isBusy = isLoading || isPendingChange; const isEffectivelyDisabled = isDisabled || isLoading; const showsDisabledMessage = isDisabled && !!disabledMessage; const disabledMessageTooltip = useTooltip({ placement: 'above', focusTrigger: 'always', isEnabled: showsDisabledMessage }); const { statusIcon, describedBy: statusTooltipDescribedBy } = useInputStatusIcon({ status, statusVariant: 'detached' }); const ariaDescribedBy = [description ? descriptionID : null, status?.message ? statusMessageID : null, statusTooltipDescribedBy, showsDisabledMessage ? disabledMessageTooltip.describedBy : null].filter(Boolean).join(' ') || undefined; const minParts = useMemo(() => splitDateTime(min), [min]); const maxParts = useMemo(() => splitDateTime(max), [max]); const valueParts = useMemo(() => splitDateTime(optimisticValue), [optimisticValue]); const calendarMin = minParts.date; const calendarMax = maxParts.date; const { isDateDisabled } = useCalendarConstraints({ min: calendarMin, max: calendarMax, dateConstraints }); const [isSheetOpen, setIsSheetOpen] = useState(false); const [activePanel, setActivePanel] = useState('date'); const [isWheelOpen, setIsWheelOpen] = useState(false); const [draftTime, setDraftTime] = useState(() => normalizeISOTime(splitDateTime(value).time, hasSeconds)); const today = useMemo(() => plainDateToday(), []); const selectedDate = useMemo(() => parseValidISODate(valueParts.date), [valueParts.date]); const fallbackTime = useMemo(() => getDefaultTime(hasSeconds), [hasSeconds]); const normalizedValueTime = normalizeISOTime(valueParts.time, hasSeconds); const timeForWheels = normalizedValueTime ?? draftTime ?? fallbackTime; const parsedWheelTime = useMemo(() => parseISOTime(timeForWheels) ?? { hour: 0, minute: 0, second: 0 }, [timeForWheels]); const weekStartsOn = normalizeDayOfWeek(weekStartsOnProp); useEffect(() => { if (normalizedValueTime !== undefined) { // eslint-disable-next-line @eslint-react/set-state-in-effect -- mirrors an externally controlled time into the sheet-local draft setDraftTime(normalizedValueTime); } else if (optimisticValue === undefined) { // eslint-disable-next-line @eslint-react/set-state-in-effect -- external clear resets the sheet-local draft too setDraftTime(undefined); } }, [normalizedValueTime, optimisticValue]); const [anchorMonthIndex] = useState(() => monthIndexOf(selectedDate ?? plainDateToday())); const minMonthIndex = calendarMin != null ? monthIndexOf(plainDateFromISO(calendarMin)) : anchorMonthIndex - DEFAULT_MONTH_REACH; const maxMonthIndex = calendarMax != null ? monthIndexOf(plainDateFromISO(calendarMax)) : anchorMonthIndex + DEFAULT_MONTH_REACH; const [monthIndex, setMonthIndex] = useState(() => clampIndex(anchorMonthIndex, minMonthIndex, maxMonthIndex)); const { year, month } = fromMonthIndex(monthIndex); const canStepBack = monthIndex > minMonthIndex; const canStepForward = monthIndex < maxMonthIndex; useEffect(() => { const clamped = clampIndex(monthIndex, minMonthIndex, maxMonthIndex); if (clamped !== monthIndex) { // eslint-disable-next-line @eslint-react/set-state-in-effect -- min/max can change underneath the mounted sheet; keep the visible month in range setMonthIndex(clamped); scrollerHandleRef.current?.scrollToMonth(clamped, 'auto'); } }, [monthIndex, minMonthIndex, maxMonthIndex]); const dayNames = useMemo(() => Array.from({ length: 7 }, (_, offset) => plainDateFormat({ year: 1970, month: 1, day: 4 + (weekStartsOn + offset) % 7 }, DATE_FORMAT_WEEKDAY_ONLY, locale)), [weekStartsOn, locale]); const monthYearLabel = plainDateFormat({ year, month, day: 1 }, DATE_FORMAT_MONTH_YEAR, locale); const dateDisplayValue = useMemo(() => { if (selectedDate == null) { return ''; } return plainDateFormat(selectedDate, DATE_FORMAT_LONG, locale); }, [selectedDate, locale]); const timeDisplayValue = useMemo(() => { const displayTime = normalizedValueTime ?? draftTime; return displayTime ? (hourFormat === '12h' ? formatDisplayTime12h : formatDisplayTime24h)(displayTime, hasSeconds) : ''; }, [draftTime, normalizedValueTime, hourFormat, hasSeconds]); const timeBoundsForDate = useCallback((date: ISODateString | undefined) => ({ min: date != null && minParts.date === date && minParts.time ? minParts.time : undefined, max: date != null && maxParts.date === date && maxParts.time ? maxParts.time : undefined }), [minParts.date, minParts.time, maxParts.date, maxParts.time]); const clampTimeForDate = useCallback((date: ISODateString | undefined, time: ISOTimeString): ISOTimeString => { const bounds = timeBoundsForDate(date); return clampTime(time, bounds.min, bounds.max, hasSeconds); }, [hasSeconds, timeBoundsForDate]); const fireChange = useCallback((newValue: ISODateTimeString | undefined) => { if (isEffectivelyDisabled) { return; } onChange(newValue); if (changeAction) { startTransition(async () => { setOptimisticValue(newValue); await changeAction(newValue); }); } }, [isEffectivelyDisabled, onChange, changeAction, startTransition, setOptimisticValue]); const openSheet = useCallback((panel: TouchDateTimePanel) => { if (!isEffectivelyDisabled) { setIsWheelOpen(false); setActivePanel(panel); setIsSheetOpen(true); } }, [isEffectivelyDisabled]); const closeSheet = useCallback(() => { if (selectedDate == null) { // eslint-disable-next-line @eslint-react/set-state-in-effect -- The shared close path is also used by the disabled/loading effect. setDraftTime(undefined); } // eslint-disable-next-line @eslint-react/set-state-in-effect -- The shared close path is also used by the disabled/loading effect. setIsSheetOpen(false); }, [selectedDate]); const handleSheetOpenChange = useCallback((nextOpen: boolean) => { if (nextOpen) { setIsSheetOpen(true); } else { closeSheet(); } }, [closeSheet]); useEffect(() => { if (isEffectivelyDisabled && isSheetOpen) { closeSheet(); } }, [closeSheet, isEffectivelyDisabled, isSheetOpen]); const stepMonth = useCallback((delta: number) => { const target = clampIndex(monthIndex + delta, minMonthIndex, maxMonthIndex); if (target === monthIndex) { return; } setMonthIndex(target); scrollerHandleRef.current?.scrollToMonth(target, 'smooth'); }, [monthIndex, minMonthIndex, maxMonthIndex]); const handleResetInSheet = useCallback(() => { fireChange(undefined); setDraftTime(undefined); const currentMonth = monthIndexOf(today); if (currentMonth < minMonthIndex || currentMonth > maxMonthIndex) { return; } setMonthIndex(currentMonth); scrollerHandleRef.current?.scrollToMonth(currentMonth, 'smooth'); }, [fireChange, today, minMonthIndex, maxMonthIndex]); const handleDateSelect = useCallback((nextDate: ISODateString) => { const nextTime = clampTimeForDate(nextDate, timeForWheels); setDraftTime(nextTime); const combined = combineDateTime(nextDate, nextTime); if (combined && combined !== optimisticValue) { fireChange(combined); } }, [clampTimeForDate, fireChange, optimisticValue, timeForWheels]); const commitWheelTime = useCallback((rawTime: ISOTimeString) => { const nextTime = clampTimeForDate(valueParts.date, rawTime); setDraftTime(nextTime); if (valueParts.date != null) { const combined = combineDateTime(valueParts.date, nextTime); if (combined && combined !== optimisticValue) { fireChange(combined); } } }, [clampTimeForDate, fireChange, optimisticValue, valueParts.date]); const composeWheelTime = useCallback(({ hour24, hour12, minute, second, meridiem }: { hour24?: number; hour12?: number; minute?: number; second?: number; meridiem?: number; }): ISOTimeString => { const currentMeridiem = parsedWheelTime.hour < 12 ? 0 : 1; let hour = parsedWheelTime.hour; if (hourFormat === '24h') { hour = hour24 ?? hour; } else { hour = hour24From12(hour12 ?? hour12From24(hour), meridiem ?? currentMeridiem); } return formatISOTime({ hour, minute: minute ?? parsedWheelTime.minute, second: hasSeconds ? second ?? parsedWheelTime.second : 0 }, hasSeconds); }, [hasSeconds, hourFormat, parsedWheelTime]); const timeBounds = timeBoundsForDate(valueParts.date); const hourOptions: WheelOption[] = useMemo(() => { if (hourFormat === '24h') { return Array.from({ length: 24 }, (_, hour) => ({ value: hour, label: twoDigits(hour), isDisabled: !rangeOverlaps(hour * 3600, hour * 3600 + 3599, timeBounds.min, timeBounds.max) })); } const meridiem = parsedWheelTime.hour < 12 ? 0 : 1; // In 12-hour mode the hour column is interpreted inside the active AM/PM // half, so disabled rows describe reachability within that half only. return Array.from({ length: 12 }, (_, index) => { const hour = index + 1; const hour24 = hour24From12(hour, meridiem); return { value: hour, label: String(hour), isDisabled: !rangeOverlaps(hour24 * 3600, hour24 * 3600 + 3599, timeBounds.min, timeBounds.max) }; }); }, [hourFormat, parsedWheelTime.hour, timeBounds.min, timeBounds.max]); const minuteOptions: WheelOption[] = useMemo(() => Array.from({ length: 60 }, (_, minute) => { const start = parsedWheelTime.hour * 3600 + minute * 60; return { value: minute, label: twoDigits(minute), isDisabled: !rangeOverlaps(start, start + (hasSeconds ? 59 : 0), timeBounds.min, timeBounds.max) }; }), [hasSeconds, parsedWheelTime.hour, timeBounds.min, timeBounds.max]); const secondOptions: WheelOption[] = useMemo(() => Array.from({ length: 60 }, (_, second) => ({ value: second, label: twoDigits(second), isDisabled: !isTimeInRange(formatISOTime({ hour: parsedWheelTime.hour, minute: parsedWheelTime.minute, second }, true), timeBounds.min, timeBounds.max) })), [parsedWheelTime.hour, parsedWheelTime.minute, timeBounds.min, timeBounds.max]); const meridiemOptions: WheelOption[] = useMemo(() => [{ value: 0, label: t('@astryx.dateTimeInput.meridiemAM'), isDisabled: !rangeOverlaps(0, 11 * 3600 + 3599, timeBounds.min, timeBounds.max) }, { value: 1, label: t('@astryx.dateTimeInput.meridiemPM'), isDisabled: !rangeOverlaps(12 * 3600, 23 * 3600 + 3599, timeBounds.min, timeBounds.max) }], [t, timeBounds.min, timeBounds.max]); const handleVisibleMonthChange = useCallback((next: number) => { if (activePanel === 'date' && !isWheelOpen) { setMonthIndex(next); } }, [activePanel, isWheelOpen]); const monthIndexRef = useRef(monthIndex); monthIndexRef.current = monthIndex; useEffect(() => { if (activePanel === 'date' && !isWheelOpen) { scrollerHandleRef.current?.scrollToMonth(monthIndexRef.current, 'auto'); } }, [activePanel, isWheelOpen]); const handleInputKeyDown = useCallback((event: React.KeyboardEvent, panel: TouchDateTimePanel) => { if (isImeKeyEvent(event.nativeEvent)) { return; } if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ' || event.key === 'Spacebar') { event.preventDefault(); openSheet(panel); } }, [openSheet]); const clearFocusTimerRef = useRef(null); useEffect(() => () => { if (clearFocusTimerRef.current != null) { clearTimeout(clearFocusTimerRef.current); } }, []); const timeSegmentFocusTimerRef = useRef(null); useEffect(() => () => { if (timeSegmentFocusTimerRef.current != null) { clearTimeout(timeSegmentFocusTimerRef.current); } }, []); const handleClear = useCallback((event: React.MouseEvent) => { event.stopPropagation(); fireChange(undefined); setDraftTime(undefined); setIsSheetOpen(false); const field = dateInputRef.current; if (field == null) { return; } clearFocusTimerRef.current = window.setTimeout(() => { clearFocusTimerRef.current = null; field.focus({ preventScroll: true }); }, 0); }, [fireChange]); const surface =
{ setActivePanel(nextPanel as TouchDateTimePanel); setIsWheelOpen(false); }} label={t('@astryx.dateTimeInput.pickerMode')} layout="fill">
stepMonth(-1)} label={t('@astryx.calendar.previousMonth')} icon={ } /> stepMonth(1)} label={t('@astryx.calendar.nextMonth')} icon={ } />
commitWheelTime(hourFormat === '24h' ? composeWheelTime({ hour24: hour }) : composeWheelTime({ hour12: hour }))} /> commitWheelTime(composeWheelTime({ minute }))} /> {hasSeconds && commitWheelTime(composeWheelTime({ second }))} />} {hourFormat === '12h' && commitWheelTime(composeWheelTime({ meridiem }))} />}
; return
{ disabledMessageTooltip.ref(el); }} {...rest} {...mergeProps(themeProps('date-time-input', { size, status: status?.type ?? null, disabled: isDisabled ? 'disabled' : null }), stylex.props(styles.touchRow, xstyle), className, style)}>
{ if (event.target === event.currentTarget) { openSheet('date'); } }} {...mergeProps(themeProps('date-time-input-date-segment', { size, status: status?.type ?? null }), stylex.props(inputWrapperStyles.base, sizeStyles[size], styles.touchDateWrapper, isEffectivelyDisabled && inputWrapperStyles.disabled, status && inputStatusBorderStyles[status.type], status && !isEffectivelyDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type]))}> {}} onClick={() => openSheet('date')} onKeyDown={event => handleInputKeyDown(event, 'date')} placeholder={placeholder} disabled={isEffectivelyDisabled && !showsDisabledMessage} aria-disabled={showsDisabledMessage ? 'true' : undefined} aria-describedby={ariaDescribedBy} aria-required={isEffectivelyRequired ? 'true' : undefined} aria-invalid={status?.type === 'error' ? 'true' : undefined} aria-busy={isBusy || undefined} aria-expanded={isSheetOpen && activePanel === 'date'} aria-haspopup="dialog" aria-autocomplete="none" autoComplete="off" {...{ 0: { className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 xf9zqsd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5 xbuiw85 x1ypdohk x16khyan x87ps6o" }, 1: { className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 xf9zqsd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5 xbuiw85 x87ps6o xt0e3qv" } }[Number(!!isEffectivelyDisabled) << 0]} /> {hasClear && value !== undefined && !isEffectivelyDisabled && } {isBusy && } {isRenderable(statusIcon) && statusIcon}
{ if (event.target === event.currentTarget) { openSheet('time'); } }} {...mergeProps(themeProps('date-time-input-time-segment', { size, status: status?.type ?? null }), stylex.props(inputWrapperStyles.base, sizeStyles[size], styles.touchTimeWrapper, isEffectivelyDisabled && inputWrapperStyles.disabled, status && inputStatusBorderStyles[status.type], status && !isEffectivelyDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type]))}> {}} onClick={() => openSheet('time')} onKeyDown={event => handleInputKeyDown(event, 'time')} placeholder={timePlaceholder} disabled={isEffectivelyDisabled && !showsDisabledMessage} aria-disabled={showsDisabledMessage ? 'true' : undefined} aria-label={resolvedTimeLabel} aria-describedby={ariaDescribedBy} aria-required={isEffectivelyRequired ? 'true' : undefined} aria-invalid={status?.type === 'error' ? 'true' : undefined} aria-busy={isBusy || undefined} aria-expanded={isSheetOpen && activePanel === 'time'} aria-haspopup="dialog" aria-autocomplete="none" autoComplete="off" {...{ 0: { className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 xf9zqsd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5 xbuiw85 x1ypdohk x16khyan x87ps6o" }, 1: { className: "x1lliihq x98rzlu xeuugli xc342km xng3xce x1717udv x9ynric xjm74w1 xf9zqsd xw6l6zx x1tgivj0 xjbqb8w x1a2a7pz xeyghm5 xbuiw85 x87ps6o xt0e3qv" } }[Number(!!isEffectivelyDisabled) << 0]} />
{surface}
{showsDisabledMessage && disabledMessageTooltip.renderTooltip(disabledMessage)}
; } TouchDateTimeField.displayName = 'TouchDateTimeField';