mirror of
https://github.com/nowcommunity/nowdatabase.git
synced 2026-10-12 19:13:52 +03:00
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:
@@ -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'}
|
||||
/>,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user