focus the first invalid measurement on page load and when trying to leave
This commit is contained in:
parent
018b5372fe
commit
ce23291fc5
3 changed files with 35 additions and 10 deletions
|
@ -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={`
|
||||||
|
|
|
@ -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} />
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue