feat(wizard): renderer pregunta-por-pantalla con layout estable [4D]
This commit is contained in:
@@ -602,7 +602,13 @@ body {
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding-bottom: 8px;
|
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: '';
|
content: '';
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||||
import type { BrandConfig, Survey } from '@/lib/types'
|
import type { BrandConfig, Survey } from '@/lib/types'
|
||||||
import type { AnswerValue, FormAnswers, FormSchema, Screen } from '@/lib/form-engine.types'
|
import type { AnswerValue, FormAnswers, FormSchema, Screen } from '@/lib/form-engine.types'
|
||||||
import {
|
import {
|
||||||
@@ -52,6 +52,8 @@ export function WizardForm({ survey, schema }: WizardFormProps) {
|
|||||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
const [hydrated, setHydrated] = useState(false)
|
const [hydrated, setHydrated] = useState(false)
|
||||||
const [a11yAnnounce, setA11yAnnounce] = useState('')
|
const [a11yAnnounce, setA11yAnnounce] = useState('')
|
||||||
|
const answerAreaRef = useRef<HTMLDivElement>(null)
|
||||||
|
const [hasOverflow, setHasOverflow] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const draft = loadDraft(survey.id)
|
const draft = loadDraft(survey.id)
|
||||||
@@ -78,6 +80,24 @@ export function WizardForm({ survey, schema }: WizardFormProps) {
|
|||||||
else setA11yAnnounce('Fin del formulario')
|
else setA11yAnnounce('Fin del formulario')
|
||||||
}, [screen?.id])
|
}, [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') {
|
if (stage === 'consent') {
|
||||||
return (
|
return (
|
||||||
<ConsentScreen
|
<ConsentScreen
|
||||||
@@ -193,7 +213,10 @@ export function WizardForm({ survey, schema }: WizardFormProps) {
|
|||||||
<h2 className="wizard-prompt-text">{screen.question.prompt}</h2>
|
<h2 className="wizard-prompt-text">{screen.question.prompt}</h2>
|
||||||
{screen.question.hint && <p className="wizard-prompt-hint">{screen.question.hint}</p>}
|
{screen.question.hint && <p className="wizard-prompt-hint">{screen.question.hint}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="wizard-answer-area">
|
<div
|
||||||
|
ref={answerAreaRef}
|
||||||
|
className={`wizard-answer-area${hasOverflow ? ' has-overflow' : ''}`}
|
||||||
|
>
|
||||||
<QuestionRenderer
|
<QuestionRenderer
|
||||||
question={screen.question}
|
question={screen.question}
|
||||||
value={currentValue}
|
value={currentValue}
|
||||||
|
|||||||
Reference in New Issue
Block a user