// Модальное окно «Заказать» — для товаров под заказ function TelegramIcon({ size = 16 }) { return ; } function OrderModal({ product, onClose }) { const [contactType, setContactType] = React.useState('phone'); // phone | max | telegram const [phone, setPhone] = React.useState(''); const [email, setEmail] = React.useState(''); const [name, setName] = React.useState(''); const [comment, setComment] = React.useState(''); const [agree, setAgree] = React.useState(true); const [touched, setTouched] = React.useState({}); const [submitting, setSubmitting] = React.useState(false); const [done, setDone] = React.useState(null); // { orderId } const phoneRef = React.useRef(null); const dialogRef = React.useRef(null); // ----- Bottom-sheet swipe-down (mobile only) ----- // Тачи слушаем только на «ручке» сверху диалога — внутренний скролл формы // должен работать обычным образом. На десктопе обработчики висят, но // window.innerWidth > 767 → сразу выходим. const [dragY, setDragY] = React.useState(0); const touchStartY = React.useRef(null); const onHandleTouchStart = (e) => { if (window.innerWidth > 767) return; touchStartY.current = e.touches[0].clientY; }; const onHandleTouchMove = (e) => { if (touchStartY.current == null) return; const dy = e.touches[0].clientY - touchStartY.current; if (dy > 0) setDragY(dy); }; const onHandleTouchEnd = () => { if (touchStartY.current == null) return; if (dragY > 120) onClose(); else setDragY(0); touchStartY.current = null; }; // Автофокус + блокировка скролла + закрытие по Esc React.useEffect(() => { const prevOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; setTimeout(() => phoneRef.current?.focus(), 50); const onKey = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => { document.body.style.overflow = prevOverflow; window.removeEventListener('keydown', onKey); }; }, [onClose]); // Маска телефона: +7 (___) ___-__-__ const formatPhone = (raw) => { const digits = raw.replace(/\D/g, '').replace(/^8/, '7').replace(/^(?!7)/, '7').slice(0, 11); const d = digits.slice(1); // без ведущей 7 let out = '+7'; if (d.length > 0) out += ' (' + d.slice(0, 3); if (d.length >= 3) out += ') ' + d.slice(3, 6); if (d.length >= 6) out += '-' + d.slice(6, 8); if (d.length >= 8) out += '-' + d.slice(8, 10); return out; }; const onPhoneChange = (e) => { const val = e.target.value; // Разрешим очистить полностью if (!val.replace(/\D/g, '')) { setPhone(''); return; } setPhone(formatPhone(val)); }; const phoneDigits = phone.replace(/\D/g, ''); const phoneValid = phoneDigits.length === 11; const emailValid = !email || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); const canSubmit = phoneValid && emailValid && agree && !submitting; const submit = (e) => { e.preventDefault(); setTouched({ phone: true, email: true }); if (!canSubmit) return; setSubmitting(true); // Имитация запроса setTimeout(() => { setSubmitting(false); setDone({ orderId: 'ПЭ-' + String(Math.floor(Math.random() * 90000) + 10000) }); }, 900); }; const backdropClick = (e) => { if (e.target === e.currentTarget) onClose(); }; const inputBase = { width: '100%', height: 52, padding: '0 16px', borderRadius: 12, border: '1.5px solid var(--line)', background: '#fff', fontFamily: 'var(--rc)', fontSize: 16, color: 'var(--text)', outline: 'none', transition: 'border-color .15s' }; const errorStyle = { borderColor: '#E53324' }; const labelStyle = { fontFamily: 'var(--rc)', fontSize: 13, color: 'var(--text-2)', marginBottom: 6, display: 'block' }; return (
0 ? ' dragging' : '')} style={{ width: 520, maxWidth:'100%', maxHeight:'calc(100vh - 40px)', overflowY:'auto', background:'#fff', borderRadius: 24, boxShadow:'0 30px 80px rgba(0,0,0,0.25)', animation:'om-pop .22s ease-out', transform: dragY > 0 ? `translateY(${dragY}px)` : undefined }}> {/* Drag-handle для свайпа вниз — виден только на мобиле */}
{!done ? ( <> {/* Header */}

Заказ под заказ

Перезвоним в течение 15 минут в рабочее время (9:00–19:00 МСК)
{/* Карточка товара */} {product && (
{typeof ProductPlaceholder !== 'undefined' && }
{product.name}
Арт. {product.art} · {fmtPrice(product.price)}
)}
{/* Способ связи */}
Удобный способ связи
{/* Имя */} setName(e.target.value)} placeholder="Имя" style={inputBase} /> {/* Телефон */} setTouched(t=>({...t, phone:true}))} placeholder="+7 (___) ___-__-__" inputMode="tel" autoComplete="tel" style={inputBase} /> {touched.phone && !phoneValid &&
Укажите корректный номер телефона
} {/* Email */} setEmail(e.target.value)} onBlur={()=>setTouched(t=>({...t, email:true}))} placeholder="name@company.ru" autoComplete="email" style={inputBase} /> {touched.email && !emailValid &&
Некорректный адрес электронной почты
} {/* Комментарий */}