2023-06-20 02:57:44 -05:00
|
|
|
import { isDegreeMeasurement } from 'config/measurements.mjs'
|
2023-07-26 16:38:51 -06:00
|
|
|
import { measurementAsMm, formatMm, measurementAsUnits } from 'shared/utils.mjs'
|
2023-06-20 14:30:37 -05:00
|
|
|
import { Collapse } from 'shared/components/collapse.mjs'
|
|
|
|
import { PlusIcon, EditIcon } from 'shared/components/icons.mjs'
|
2023-07-26 16:38:51 -06:00
|
|
|
import { NumberInput } from 'shared/components/workbench/menus/shared/inputs.mjs'
|
|
|
|
import { useState, useCallback } from 'react'
|
2023-06-20 02:57:44 -05:00
|
|
|
export const ns = ['account']
|
|
|
|
|
|
|
|
const Mval = ({ m, val = false, imperial = false, className = '' }) =>
|
|
|
|
val ? (
|
|
|
|
isDegreeMeasurement(m) ? (
|
|
|
|
<span className={className}>{val}°</span>
|
|
|
|
) : (
|
|
|
|
<span
|
|
|
|
dangerouslySetInnerHTML={{ __html: formatMm(val, imperial ? 'imperial' : 'metric') }}
|
|
|
|
className={className}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
) : null
|
|
|
|
|
|
|
|
const heightClasses = {
|
|
|
|
2: 'h-12',
|
|
|
|
4: 'h-10',
|
|
|
|
8: 'h-8',
|
|
|
|
16: 'h-6',
|
|
|
|
32: 'h-4',
|
|
|
|
}
|
2023-06-21 12:10:22 +02:00
|
|
|
|
2023-06-20 02:57:44 -05:00
|
|
|
const fractionClasses =
|
|
|
|
'w-full border-2 border-solid border-base-100 hover:border-secondary bg-secondary rounded bg-opacity-50 hover:bg-opacity-100'
|
2023-06-21 12:10:22 +02:00
|
|
|
|
2023-06-20 02:57:44 -05:00
|
|
|
const FractionButtons = ({ t, fraction }) => (
|
|
|
|
<div className="flex flex-row mt-1 content-center items-center justify-around">
|
|
|
|
<span className="text-xs inline-block pr-2">{t('fractions')}</span>
|
|
|
|
<div className="grow max-w-2xl flex items-baseline">
|
|
|
|
{Array.from({ length: 31 }, (_null, i) => {
|
|
|
|
let denom = 32
|
|
|
|
let num = i + 1
|
|
|
|
|
2023-06-21 12:10:22 +02:00
|
|
|
for (let n = 4; n > 0; n--) {
|
|
|
|
const fac = Math.pow(2, n)
|
2023-06-20 02:57:44 -05:00
|
|
|
if (num % fac === 0) {
|
|
|
|
denom = 32 / fac
|
|
|
|
num = num / fac
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<span className="group w-[3.125%] relative" key={i}>
|
|
|
|
<button
|
|
|
|
className={`${heightClasses[denom]} ${fractionClasses}`}
|
2023-06-21 12:10:22 +02:00
|
|
|
title={`${num}/${denom}″`}
|
2023-06-20 02:57:44 -05:00
|
|
|
onClick={() => fraction(num, denom)}
|
|
|
|
/>
|
|
|
|
<span className="group-hover:visible invisible text-xs text-center absolute left-0 -bottom-6">{`${num}/${denom}"`}</span>
|
|
|
|
</span>
|
|
|
|
)
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
2023-06-21 12:10:22 +02:00
|
|
|
|
2023-06-20 02:57:44 -05:00
|
|
|
export const MeasieRow = (props) => {
|
|
|
|
const { t, m, mset } = props
|
|
|
|
|
2023-06-21 12:10:22 +02:00
|
|
|
const isSet = typeof mset.measies?.[m] !== 'undefined'
|
2023-06-20 02:57:44 -05:00
|
|
|
|
|
|
|
return (
|
|
|
|
<Collapse
|
|
|
|
color="secondary"
|
|
|
|
openTitle={t(m)}
|
|
|
|
title={
|
|
|
|
<>
|
|
|
|
<div className="grow text-left md:text-right block md:inline font-bold pr-4">{t(m)}</div>
|
|
|
|
{isSet ? (
|
|
|
|
<Mval m={m} val={mset.measies[m]} imperial={mset.imperial} className="w-1/3" />
|
|
|
|
) : (
|
|
|
|
<div className="w-1/3" />
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
}
|
|
|
|
toggle={isSet ? <EditIcon /> : <PlusIcon />}
|
|
|
|
toggleClasses={`btn ${isSet ? 'btn-secondary' : 'btn-neutral bg-opacity-50'}`}
|
|
|
|
>
|
|
|
|
<MeasieInput {...props} />
|
|
|
|
</Collapse>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export const MeasieInput = ({
|
|
|
|
t,
|
|
|
|
m,
|
|
|
|
mset,
|
|
|
|
backend,
|
|
|
|
refresh,
|
|
|
|
toast,
|
|
|
|
children,
|
|
|
|
onUpdate,
|
2023-06-21 12:10:22 +02:00
|
|
|
startLoading = () => null,
|
|
|
|
stopLoading = () => null,
|
2023-06-20 02:57:44 -05:00
|
|
|
}) => {
|
|
|
|
const isDegree = isDegreeMeasurement(m)
|
2023-07-26 16:38:51 -06:00
|
|
|
const units = mset.imperial ? 'imperial' : 'metric'
|
|
|
|
const [val, setVal] = useState(() => {
|
|
|
|
const measie = mset.measies?.[m]
|
|
|
|
if (!measie) return ''
|
|
|
|
if (isDegree) return measie
|
|
|
|
return measurementAsUnits(measie, units)
|
|
|
|
})
|
2023-06-20 02:57:44 -05:00
|
|
|
|
2023-07-26 16:38:51 -06:00
|
|
|
const [valid, setValid] = useState(null)
|
2023-06-20 02:57:44 -05:00
|
|
|
|
|
|
|
// Update onChange
|
2023-07-26 16:38:51 -06:00
|
|
|
const update = useCallback(
|
|
|
|
(validVal, rawVal) => {
|
|
|
|
setValid(validVal)
|
|
|
|
setVal(validVal || rawVal)
|
|
|
|
|
|
|
|
if (validVal && typeof onUpdate === 'function') {
|
|
|
|
const useVal = isDegree ? validVal : measurementAsMm(validVal, units)
|
|
|
|
onUpdate(m, useVal)
|
|
|
|
}
|
|
|
|
},
|
2023-07-26 20:21:18 -06:00
|
|
|
[isDegree, setValid, setVal, onUpdate, units, m]
|
2023-07-26 16:38:51 -06:00
|
|
|
)
|
2023-06-20 02:57:44 -05:00
|
|
|
|
|
|
|
const save = async () => {
|
|
|
|
// FIXME
|
|
|
|
startLoading()
|
|
|
|
const measies = {}
|
2023-07-26 16:38:51 -06:00
|
|
|
measies[m] = isDegree ? val : measurementAsMm(val, units)
|
2023-06-20 02:57:44 -05:00
|
|
|
const result = await backend.updateSet(mset.id, { measies })
|
|
|
|
if (result.success) {
|
|
|
|
refresh()
|
|
|
|
toast.for.settingsSaved()
|
|
|
|
} else toast.for.backendError()
|
|
|
|
stopLoading()
|
|
|
|
}
|
|
|
|
|
2023-07-26 20:21:18 -06:00
|
|
|
const fraction = (i, base) => update(Math.floor(('' + val).split(/[\s.]/)[0]) + i / base)
|
2023-06-20 02:57:44 -05:00
|
|
|
|
|
|
|
if (!m) return null
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="form-control mb-2 ">
|
|
|
|
<div className="flex items-center gap-4 flex-wrap mx-auto">
|
|
|
|
<label className="shrink-0 grow max-w-full">
|
|
|
|
{children}
|
|
|
|
<span className="input-group">
|
2023-07-26 16:38:51 -06:00
|
|
|
<NumberInput
|
|
|
|
className={`border-r-0 w-full`}
|
2023-06-20 02:57:44 -05:00
|
|
|
value={val}
|
2023-07-26 16:38:51 -06:00
|
|
|
onUpdate={update}
|
|
|
|
onMount={setValid}
|
2023-06-20 02:57:44 -05:00
|
|
|
/>
|
2023-07-26 16:38:51 -06:00
|
|
|
<span
|
|
|
|
className={`bg-transparent border-y w-20
|
|
|
|
${valid === false && 'border-error text-neutral-content'}
|
|
|
|
${valid && 'border-success text-neutral-content'}
|
|
|
|
${valid === null && 'border-base-200 text-base-content'}
|
|
|
|
`}
|
|
|
|
>
|
|
|
|
<Mval
|
|
|
|
imperial={mset.imperial}
|
|
|
|
val={isDegree ? val : measurementAsMm(val, units)}
|
|
|
|
m={m}
|
|
|
|
className="text-base-content bg-transparent text-success text-xs font-bold p-0"
|
|
|
|
/>
|
|
|
|
</span>
|
2023-06-20 02:57:44 -05:00
|
|
|
<span
|
|
|
|
role="img"
|
|
|
|
className={`bg-transparent border-y
|
|
|
|
${valid === false && 'border-error text-neutral-content'}
|
2023-07-26 16:38:51 -06:00
|
|
|
${valid && 'border-success text-neutral-content'}
|
2023-06-20 02:57:44 -05:00
|
|
|
${valid === null && 'border-base-200 text-base-content'}
|
|
|
|
`}
|
|
|
|
>
|
2023-07-26 16:38:51 -06:00
|
|
|
{valid && '👍'}
|
2023-06-20 02:57:44 -05:00
|
|
|
{valid === false && '🤔'}
|
|
|
|
</span>
|
|
|
|
<span
|
|
|
|
className={`w-14 text-center
|
|
|
|
${valid === false && 'bg-error text-neutral-content'}
|
2023-07-26 16:38:51 -06:00
|
|
|
${valid && 'bg-success text-neutral-content'}
|
2023-06-20 02:57:44 -05:00
|
|
|
${valid === null && 'bg-base-200 text-base-content'}
|
|
|
|
`}
|
|
|
|
>
|
|
|
|
{isDegree ? '°' : mset.imperial ? 'in' : 'cm'}
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
</label>
|
|
|
|
{mset.imperial && (
|
|
|
|
<div className="grow my-2 sm:min-w-[22rem]">
|
|
|
|
{!isDegree && <FractionButtons {...{ t, fraction }} />}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
{backend && (
|
|
|
|
<button className="btn btn-secondary w-24" onClick={save} disabled={!valid}>
|
|
|
|
{t('save')}
|
|
|
|
</button>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|