fix utm visual selector

This commit is contained in:
Nastya 2025-10-27 22:56:02 +03:00
parent 639929d825
commit 75aca56c01
2 changed files with 87 additions and 45 deletions

@ -125,9 +125,7 @@ export default function QuizMarkCreate() {
fullWidth
size="small"
sx={{
width: "100%",
maxWidth: "118px",
width: "137px",
height: "24px",
}}
>

@ -26,21 +26,36 @@ const OverTime = () => {
Math.max(0, (initialOverTime.endsAt || 0) - Date.now())
);
const [isEditing, setIsEditing] = useState<boolean>(false);
const daysRef = useRef<HTMLInputElement | null>(null);
const hoursRef = useRef<HTMLInputElement | null>(null);
const minutesRef = useRef<HTMLInputElement | null>(null);
const secondsRef = useRef<HTMLInputElement | null>(null);
const isAnyTimeInputFocused = (): boolean => {
const active = document.activeElement as HTMLElement | null;
return [daysRef.current, hoursRef.current, minutesRef.current, secondsRef.current].some(
(ref) => ref !== null && ref === active
);
};
const toDigits = (value: string): string => (value || "").replace(/\D+/g, "");
const clampTwoDigits = (value: string): string => {
const digits = toDigits(value).slice(0, 2);
if (digits.length === 0) return "";
const num = Number(digits);
if (isNaN(num)) return "";
if (num > 60) return "60";
return digits;
};
const clampTwoDigitsDays = (value: string): string => toDigits(value).slice(0, 2);
const clampTwoDigits = (value: string): string => {
const digits = toDigits(value).slice(0, 2);
if (digits.length === 0) return "";
const num = Number(digits);
if (isNaN(num)) return "";
return num > 60 ? "60" : digits;
};
const clampTwoDigitsDays = (value: string): string => {
return toDigits(value).slice(0, 2);
};
const pad2 = (value: string): string => {
const d = toDigits(value);
return (d.length === 0 ? "00" : d.padStart(2, "0").slice(-2));
};
const allowControlKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
const allowed = ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "Tab", "Home", "End", "Enter"];
@ -95,16 +110,6 @@ const OverTime = () => {
const cfgEndsAt = (quiz as any)?.config?.overTime?.endsAt ?? 0;
const ms = Math.max(0, cfgEndsAt - Date.now());
setRemainingMs(ms);
if (ms <= 0) {
// выключаем флаг
setEnabled(false);
updateQuiz(quiz!.id, (q) => {
const cfg: any = (q as any).config;
if (!cfg.overTime) cfg.overTime = { enabled: false, endsAt: 0, description: "" };
cfg.overTime.enabled = false;
cfg.overTime.endsAt = 0;
});
}
};
// первый расчёт по requestAnimationFrame для мгновенного обновления
rafId = window.requestAnimationFrame(() => tick());
@ -135,6 +140,19 @@ const OverTime = () => {
return { d, h, m, s };
}, [remainingMs]);
// Показываем обратный отсчёт в полях ввода, когда счётчик включён
useEffect(() => {
if (!enabled || isEditing) return;
const dStr = String(rem.d).padStart(2, "0").slice(-2);
const hStr = fmt(rem.h).slice(-2);
const mStr = fmt(rem.m).slice(-2);
const sStr = fmt(rem.s).slice(-2);
setDays(dStr);
setHours(hStr);
setMinutes(mStr);
setSeconds(sStr);
}, [enabled, rem, isEditing]);
if (!quiz) return null;
return (
<Box sx={{ display: "flex", flexDirection: "column", mt: "20px", padding: "15px", backgroundColor: "#F2F3F7", width: "100%", maxWidth: "357px", height: "295px", borderRadius: "8px" }}>
@ -204,14 +222,22 @@ const OverTime = () => {
<CustomTextField
placeholder="00"
value={days}
onChange={(e) => {
const next = clampTwoDigitsDays(e.target.value);
onChange={(e) => {
const next = clampTwoDigitsDays(e.target.value);
setDays(next);
persistEndsAt(next, hours, minutes, seconds);
}}
onKeyDown={handleTwoDigitKeyDown(days) as any}
inputRef={daysRef}
onFocus={() => daysRef.current?.select()}
onFocus={() => { setIsEditing(true); daysRef.current?.select(); }}
onBlur={() => setTimeout(() => {
setIsEditing(isAnyTimeInputFocused());
const nd = pad2(days);
const nh = pad2(hours);
const nm = pad2(minutes);
const ns = pad2(seconds);
setDays(nd);
persistEndsAt(nd, nh, nm, ns);
}, 0)}
onClick={() => daysRef.current?.select()}
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }}
@ -231,14 +257,22 @@ const OverTime = () => {
<CustomTextField
placeholder="00"
value={hours}
onChange={(e) => {
const next = clampTwoDigits(e.target.value);
onChange={(e) => {
const next = clampTwoDigits(e.target.value);
setHours(next);
persistEndsAt(days, next, minutes, seconds);
}}
onKeyDown={handleTwoDigitKeyDown(hours) as any}
inputRef={hoursRef}
onFocus={() => hoursRef.current?.select()}
onFocus={() => { setIsEditing(true); hoursRef.current?.select(); }}
onBlur={() => setTimeout(() => {
setIsEditing(isAnyTimeInputFocused());
const nd = pad2(days);
const nh = pad2(hours);
const nm = pad2(minutes);
const ns = pad2(seconds);
setHours(nh);
persistEndsAt(nd, nh, nm, ns);
}, 0)}
onClick={() => hoursRef.current?.select()}
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }}
@ -258,14 +292,22 @@ const OverTime = () => {
<CustomTextField
placeholder="00"
value={minutes}
onChange={(e) => {
const next = clampTwoDigits(e.target.value);
onChange={(e) => {
const next = clampTwoDigits(e.target.value);
setMinutes(next);
persistEndsAt(days, hours, next, seconds);
}}
onKeyDown={handleTwoDigitKeyDown(minutes) as any}
inputRef={minutesRef}
onFocus={() => minutesRef.current?.select()}
onFocus={() => { setIsEditing(true); minutesRef.current?.select(); }}
onBlur={() => setTimeout(() => {
setIsEditing(isAnyTimeInputFocused());
const nd = pad2(days);
const nh = pad2(hours);
const nm = pad2(minutes);
const ns = pad2(seconds);
setMinutes(nm);
persistEndsAt(nd, nh, nm, ns);
}, 0)}
onClick={() => minutesRef.current?.select()}
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }}
@ -285,14 +327,22 @@ const OverTime = () => {
<CustomTextField
placeholder="00"
value={seconds}
onChange={(e) => {
const next = clampTwoDigits(e.target.value);
onChange={(e) => {
const next = clampTwoDigits(e.target.value);
setSeconds(next);
persistEndsAt(days, hours, minutes, next);
}}
onKeyDown={handleTwoDigitKeyDown(seconds) as any}
inputRef={secondsRef}
onFocus={() => secondsRef.current?.select()}
onFocus={() => { setIsEditing(true); secondsRef.current?.select(); }}
onBlur={() => setTimeout(() => {
setIsEditing(isAnyTimeInputFocused());
const nd = pad2(days);
const nh = pad2(hours);
const nm = pad2(minutes);
const ns = pad2(seconds);
setSeconds(ns);
persistEndsAt(nd, nh, nm, ns);
}, 0)}
onClick={() => secondsRef.current?.select()}
InputProps={{ inputProps: { pattern: "\\d*", inputMode: "numeric", maxLength: 2 } }}
sx={{ height: "48px", width: "100%", maxWidth: "51px", backgroundColor: "white", p: "8px 6px" }}
@ -309,13 +359,7 @@ const OverTime = () => {
</Typography>
</Box>
</Box>
{enabled && (
<Box sx={{ mt: "10px" }}>
<Typography sx={{ fontWeight: 500, color: theme.palette.grey3.main, fontSize: "16px" }}>
До конца: {rem.d} д {fmt(rem.h)}:{fmt(rem.m)}:{fmt(rem.s)}
</Typography>
</Box>
)}
{/* Удалён маленький таймер с обратным отсчётом */}
</Box>
</Box>
)