diff --git a/src/components/fields/AxisInterval.js b/src/components/fields/AxisInterval.js index cd460aa6a..07040952a 100644 --- a/src/components/fields/AxisInterval.js +++ b/src/components/fields/AxisInterval.js @@ -13,6 +13,10 @@ const MILLISECONDS_IN_DAY = MILLISECONDS_IN_MINUTE * 60 * 24; // eslint-disable- const DAYS_IN_MONTH = 30; const MONTHS_IN_YEAR = 12; //eslint-disable-line +function twoDecimalsRound(value) { + return Math.round(value * 100) / 100; +} + function getSmallestUnit(milliseconds) { const units = { seconds: MILLISECONDS_IN_SECOND, @@ -36,7 +40,7 @@ function getSmallestUnit(milliseconds) { return smallestUnit; } -class UnconnectedAxisInterval extends Component { +export class UnconnectedAxisInterval extends Component { constructor(props) { super(props); @@ -54,13 +58,22 @@ class UnconnectedAxisInterval extends Component { update(value) { let adjustedValue = value < 0 ? 0 : value; + const isValueInteger = adjustedValue % 1 === 0; if (this.state.units === 'years') { - adjustedValue = 'M' + adjustedValue * MONTHS_IN_YEAR; + if (isValueInteger) { + adjustedValue = 'M' + adjustedValue * MONTHS_IN_YEAR; + } else { + adjustedValue = adjustedValue * MONTHS_IN_YEAR * DAYS_IN_MONTH * MILLISECONDS_IN_DAY; + } } if (this.state.units === 'months') { - adjustedValue = 'M' + adjustedValue; + if (isValueInteger) { + adjustedValue = 'M' + adjustedValue; + } else { + adjustedValue = adjustedValue * DAYS_IN_MONTH * MILLISECONDS_IN_DAY; + } } if (this.state.units === 'days') { @@ -89,7 +102,12 @@ class UnconnectedAxisInterval extends Component { this.setState({units: value}); if (['years', 'months'].includes(value)) { - this.props.updatePlot('M' + Math.round(milliseconds / MILLISECONDS_IN_DAY / DAYS_IN_MONTH)); + const nbMonths = milliseconds / MILLISECONDS_IN_DAY / DAYS_IN_MONTH; + if (nbMonths % 1 === 0) { + this.props.updatePlot('M' + nbMonths); + } else { + this.props.updatePlot(milliseconds); + } } else { this.props.updatePlot(milliseconds); } @@ -100,19 +118,25 @@ class UnconnectedAxisInterval extends Component { typeof value === 'string' && value[0] === 'M' ? parseInt(value.substring(1), 10) : value; if (this.state.units === 'years') { - return numericValue / MONTHS_IN_YEAR; + if (typeof value === 'string') { + return twoDecimalsRound(numericValue / MONTHS_IN_YEAR); + } + return twoDecimalsRound(numericValue / MILLISECONDS_IN_DAY / DAYS_IN_MONTH / MONTHS_IN_YEAR); } if (this.state.units === 'months') { - return numericValue; + if (typeof value === 'string') { + return twoDecimalsRound(numericValue); + } + return twoDecimalsRound(numericValue / MILLISECONDS_IN_DAY / DAYS_IN_MONTH); } if (this.state.units === 'days') { - return Math.round(numericValue / MILLISECONDS_IN_DAY); + return twoDecimalsRound(numericValue / MILLISECONDS_IN_DAY); } if (this.state.units === 'minutes') { - return Math.round(numericValue / MILLISECONDS_IN_MINUTE); + return twoDecimalsRound(numericValue / MILLISECONDS_IN_MINUTE); } if (this.state.units === 'seconds') { - return Math.round(numericValue / MILLISECONDS_IN_SECOND); + return twoDecimalsRound(numericValue / MILLISECONDS_IN_SECOND); } if (this.state.units === 'milliseconds') { return numericValue; @@ -126,8 +150,12 @@ class UnconnectedAxisInterval extends Component { const binStartValue = this.props.fullContainer[attrHead].start; const BinStartIsDate = typeof binStartValue === 'string' && (isDateTime(binStartValue) || isJSDate(binStartValue)); + const tick0 = + this.props.fullContainer.tick0 && + (this.props.fullContainer.tick0 || this.props.fullContainer.colorbar.tick0); + const tick0IsDate = tick0 && (isDateTime(tick0) || isJSDate(tick0)); - return BinStartIsDate ? ( + return BinStartIsDate || tick0IsDate ? ( this.onUnitChange(value)} value={this.state.units} /> -
+
this.update(value)} diff --git a/src/components/fields/derived.js b/src/components/fields/derived.js index bac55cb9a..451083a37 100644 --- a/src/components/fields/derived.js +++ b/src/components/fields/derived.js @@ -6,6 +6,7 @@ import {UnconnectedNumeric} from './Numeric'; import {UnconnectedNumericOrDate} from './NumericOrDate'; import {UnconnectedAxisRangeValue} from './AxisRangeValue'; import {UnconnectedRadio} from './Radio'; +import {UnconnectedAxisInterval} from './AxisInterval'; import Info from './Info'; import {UnconnectedColorPicker} from './ColorPicker'; import {UnconnectedTextEditor} from './TextEditor'; @@ -226,6 +227,24 @@ export const DTicks = connectToContainer(UnconnectedAxisRangeValue, { }, }); +export const DTicksInterval = connectToContainer(UnconnectedAxisInterval, { + modifyPlotProps: (props, context, plotProps) => { + const {fullContainer} = plotProps; + if ( + fullContainer && + fullContainer._name && + (fullContainer._name.startsWith('lat') || fullContainer._name.startsWith('lon')) + ) { + // don't mess with visibility on geo axes + return plotProps; + } + if (plotProps.isVisible && fullContainer && fullContainer.tickmode !== 'linear') { + plotProps.isVisible = false; + } + return plotProps; + }, +}); + class UnconnectedNumericFraction extends UnconnectedNumeric {} UnconnectedNumericFraction.propTypes = UnconnectedNumeric.propTypes; UnconnectedNumericFraction.defaultProps = { diff --git a/src/components/fields/index.js b/src/components/fields/index.js index cfae37e96..6322e6b2b 100644 --- a/src/components/fields/index.js +++ b/src/components/fields/index.js @@ -40,6 +40,7 @@ import { AxesRange, NTicks, DTicks, + DTicksInterval, AxisAnchorDropdown, ContourNumeric, FillDropdown, @@ -72,6 +73,7 @@ export { AxesRange, NTicks, DTicks, + DTicksInterval, AxesSelector, ColorPicker, ColorscalePicker, diff --git a/src/components/index.js b/src/components/index.js index 7109c5704..824c87f71 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -9,6 +9,7 @@ import { AxesRange, NTicks, DTicks, + DTicksInterval, AxesSelector, ColorPicker, ColorscalePicker, @@ -103,6 +104,7 @@ export { AxisSide, NTicks, DTicks, + DTicksInterval, ShapeAccordion, RangeSelectorAccordion, SliderAccordion, diff --git a/src/default_panels/StyleAxesPanel.js b/src/default_panels/StyleAxesPanel.js index fc45380a4..9e13ce75d 100644 --- a/src/default_panels/StyleAxesPanel.js +++ b/src/default_panels/StyleAxesPanel.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { AxesRange, DTicks, + DTicksInterval, NTicks, ColorPicker, Dropdown, @@ -121,7 +122,7 @@ class StyleAxesPanel extends Component { /> - + @@ -274,7 +275,7 @@ class StyleAxesPanel extends Component { /> - + @@ -310,7 +311,7 @@ class StyleAxesPanel extends Component { /> - + diff --git a/src/default_panels/StyleColorbarsPanel.js b/src/default_panels/StyleColorbarsPanel.js index 5d9b97881..3f2c79214 100644 --- a/src/default_panels/StyleColorbarsPanel.js +++ b/src/default_panels/StyleColorbarsPanel.js @@ -15,6 +15,7 @@ import { ColorPicker, VisibilitySelect, NumericOrDate, + AxisInterval, } from '../components'; export const traceHasColorbar = (trace, fullTrace) => @@ -213,7 +214,7 @@ const StyleColorBarsPanel = (props, {localize: _}) => { /> - + @@ -241,7 +242,7 @@ const StyleColorBarsPanel = (props, {localize: _}) => { /> - + diff --git a/src/index.js b/src/index.js index f53cee550..7d6856974 100644 --- a/src/index.js +++ b/src/index.js @@ -35,6 +35,7 @@ import { TransformAccordion, NTicks, DTicks, + DTicksInterval, AxesSelector, PanelMessage, Button, @@ -107,6 +108,7 @@ export { AxesRange, NTicks, DTicks, + DTicksInterval, AxesSelector, Button, ColorPicker,