-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add DistanceUnitFormatter and DurationUnitFormatter, two react compon…
…ents to automatically choose the best unit for a value They also allow clicking on the values to cycle between the different possible units. Example usage is shown in transit routing output for the walking mode. Fixes #775
- Loading branch information
1 parent
726c117
commit 90d99ee
Showing
7 changed files
with
191 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
packages/chaire-lib-frontend/src/components/pageParts/DistanceUnitFormatter.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
* Copyright 2023, Polytechnique Montreal and contributors | ||
* | ||
* This file is licensed under the MIT License. | ||
* License text available at https://opensource.org/licenses/MIT | ||
*/ | ||
import React, { useState, useEffect } from 'react'; | ||
import { withTranslation, WithTranslation } from 'react-i18next'; | ||
import { roundToDecimals } from 'chaire-lib-common/lib/utils/MathUtils'; | ||
import { metersToMiles, metersToFeet } from 'chaire-lib-common/lib/utils/PhysicsUtils'; | ||
|
||
const destinationUnitOptions = ['kilometers', 'meters', 'miles', 'feet'] as const; | ||
type destinationUnitOptionsType = typeof destinationUnitOptions[number]; | ||
|
||
export interface DistanceUnitFormatterProps extends WithTranslation { | ||
value: number; | ||
sourceUnit: 'kilometers' | 'meters'; | ||
destinationUnit?: destinationUnitOptionsType; | ||
} | ||
|
||
const DistanceUnitFormatter: React.FunctionComponent<DistanceUnitFormatterProps> = ( | ||
props: DistanceUnitFormatterProps | ||
) => { | ||
const [destinationUnit, setDestinationUnit] = useState<string | undefined>(props.destinationUnit); | ||
|
||
const valueInMeters = props.sourceUnit === 'meters' ? props.value : props.value / 1000; | ||
|
||
useEffect(() => { | ||
// This effect runs only after the initial render | ||
// If the destination unit was not specified, we choose the best one based on the magnitude of the value. | ||
if (destinationUnit === undefined) { | ||
if (valueInMeters < 1000) { | ||
setDestinationUnit('meters'); | ||
} else { | ||
setDestinationUnit('kilometers'); | ||
} | ||
} | ||
}, [valueInMeters]); | ||
|
||
const unitFormatters: Record<destinationUnitOptionsType, (value: number) => string> = { | ||
meters: (value) => `${roundToDecimals(value, 0)} ${props.t('main:meterAbbr')}`, | ||
kilometers: (value) => `${roundToDecimals(value / 1000, 2)} ${props.t('main:kilometerAbbr')}`, | ||
miles: (value) => `${roundToDecimals(metersToMiles(value), 2)} ${props.t('main:mileAbbr')}`, | ||
feet: (value) => `${roundToDecimals(metersToFeet(value), 0)} ${props.t('main:feetAbbr')}` | ||
}; | ||
|
||
const formattedValue = destinationUnit ? unitFormatters[destinationUnit](valueInMeters) : ''; | ||
|
||
const cycleThroughDestinationUnits = () => { | ||
// Infer the next unit based on the currently displayed unit. | ||
setDestinationUnit((prevUnit) => { | ||
return destinationUnitOptions[ | ||
(destinationUnitOptions.indexOf(prevUnit as destinationUnitOptionsType) + 1) % | ||
destinationUnitOptions.length | ||
]; | ||
}); | ||
}; | ||
|
||
return <span onClick={cycleThroughDestinationUnits}>{formattedValue}</span>; | ||
}; | ||
|
||
export default withTranslation([])(DistanceUnitFormatter); |
67 changes: 67 additions & 0 deletions
67
packages/chaire-lib-frontend/src/components/pageParts/DurationUnitFormatter.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* Copyright 2023, Polytechnique Montreal and contributors | ||
* | ||
* This file is licensed under the MIT License. | ||
* License text available at https://opensource.org/licenses/MIT | ||
*/ | ||
import React, { useState, useEffect } from 'react'; | ||
import { withTranslation, WithTranslation } from 'react-i18next'; | ||
import { toXXhrYYminZZsec } from 'chaire-lib-common/lib/utils/DateTimeUtils'; | ||
import { roundToDecimals } from 'chaire-lib-common/lib/utils/MathUtils'; | ||
|
||
const destinationUnitOptions = ['hrMinSec', 'seconds', 'minutes', 'hours'] as const; | ||
type destinationUnitOptionsType = typeof destinationUnitOptions[number]; | ||
|
||
export interface DurationUnitFormatterProps extends WithTranslation { | ||
value: number; | ||
sourceUnit: 'seconds' | 'minutes' | 'hours'; | ||
destinationUnit?: destinationUnitOptionsType; | ||
} | ||
|
||
const DurationUnitFormatter: React.FunctionComponent<DurationUnitFormatterProps> = ( | ||
props: DurationUnitFormatterProps | ||
) => { | ||
const [destinationUnit, setDestinationUnit] = useState<string | undefined>(props.destinationUnit); | ||
|
||
const valueInSeconds = | ||
props.sourceUnit === 'seconds' | ||
? props.value | ||
: props.sourceUnit === 'minutes' | ||
? props.value * 60 | ||
: props.sourceUnit === 'hours' | ||
? props.value * 60 * 60 | ||
: props.value; | ||
|
||
useEffect(() => { | ||
// This effect runs only after the initial render | ||
// If the destination unit was not specified, we choose a default. | ||
if (destinationUnit === undefined) { | ||
setDestinationUnit('hrMinSec'); | ||
} | ||
}, [valueInSeconds]); | ||
|
||
const unitFormatters: Record<destinationUnitOptionsType, (value: number) => string> = { | ||
hrMinSec: (value) => | ||
toXXhrYYminZZsec(value, props.t('main:hourAbbr'), props.t('main:minuteAbbr'), props.t('main:secondAbbr')) || | ||
`${value.toString()} ${props.t('main:secondAbbr')}`, | ||
seconds: (value) => `${roundToDecimals(value, 2)} ${props.t('main:secondAbbr')}`, | ||
minutes: (value) => `${roundToDecimals(value / 60, 2)} ${props.t('main:minuteAbbr')}`, | ||
hours: (value) => `${roundToDecimals(value / 3600, 2)} ${props.t('main:hourAbbr')}` | ||
}; | ||
|
||
const formattedValue = destinationUnit ? unitFormatters[destinationUnit](valueInSeconds) : ''; | ||
|
||
const cycleThroughDestinationUnits = () => { | ||
// Infer the next unit based on the currently displayed unit. | ||
setDestinationUnit((prevUnit) => { | ||
return destinationUnitOptions[ | ||
(destinationUnitOptions.indexOf(prevUnit as destinationUnitOptionsType) + 1) % | ||
destinationUnitOptions.length | ||
]; | ||
}); | ||
}; | ||
|
||
return <span onClick={cycleThroughDestinationUnits}>{formattedValue}</span>; | ||
}; | ||
|
||
export default withTranslation([])(DurationUnitFormatter); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters