1
0
Fork 0

focus the first invalid measurement on page load and when trying to leave

This commit is contained in:
Enoch Riese 2022-06-22 19:13:40 -05:00
parent 018b5372fe
commit ce23291fc5
3 changed files with 35 additions and 10 deletions

View file

@ -11,7 +11,7 @@ import measurementAsMm from 'pkgs/utils/src/measurementAsMm'
* m holds the measurement name. It's just so long to type * m holds the measurement name. It's just so long to type
* measurement and I always have some typo in it because dyslexia. * measurement and I always have some typo in it because dyslexia.
*/ */
const MeasurementInput = ({ m, gist, app, updateMeasurements }) => { const MeasurementInput = ({ m, gist, app, updateMeasurements, focus }) => {
const { t } = useTranslation(['app', 'measurements']) const { t } = useTranslation(['app', 'measurements'])
const prefix = (app.site === 'org') ? '' : 'https://freesewing.org' const prefix = (app.site === 'org') ? '' : 'https://freesewing.org'
const title = t(`measurements:${m}`) const title = t(`measurements:${m}`)
@ -30,6 +30,7 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
// keep a single reference to a debounce timer // keep a single reference to a debounce timer
const debounceTimeout = useRef(null); const debounceTimeout = useRef(null);
const input = useRef(null);
// onChange // onChange
const update = useCallback((evt) => { const update = useCallback((evt) => {
@ -68,8 +69,17 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
} }
}, [memoVal, factor]) }, [memoVal, factor])
// focus when prompted by parent
useEffect(() => {
if (focus) {
input.current.focus();
}
}, [focus])
// cleanup // cleanup
useEffect(() => clearTimeout(debounceTimeout.current), []) useEffect(() => {
clearTimeout(debounceTimeout.current)
}, [])
if (!m) return null if (!m) return null
@ -89,6 +99,7 @@ const MeasurementInput = ({ m, gist, app, updateMeasurements }) => {
<label className="input-group input-group-lg"> <label className="input-group input-group-lg">
<input <input
key={`input-${m}`} key={`input-${m}`}
ref={input}
type="text" type="text"
placeholder={title} placeholder={title}
className={` className={`

View file

@ -1,4 +1,4 @@
import React, {useMemo} from 'react' import React, {useMemo, useEffect, useState} from 'react'
import MeasurementInput from '../inputs/measurement.js' import MeasurementInput from '../inputs/measurement.js'
import { withBreasts, withoutBreasts } from '@freesewing/models' import { withBreasts, withoutBreasts } from '@freesewing/models'
import nonHuman from './non-human.js' import nonHuman from './non-human.js'
@ -27,7 +27,7 @@ const icons = {
without: <WithoutBreastsIcon />, without: <WithoutBreastsIcon />,
} }
const WorkbenchMeasurements = ({ app, design, gist, updateGist }) => { const WorkbenchMeasurements = ({ app, design, gist, updateGist, gistReady }) => {
const { t } = useTranslation(['app', 'cfp']) const { t } = useTranslation(['app', 'cfp'])
// Method to handle measurement updates // Method to handle measurement updates
@ -41,6 +41,20 @@ const WorkbenchMeasurements = ({ app, design, gist, updateGist }) => {
} }
} }
const [firstInvalid, setFirstInvalid] = useState(undefined)
useEffect(() => {
if (!gistReady) { return }
for (const m of design.config?.measurements || []) {
if (!gist?.measurements?.[m]) {
setFirstInvalid(m);
return;
}
setFirstInvalid(undefined)
}
}, [gistReady])
// Save us some typing // Save us some typing
const inputProps = useMemo(() => ({ app, updateMeasurements, gist }), [app, gist]) const inputProps = useMemo(() => ({ app, updateMeasurements, gist }), [app, gist])
@ -86,7 +100,7 @@ const WorkbenchMeasurements = ({ app, design, gist, updateGist }) => {
</div> </div>
</details> </details>
<details className="my-2"> <details open className="my-2">
<summary><h2 className="inline pl-2">{t('cfp:enterMeasurements')}</h2></summary> <summary><h2 className="inline pl-2">{t('cfp:enterMeasurements')}</h2></summary>
<Setting key={'units'} setting={'units'} config={settings.units} updateGist={updateGist} {...inputProps} /> <Setting key={'units'} setting={'units'} config={settings.units} updateGist={updateGist} {...inputProps} />
<div className="ml-2 pl-4 border-l-2"> <div className="ml-2 pl-4 border-l-2">
@ -94,7 +108,7 @@ const WorkbenchMeasurements = ({ app, design, gist, updateGist }) => {
<> <>
<h3>{t('requiredMeasurements')}</h3> <h3>{t('requiredMeasurements')}</h3>
{design.config.measurements.map(m => ( {design.config.measurements.map(m => (
<MeasurementInput key={m} m={m} {...inputProps} /> <MeasurementInput key={m} m={m} focus={m == firstInvalid} {...inputProps} />
))} ))}
</> </>
)} )}

View file

@ -47,7 +47,7 @@ const hasRequiredMeasurements = (design, gist) => {
const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false }) => { const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false }) => {
// State for gist // State for gist
const [gist, setGist, ready] = useGist(design, app); const [gist, setGist, gistReady] = useGist(design, app);
const [messages, setMessages] = useState([]) const [messages, setMessages] = useState([])
const [popup, setPopup] = useState(false) const [popup, setPopup] = useState(false)
@ -55,10 +55,10 @@ const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false
// force view to measurements // force view to measurements
useEffect(() => { useEffect(() => {
if ( if (
ready && gist._state?.view !== 'measurements' gistReady && gist._state?.view !== 'measurements'
&& !hasRequiredMeasurements(design, gist) && !hasRequiredMeasurements(design, gist)
) updateGist(['_state', 'view'], 'measurements') ) updateGist(['_state', 'view'], 'measurements')
}, [ready, gist._state.view]) }, [gistReady, gist._state.view])
// If we need to preload the gist, do so // If we need to preload the gist, do so
useEffect(() => { useEffect(() => {
@ -109,7 +109,7 @@ const WorkbenchWrapper = ({ app, design, preload=false, from=false, layout=false
} }
// Props to pass down // Props to pass down
const componentProps = { app, design, gist, updateGist, unsetGist, setGist, draft, feedback, showInfo: setPopup } const componentProps = { app, design, gist, updateGist, unsetGist, setGist, draft, feedback, gistReady, showInfo: setPopup }
// Required props for layout // Required props for layout
const layoutProps = { const layoutProps = {
app: app, app: app,