Merge pull request #389 from nowcommunity/time-unit-bounds-to-age-fields

Calculate and insert max/min age from time unit + fraction into Age field
This commit is contained in:
Eero Antikainen
2024-11-01 14:40:24 +02:00
committed by GitHub
5 changed files with 132 additions and 7 deletions
+2 -2
View File
@@ -12,7 +12,7 @@ export const calculateLocalityMaxAge = (upperBound: number, lowerBound: number,
throw new Error('Denominator cannot be 0')
}
return upperBound + ((lowerBound - upperBound) * numerator) / denominator
return lowerBound - ((lowerBound - upperBound) * (numerator - 1)) / denominator
}
export const calculateLocalityMinAge = (upperBound: number, lowerBound: number, frac: string | null) => {
@@ -29,5 +29,5 @@ export const calculateLocalityMinAge = (upperBound: number, lowerBound: number,
throw new Error('Denominator cannot be 0')
}
return lowerBound - ((lowerBound - upperBound) * (numerator - 1)) / denominator
return lowerBound - ((lowerBound - upperBound) * numerator) / denominator
}
@@ -33,6 +33,7 @@ export type TextFieldOptions = (
) & {
disabled?: boolean
big?: boolean
readonly?: boolean
changeSetter?: React.Dispatch<React.SetStateAction<number>>
}
@@ -19,6 +19,7 @@ import { useDetailContext } from '../Context/DetailContext'
import { DataValue } from './tabLayoutHelpers'
import { modalStyle } from './misc'
import { EditDataType } from '@/backendTypes'
import { calculateLocalityMinAge, calculateLocalityMaxAge } from '@/util/ageCalculator'
const fieldWidth = '14em'
@@ -189,6 +190,7 @@ export const EditableTextField = <T extends object>({
round,
big = false,
disabled = false,
readonly = false,
changeSetter,
}: {
field: keyof EditDataType<T>
@@ -196,6 +198,7 @@ export const EditableTextField = <T extends object>({
round?: number
big?: boolean
disabled?: boolean
readonly?: boolean
changeSetter?: React.Dispatch<React.SetStateAction<number>>
}) => {
const { setEditData, editData, validator } = useDetailContext<T>()
@@ -225,6 +228,7 @@ export const EditableTextField = <T extends object>({
type={type}
multiline={big}
disabled={disabled}
InputProps={readonly ? { readOnly: true } : { readOnly: false }}
/>
)
@@ -344,3 +348,84 @@ export const TimeBoundSelection = <T extends object, ParentType extends object>(
)
return <DataValue<ParentType> field={targetField as keyof EditDataType<ParentType>} EditElement={editingComponent} />
}
export const BasisForAgeSelection = <T extends object, ParentType extends object>({
targetField,
sourceField,
lowBoundField,
upBoundField,
fraction,
selectorTable,
disabled,
}: {
targetField: keyof ParentType
sourceField: keyof T
selectorTable: ReactElement
lowBoundField: keyof T
upBoundField: keyof T
fraction: string | null | undefined
disabled?: boolean
}) => {
const { editData, setEditData, validator } = useDetailContext<ParentType>()
const { error } = validator(editData, targetField as keyof EditDataType<ParentType>)
const [open, setOpen] = useState(false)
const selectorFn = (selected: T) => {
if (targetField === 'bfa_min') {
setEditData({
...editData,
min_age: calculateLocalityMinAge(
Number(selected[upBoundField]),
Number(selected[lowBoundField]),
String(fraction).split(' ')[2]
),
[targetField]: selected[sourceField],
})
} else if (targetField === 'bfa_max') {
setEditData({
...editData,
max_age: calculateLocalityMaxAge(
Number(selected[upBoundField]),
Number(selected[lowBoundField]),
String(fraction).split(' ')[2]
),
[targetField]: selected[sourceField],
})
} else {
setEditData({ ...editData, [targetField]: selected[sourceField] })
}
setOpen(false)
}
const selectorTableWithFn = cloneElement(selectorTable, { selectorFn })
if (open)
return (
<Box>
<Modal
open={open}
aria-labelledby={`modal-${targetField as string}`}
aria-describedby={`modal-${targetField as string}`}
>
<Box sx={{ ...modalStyle }}>
<Box marginBottom="2em" marginTop="1em">
{selectorTableWithFn}
</Box>
<Button onClick={() => setOpen(false)}>Cancel</Button>
</Box>
</Modal>
</Box>
)
const editingComponent = (
<TextField
variant="outlined"
size="small"
error={!!error}
helperText={error ?? ''}
value={editData[targetField as keyof EditDataType<ParentType>]}
onClick={() => setOpen(true)}
disabled={disabled}
sx={{ backgroundColor: disabled ? 'grey' : '' }}
inputProps={{ readOnly: true }}
/>
)
return <DataValue<ParentType> field={targetField as keyof EditDataType<ParentType>} EditElement={editingComponent} />
}
@@ -1,5 +1,5 @@
import { LocalityDetailsType, TimeUnitDetailsType } from '@/backendTypes'
import { FieldWithTableSelection } from '@/components/DetailView/common/editingComponents'
import { BasisForAgeSelection } from '@/components/DetailView/common/editingComponents'
import { emptyOption } from '@/components/DetailView/common/misc'
import { ArrayFrame, HalfFrames } from '@/components/DetailView/common/tabLayoutHelpers'
import { useDetailContext } from '@/components/DetailView/Context/DetailContext'
@@ -55,12 +55,15 @@ export const AgeTab = () => {
['', 'Age (Ma)', 'Basis for age (Absolute)', 'Basis for age (Time Unit)', 'Basis for age (Fraction)'],
[
'Minimum age',
textField('min_age', { type: 'number', round: 3 }),
textField('min_age', { type: 'number', round: 3, readonly: editData.date_meth === 'time_unit' }),
dropdown('bfa_min_abs', bfa_abs_options, 'Min Basis for age (absolute)', editData.date_meth === 'time_unit'),
<FieldWithTableSelection<TimeUnitDetailsType, LocalityDetailsType>
<BasisForAgeSelection<TimeUnitDetailsType, LocalityDetailsType>
key="bfa_min"
sourceField="tu_name"
targetField="bfa_min"
lowBoundField="low_bound"
upBoundField="up_bound"
fraction={editData.frac_min}
selectorTable={<TimeUnitTable />}
disabled={editData.date_meth === 'absolute'}
/>,
@@ -68,12 +71,15 @@ export const AgeTab = () => {
],
[
'Maximum age',
textField('max_age', { type: 'number', round: 3 }),
textField('max_age', { type: 'number', round: 3, readonly: editData.date_meth === 'time_unit' }),
dropdown('bfa_max_abs', bfa_abs_options, 'Max Basis for age (absolute)', editData.date_meth === 'time_unit'),
<FieldWithTableSelection<TimeUnitDetailsType, LocalityDetailsType>
<BasisForAgeSelection<TimeUnitDetailsType, LocalityDetailsType>
key="bfa_max"
sourceField="tu_name"
targetField="bfa_max"
lowBoundField="low_bound"
upBoundField="up_bound"
fraction={editData.frac_max}
selectorTable={<TimeUnitTable />}
disabled={editData.date_meth === 'absolute'}
/>,
+33
View File
@@ -0,0 +1,33 @@
export const calculateLocalityMaxAge = (upperBound: number, lowerBound: number, frac: string | null) => {
if (!frac) {
return lowerBound
}
const parts = frac.split(':')
const numerator = parseInt(parts[0])
const denominator = parseInt(parts[1])
if (denominator === 0) {
// This shouldn't be possible
throw new Error('Denominator cannot be 0')
}
return lowerBound - ((lowerBound - upperBound) * (numerator - 1)) / denominator
}
export const calculateLocalityMinAge = (upperBound: number, lowerBound: number, frac: string | null) => {
if (!frac) {
return upperBound
}
const parts = frac.split(':')
const numerator = parseInt(parts[0])
const denominator = parseInt(parts[1])
if (denominator === 0) {
// This shouldn't be possible
throw new Error('Denominator cannot be 0')
}
return lowerBound - ((lowerBound - upperBound) * numerator) / denominator
}