feat(wizard): renderer pregunta-por-pantalla con layout estable [4D]

This commit is contained in:
markosbenitez
2026-06-23 22:40:01 -03:00
parent 7c8b034756
commit ce679ec1c3
2 changed files with 32 additions and 3 deletions

View File

@@ -602,7 +602,13 @@ body {
overflow-y: auto;
padding-bottom: 8px;
}
.wizard-answer-area::after {
/* El fade SOLO se pinta cuando .wizard-answer-area mide overflow real
(clase has-overflow agregada por JS — ver wizard-form.tsx). CSS no tiene
forma de detectar scrollHeight > clientHeight por sí solo: un ::after
incondicional (o un mask-image fijo) se pinta igual sobre el final de la
caja aunque el contenido entre completo, mostrando una franja/degradado
fantasma en preguntas cortas. */
.wizard-answer-area.has-overflow::after {
content: '';
position: sticky;
bottom: 0;

View File

@@ -1,5 +1,5 @@
'use client'
import { useEffect, useState } from 'react'
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
import type { BrandConfig, Survey } from '@/lib/types'
import type { AnswerValue, FormAnswers, FormSchema, Screen } from '@/lib/form-engine.types'
import {
@@ -52,6 +52,8 @@ export function WizardForm({ survey, schema }: WizardFormProps) {
const [errors, setErrors] = useState<Record<string, string>>({})
const [hydrated, setHydrated] = useState(false)
const [a11yAnnounce, setA11yAnnounce] = useState('')
const answerAreaRef = useRef<HTMLDivElement>(null)
const [hasOverflow, setHasOverflow] = useState(false)
useEffect(() => {
const draft = loadDraft(survey.id)
@@ -78,6 +80,24 @@ export function WizardForm({ survey, schema }: WizardFormProps) {
else setA11yAnnounce('Fin del formulario')
}, [screen?.id])
// El fade de wizard-answer-area solo debe aparecer si hay overflow real —
// CSS no puede expresar esa condición (no hay selector para "este elemento
// scrollea"), así que se mide el DOM. Se remide al cambiar de pantalla
// (cambia todo el contenido) y al resize (puede cambiar cuántas opciones entran).
useLayoutEffect(() => {
const el = answerAreaRef.current
setHasOverflow(el ? el.scrollHeight > el.clientHeight : false)
}, [screen?.id])
useEffect(() => {
function recalc() {
const el = answerAreaRef.current
setHasOverflow(el ? el.scrollHeight > el.clientHeight : false)
}
window.addEventListener('resize', recalc)
return () => window.removeEventListener('resize', recalc)
}, [])
if (stage === 'consent') {
return (
<ConsentScreen
@@ -193,7 +213,10 @@ export function WizardForm({ survey, schema }: WizardFormProps) {
<h2 className="wizard-prompt-text">{screen.question.prompt}</h2>
{screen.question.hint && <p className="wizard-prompt-hint">{screen.question.hint}</p>}
</div>
<div className="wizard-answer-area">
<div
ref={answerAreaRef}
className={`wizard-answer-area${hasOverflow ? ' has-overflow' : ''}`}
>
<QuestionRenderer
question={screen.question}
value={currentValue}