SliderControl (which drives the auto-inspector's ~144 fields and most custom panels) previously ignored the viewer unit preference and always rendered raw meters with a static "m" label, while MetricControl converted to feet — so toggling imperial changed some length inputs and not others. Extract the conversion into a shared `useLinearDisplay(unit, precision)` hook and route BOTH controls through it, so every `unit="m"` length field displays and edits in feet when imperial and meters when metric — consistently. Values are always stored in meters; only display, the text field, and drag/ wheel/arrow deltas move to the display unit. For metric and non-length units (°, %, in, …) the conversions are the identity, so those paths are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
41 lines
1.6 KiB
TypeScript
41 lines
1.6 KiB
TypeScript
'use client'
|
|
|
|
import { useViewer } from '@pascal-app/viewer'
|
|
import { useCallback } from 'react'
|
|
import { getLinearUnitLabel, linearUnitToMeters, metersToLinearUnit } from './measurements'
|
|
|
|
/**
|
|
* Shared display/storage conversion for numeric property controls so that
|
|
* every length input honors the metric/imperial toggle identically.
|
|
*
|
|
* Values are always STORED in the field's own unit (meters for `unit === 'm'`).
|
|
* When the viewer preference is imperial AND the field is a meter length, the
|
|
* value is DISPLAYED (and edited) in feet; otherwise the conversions are the
|
|
* identity, so metric fields and non-length units (`'°'`, `'%'`, `'in'`, `''`,
|
|
* …) behave exactly as before.
|
|
*
|
|
* Used by both `SliderControl` and `MetricControl` — keep the two in sync via
|
|
* this single source of truth.
|
|
*/
|
|
export function useLinearDisplay(unit: string, precision: number) {
|
|
const viewerUnit = useViewer((state) => state.unit)
|
|
const isImperial = viewerUnit === 'imperial' && unit === 'm'
|
|
const displayUnit = isImperial ? getLinearUnitLabel('imperial') : unit
|
|
|
|
const toDisplay = useCallback(
|
|
(stored: number) => (isImperial ? metersToLinearUnit(stored, 'imperial') : stored),
|
|
[isImperial],
|
|
)
|
|
const toStored = useCallback(
|
|
(display: number) => (isImperial ? linearUnitToMeters(display, 'imperial') : display),
|
|
[isImperial],
|
|
)
|
|
// Round a stored value so it lands on a clean number of DISPLAY-unit digits.
|
|
const roundStored = useCallback(
|
|
(stored: number) => toStored(Number.parseFloat(toDisplay(stored).toFixed(precision))),
|
|
[toDisplay, toStored, precision],
|
|
)
|
|
|
|
return { isImperial, displayUnit, toDisplay, toStored, roundStored }
|
|
}
|