From 4839c8b4f175366bb8e7ca0cffec02c3427bdf2a Mon Sep 17 00:00:00 2001 From: "Dae Kun (DK) Kwon" Date: Wed, 15 May 2024 08:18:52 -0400 Subject: [PATCH 1/9] test date input for time slider and adjust input element --- .../plotControls/AxisRangeControl.tsx | 5 +++ .../components/plotControls/TimeSlider.tsx | 8 ++-- .../widgets/NumberAndDateInputs.tsx | 6 ++- .../widgets/NumberAndDateRangeInputs.tsx | 11 ++++- .../plotControls/TimeSlider.stories.tsx | 44 ++++++++++++++++--- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/packages/libs/components/src/components/plotControls/AxisRangeControl.tsx b/packages/libs/components/src/components/plotControls/AxisRangeControl.tsx index ccffa18c62..0853cdfae3 100755 --- a/packages/libs/components/src/components/plotControls/AxisRangeControl.tsx +++ b/packages/libs/components/src/components/plotControls/AxisRangeControl.tsx @@ -24,6 +24,8 @@ export interface AxisRangeControlProps logScale?: boolean; /** specify step for increment/decrement buttons in MUI number inputs; MUI's default is 1 */ step?: number; + /** specify the height of the input element */ + inputHeight?: number; } export default function AxisRangeControl({ @@ -36,6 +38,7 @@ export default function AxisRangeControl({ disabled = false, logScale = false, step = undefined, + inputHeight, }: AxisRangeControlProps) { const validator = useCallback( ( @@ -79,6 +82,7 @@ export default function AxisRangeControl({ validator={validator} // add disabled prop to disable input fields disabled={disabled} + inputHeight={inputHeight} /> ) : ( ) ) : null; diff --git a/packages/libs/components/src/components/plotControls/TimeSlider.tsx b/packages/libs/components/src/components/plotControls/TimeSlider.tsx index 4931760d91..22e0899b7a 100755 --- a/packages/libs/components/src/components/plotControls/TimeSlider.tsx +++ b/packages/libs/components/src/components/plotControls/TimeSlider.tsx @@ -176,10 +176,10 @@ function TimeSlider(props: TimeSliderProps) { ); // `brushKey` makes/fakes the brush as a controlled component, - const brushKey = 'not_fake_controlled'; - // selectedRange != null - // ? selectedRange.start + ':' + selectedRange.end - // : 'no_brush'; + const brushKey = + selectedRange != null + ? selectedRange.start + ':' + selectedRange.end + : 'no_brush'; return (
= { disabled?: boolean; /** Style the Text Field with the warning color and bold stroke */ applyWarningStyles?: boolean; + /** specify the height of the input element */ + inputHeight?: number; }; export type NumberInputProps = BaseProps & { step?: number }; @@ -86,6 +88,8 @@ function BaseInput({ displayRangeViolationWarnings = true, disabled = false, applyWarningStyles = false, + // default value is 36.5 + inputHeight = 36.5, ...props }: BaseInputProps) { if (validator && (required || minValue != null || maxValue != null)) @@ -102,7 +106,7 @@ function BaseInput({ const classes = makeStyles({ root: { - height: 36.5, // default height is 56 and is waaaay too tall + height: inputHeight, // default height is 56 and is waaaay too tall // 34.5 is the height of the reset button, but 36.5 lines up better // set width for date width: valueType === 'date' ? 165 : '', diff --git a/packages/libs/components/src/components/widgets/NumberAndDateRangeInputs.tsx b/packages/libs/components/src/components/widgets/NumberAndDateRangeInputs.tsx index cdc625cb07..d1d6159b0e 100755 --- a/packages/libs/components/src/components/widgets/NumberAndDateRangeInputs.tsx +++ b/packages/libs/components/src/components/widgets/NumberAndDateRangeInputs.tsx @@ -43,6 +43,8 @@ export type BaseProps = { clearButtonLabel?: string; /** add disabled prop to disable input fields */ disabled?: boolean; + /** specify the height of the input element */ + inputHeight?: number; }; export type NumberRangeInputProps = BaseProps & { step?: number }; @@ -86,6 +88,7 @@ function BaseInput({ clearButtonLabel = 'Clear', // add disabled prop to disable input fields disabled = false, + inputHeight, ...props }: BaseInputProps) { if (validator && required) @@ -175,7 +178,9 @@ function BaseInput({ {label} )} -
+
{valueType === 'number' ? ( ) : ( )}
@@ -241,6 +248,7 @@ function BaseInput({ // add disabled prop to disable input fields disabled={disabled} step={step} + inputHeight={inputHeight} /> ) : ( )} {showClearButton && ( diff --git a/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx b/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx index eaf144135b..a9d5d7c80b 100755 --- a/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx +++ b/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useCallback } from 'react'; import { Story, Meta } from '@storybook/react/types-6-0'; import { LinePlotProps } from '../../plots/LinePlot'; import TimeSlider, { @@ -6,6 +6,9 @@ import TimeSlider, { } from '../../components/plotControls/TimeSlider'; import { DraggablePanel } from '@veupathdb/coreui/lib/components/containers'; +import AxisRangeControl from '../../components/plotControls/AxisRangeControl'; +import { NumberOrDateRange } from '../../types/general'; + export default { title: 'Plot Controls/TimeSlider', component: TimeSlider, @@ -303,6 +306,18 @@ export const TimeFilter: Story = (args: any) => { const defaultSymbolSize = 0.8; const defaultColor = '#333'; + // control selectedRange + const handleAxisRangeChange = useCallback( + (newRange?: NumberOrDateRange) => { + if (newRange) + setSelectedRange({ + start: newRange.min as string, + end: newRange.max as string, + }); + }, + [setSelectedRange] + ); + return ( = (args: any) => { paddingTop: '1em', }} > - {/* display start to end value */} -
- {selectedRange?.start} ~ {selectedRange?.end} -
+ {/* add axis range control */} +
Date: Tue, 21 May 2024 14:37:58 -0400 Subject: [PATCH 2/9] applying to SAM and adjust layout --- .../map/analysis/TimeSliderQuickFilter.tsx | 55 ++++++++++++++++--- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx index c4a46cb075..b3ffcf6e15 100755 --- a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx +++ b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { useMemo, useState, useCallback } from 'react'; import { ChevronRight, H6, Toggle } from '@veupathdb/coreui'; import TimeSlider, { TimeSliderDataProp, @@ -20,6 +20,9 @@ import { SiteInformationProps } from './Types'; import { mapSidePanelBackgroundColor } from '../constants'; import { useQuery } from '@tanstack/react-query'; +import AxisRangeControl from '@veupathdb/components/lib/components/plotControls/AxisRangeControl'; +import { NumberOrDateRange } from '@veupathdb/components/lib/types/general'; + interface Props { studyId: string; entities: StudyEntity[]; @@ -178,9 +181,6 @@ export default function TimeSliderQuickFilter({ }); } - // (easily) centering the variable picker requires two same-width divs either side - const sideElementStyle = { width: '70px' }; - const sliderHeight = minimized ? 50 : 75; const background = @@ -249,6 +249,20 @@ export default function TimeSliderQuickFilter({
); + // control selectedRange + const handleAxisRangeChange = useCallback( + (newRange?: NumberOrDateRange) => { + if (newRange) { + const newSelectedRange = { + start: newRange.min as string, + end: newRange.max as string, + }; + updateConfig({ ...config, selectedRange: newSelectedRange }); + } + }, + [updateConfig] + ); + // if no variable in a study is suitable to time slider, do not show time slider return variable != null && variableMetadata != null ? (
@@ -292,17 +306,16 @@ export default function TimeSliderQuickFilter({
+
{!minimized && ( <> -
-
+ {/* add axis range control */} + +
Date: Tue, 28 May 2024 22:29:16 -0400 Subject: [PATCH 3/9] add step buttons --- .../plotControls/TimeSlider.stories.tsx | 64 +++++++++++++-- .../map/analysis/TimeSliderQuickFilter.tsx | 79 ++++++++++++++++++- 2 files changed, 132 insertions(+), 11 deletions(-) diff --git a/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx b/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx index a9d5d7c80b..c761276040 100755 --- a/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx +++ b/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx @@ -318,6 +318,44 @@ export const TimeFilter: Story = (args: any) => { [setSelectedRange] ); + const handleArrowClick = useCallback( + (arrow: string) => { + // let's assume that selectedRange has the format of 'yyyy-mm-dd' + if ( + selectedRange && + selectedRange.start != null && + selectedRange.end != null + ) { + const selectedRangeArray = + arrow === 'left' + ? selectedRange.start.split('-') + : selectedRange.end.split('-'); + const addSubtractYear = + arrow === 'left' + ? String(Number(selectedRangeArray[0]) - 1) + : String(Number(selectedRangeArray[0]) + 1); + const changeYear = + addSubtractYear + + '-' + + selectedRangeArray[1] + + '-' + + selectedRangeArray[2]; + setSelectedRange((prev) => { + return arrow === 'left' + ? { + start: changeYear as string, + end: prev?.end as string, + } + : { + start: prev?.start as string, + end: changeYear as string, + }; + }); + } + }, + [selectedRange, setSelectedRange] + ); + return ( = (args: any) => { >
+
+ +
{/* add axis range control */} = (args: any) => { // set maxWidth containerStyles={{ maxWidth: '350px', - gridColumnStart: 2, - marginTop: -10, - paddingBottom: 5, }} // default height of the input element is 36.5 which may be too high for timeSlider // thus, introduced new prop to control it inputHeight={20} /> +
+ +
{ + // let's assume that selectedRange has the format of 'yyyy-mm-dd' + if ( + selectedRange && + selectedRange.start != null && + selectedRange.end != null + ) { + const selectedRangeArray = + arrow === 'left' + ? selectedRange.start.split('-') + : selectedRange.end.split('-'); + const addSubtractYear = + arrow === 'left' + ? String(Number(selectedRangeArray[0]) - step) + : String(Number(selectedRangeArray[0]) + step); + const newDate = + addSubtractYear + + '-' + + selectedRangeArray[1] + + '-' + + selectedRangeArray[2]; + const newSelectedRange = + arrow === 'left' + ? { + start: + extendedDisplayRange && extendedDisplayRange.start < newDate + ? (newDate as string) + : (extendedDisplayRange?.start as string), + end: selectedRange.end as string, + } + : { + start: selectedRange.start as string, + end: + extendedDisplayRange && extendedDisplayRange.end > newDate + ? (newDate as string) + : (extendedDisplayRange?.end as string), + }; + + updateConfig({ ...config, selectedRange: newSelectedRange }); + } + }, + [config, updateConfig, selectedRange] ); // if no variable in a study is suitable to time slider, do not show time slider @@ -336,6 +382,20 @@ export default function TimeSliderQuickFilter({ constraints={timeSliderVariableConstraints} />
+
+ +
{/* add axis range control */} +
+ +
Date: Tue, 4 Jun 2024 16:14:17 -0400 Subject: [PATCH 4/9] address feedbacks for step buttons --- .../components/plotControls/TimeSlider.tsx | 8 ++ .../map/analysis/TimeSliderQuickFilter.tsx | 134 ++++++++++++------ 2 files changed, 102 insertions(+), 40 deletions(-) diff --git a/packages/libs/components/src/components/plotControls/TimeSlider.tsx b/packages/libs/components/src/components/plotControls/TimeSlider.tsx index 22e0899b7a..37f8f473d3 100755 --- a/packages/libs/components/src/components/plotControls/TimeSlider.tsx +++ b/packages/libs/components/src/components/plotControls/TimeSlider.tsx @@ -44,6 +44,9 @@ export type TimeSliderProps = { debounceRateMs?: number; /** all user-interaction disabled */ disabled?: boolean; + /** for resetting disabled left and right arrow buttons when changing brush bounds */ + setDisableLeftArrow?: (value: boolean) => void; + setDisableRightArrow?: (value: boolean) => void; }; // using forwardRef @@ -63,6 +66,8 @@ function TimeSlider(props: TimeSliderProps) { debounceRateMs = 500, disabled = false, xAxisRange, + setDisableLeftArrow, + setDisableRightArrow, } = props; const resizeTriggerAreas: ResizeTriggerAreas[] = disabled @@ -120,6 +125,9 @@ function TimeSlider(props: TimeSliderProps) { : endDate : endDate, }); + // resetting disabled left and arrow buttons + if (setDisableLeftArrow) setDisableLeftArrow(false); + if (setDisableRightArrow) setDisableRightArrow(false); }, debounceRateMs), [setSelectedRange, xAxisRange] ); diff --git a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx index 4a337555c6..b8982e774e 100755 --- a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx +++ b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx @@ -35,6 +35,11 @@ interface Props { siteInformation: SiteInformationProps; } +interface selectedRangeProp { + start: string; + end: string; +} + export default function TimeSliderQuickFilter({ studyId, entities, @@ -249,6 +254,10 @@ export default function TimeSliderQuickFilter({
); + // disable arrow button + const [disableLeftArrow, setDisableLeftArrow] = useState(false); + const [disableRightArrow, setDisableRightArrow] = useState(false); + // control selectedRange const handleAxisRangeChange = useCallback( (newRange?: NumberOrDateRange) => { @@ -258,6 +267,9 @@ export default function TimeSliderQuickFilter({ end: newRange.max as string, }; updateConfig({ ...config, selectedRange: newSelectedRange }); + // resetting disabled left and arrow buttons + setDisableLeftArrow(false); + setDisableRightArrow(false); } }, [config, updateConfig] @@ -265,48 +277,24 @@ export default function TimeSliderQuickFilter({ // step buttons const handleArrowClick = useCallback( - (arrow: string, step: number = 1) => { - // let's assume that selectedRange has the format of 'yyyy-mm-dd' + (arrow: string) => { if ( + extendedDisplayRange && selectedRange && selectedRange.start != null && selectedRange.end != null ) { - const selectedRangeArray = - arrow === 'left' - ? selectedRange.start.split('-') - : selectedRange.end.split('-'); - const addSubtractYear = - arrow === 'left' - ? String(Number(selectedRangeArray[0]) - step) - : String(Number(selectedRangeArray[0]) + step); - const newDate = - addSubtractYear + - '-' + - selectedRangeArray[1] + - '-' + - selectedRangeArray[2]; - const newSelectedRange = - arrow === 'left' - ? { - start: - extendedDisplayRange && extendedDisplayRange.start < newDate - ? (newDate as string) - : (extendedDisplayRange?.start as string), - end: selectedRange.end as string, - } - : { - start: selectedRange.start as string, - end: - extendedDisplayRange && extendedDisplayRange.end > newDate - ? (newDate as string) - : (extendedDisplayRange?.end as string), - }; - + const newSelectedRange = newArrowRange( + extendedDisplayRange, + selectedRange, + arrow, + setDisableLeftArrow, + setDisableRightArrow + ); updateConfig({ ...config, selectedRange: newSelectedRange }); } }, - [config, updateConfig, selectedRange] + [config, updateConfig, extendedDisplayRange, selectedRange] ); // if no variable in a study is suitable to time slider, do not show time slider @@ -344,6 +332,9 @@ export default function TimeSliderQuickFilter({ axisColor={!active ? '#888' : '#000'} // disable user-interaction disabled={!active} + // for resetting disabled left and arrow buttons when changing brush bounds + setDisableLeftArrow={setDisableLeftArrow} + setDisableRightArrow={setDisableRightArrow} /> ) : ( @@ -384,15 +375,18 @@ export default function TimeSliderQuickFilter({
@@ -417,15 +411,18 @@ export default function TimeSliderQuickFilter({ />
@@ -468,3 +465,60 @@ export default function TimeSliderQuickFilter({
) : null; } + +function newArrowRange( + extendedDisplayRange: selectedRangeProp | undefined, + selectedRange: selectedRangeProp | undefined, + arrow: string, + setDisableLeftArrow: (value: boolean) => void, + setDisableRightArrow: (value: boolean) => void +) { + if (extendedDisplayRange && selectedRange) { + const diff = + new Date(selectedRange.end).getTime() - + new Date(selectedRange.start).getTime(); + if (arrow === 'right') { + const endDate = new Date(new Date(selectedRange.end).getTime() + diff) + .toISOString() + .split('T')[0]; + if ( + new Date(endDate).getTime() > + new Date(extendedDisplayRange.end).getTime() + ) { + setDisableRightArrow(true); + return selectedRange; + } else { + setDisableLeftArrow(false); + setDisableRightArrow(false); + return { + start: new Date(new Date(selectedRange.start).getTime() + diff) + .toISOString() + .split('T')[0], + end: endDate, + }; + } + } else { + const startDate = new Date(new Date(selectedRange.start).getTime() - diff) + .toISOString() + .split('T')[0]; + if ( + new Date(startDate).getTime() < + new Date(extendedDisplayRange.start).getTime() + ) { + setDisableLeftArrow(true); + return selectedRange; + } else { + setDisableLeftArrow(false); + setDisableRightArrow(false); + return { + start: startDate, + end: new Date(new Date(selectedRange.end).getTime() - diff) + .toISOString() + .split('T')[0], + }; + } + } + } else { + return undefined; + } +} From 9efbed82b5848bb844e048689adc041addf58045 Mon Sep 17 00:00:00 2001 From: "Dae Kun (DK) Kwon" Date: Wed, 24 Jul 2024 08:54:07 -0400 Subject: [PATCH 5/9] address feedbacks --- .../plotControls/TimeSlider.stories.tsx | 1 - .../map/analysis/TimeSliderQuickFilter.tsx | 191 ++++++++++++------ 2 files changed, 124 insertions(+), 68 deletions(-) diff --git a/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx b/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx index c761276040..3a230f73b6 100755 --- a/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx +++ b/packages/libs/components/src/stories/plotControls/TimeSlider.stories.tsx @@ -304,7 +304,6 @@ export const TimeFilter: Story = (args: any) => { // set constant values const defaultSymbolSize = 0.8; - const defaultColor = '#333'; // control selectedRange const handleAxisRangeChange = useCallback( diff --git a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx index b8982e774e..c189bb0b23 100755 --- a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx +++ b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState, useCallback } from 'react'; +import { useMemo, useState, useCallback, useEffect } from 'react'; import { ChevronRight, H6, Toggle } from '@veupathdb/coreui'; import TimeSlider, { TimeSliderDataProp, @@ -61,26 +61,29 @@ export default function TimeSliderQuickFilter({ const { siteName } = siteInformation; // extend the back end range request if our selectedRange is outside of it - const extendedDisplayRange = - variableMetadata && DateVariable.is(variableMetadata.variable) - ? selectedRange == null - ? { - start: variableMetadata.variable.distributionDefaults.rangeMin, - end: variableMetadata.variable.distributionDefaults.rangeMax, - } - : { - start: - variableMetadata.variable.distributionDefaults.rangeMin < - selectedRange.start - ? variableMetadata.variable.distributionDefaults.rangeMin - : selectedRange.start, - end: - variableMetadata.variable.distributionDefaults.rangeMax > - selectedRange.end - ? variableMetadata.variable.distributionDefaults.rangeMax - : selectedRange.end, - } - : undefined; + const extendedDisplayRange = useMemo( + () => + variableMetadata && DateVariable.is(variableMetadata.variable) + ? selectedRange == null + ? { + start: variableMetadata.variable.distributionDefaults.rangeMin, + end: variableMetadata.variable.distributionDefaults.rangeMax, + } + : { + start: + variableMetadata.variable.distributionDefaults.rangeMin < + selectedRange.start + ? variableMetadata.variable.distributionDefaults.rangeMin + : selectedRange.start, + end: + variableMetadata.variable.distributionDefaults.rangeMax > + selectedRange.end + ? variableMetadata.variable.distributionDefaults.rangeMax + : selectedRange.end, + } + : undefined, + [variableMetadata, selectedRange] + ); // converting old usePromise code to useQuery in an efficient manner const { enabled, queryKey, queryFn } = useMemo(() => { @@ -258,6 +261,9 @@ export default function TimeSliderQuickFilter({ const [disableLeftArrow, setDisableLeftArrow] = useState(false); const [disableRightArrow, setDisableRightArrow] = useState(false); + // year-based range + const [enableYearRange, setEnableYearRange] = useState(false); + // control selectedRange const handleAxisRangeChange = useCallback( (newRange?: NumberOrDateRange) => { @@ -288,8 +294,7 @@ export default function TimeSliderQuickFilter({ extendedDisplayRange, selectedRange, arrow, - setDisableLeftArrow, - setDisableRightArrow + enableYearRange ); updateConfig({ ...config, selectedRange: newSelectedRange }); } @@ -297,6 +302,47 @@ export default function TimeSliderQuickFilter({ [config, updateConfig, extendedDisplayRange, selectedRange] ); + // enabling/disabling date range arrows + useEffect(() => { + if (extendedDisplayRange && selectedRange) { + const diff = + new Date(selectedRange.end).getTime() - + new Date(selectedRange.start).getTime(); + + const expectedStartDate = new Date( + new Date(selectedRange.start).getTime() - diff + ) + .toISOString() + .split('T')[0]; + + const expectedEndDate = new Date( + new Date(selectedRange.end).getTime() + diff + ) + .toISOString() + .split('T')[0]; + + // left arrow + if ( + new Date(expectedStartDate).getTime() < + new Date(extendedDisplayRange.start).getTime() + ) { + setDisableLeftArrow(true); + } else { + setDisableLeftArrow(false); + } + + // right arrow + if ( + new Date(expectedEndDate).getTime() > + new Date(extendedDisplayRange.end).getTime() + ) { + setDisableRightArrow(true); + } else { + setDisableRightArrow(false); + } + } + }, [extendedDisplayRange, selectedRange]); + // if no variable in a study is suitable to time slider, do not show time slider return variable != null && variableMetadata != null ? (
@@ -429,8 +475,17 @@ export default function TimeSliderQuickFilter({
+ setEnableYearRange(toggle)} + /> +
void, - setDisableRightArrow: (value: boolean) => void + enableYearRange: boolean ) { if (extendedDisplayRange && selectedRange) { - const diff = - new Date(selectedRange.end).getTime() - - new Date(selectedRange.start).getTime(); - if (arrow === 'right') { - const endDate = new Date(new Date(selectedRange.end).getTime() + diff) - .toISOString() - .split('T')[0]; - if ( - new Date(endDate).getTime() > - new Date(extendedDisplayRange.end).getTime() - ) { - setDisableRightArrow(true); - return selectedRange; - } else { - setDisableLeftArrow(false); - setDisableRightArrow(false); - return { - start: new Date(new Date(selectedRange.start).getTime() + diff) - .toISOString() - .split('T')[0], - end: endDate, - }; - } + // if year range is enabled + if (enableYearRange) { + const selectedRangeStart = selectedRange.start.split('-'); + const selectedRangeEnd = selectedRange.end.split('-'); + const diff = Number(selectedRangeEnd[0]) - Number(selectedRangeStart[0]); + // if start and end have the same year, then yearDiff = 1 year + const yearDiff = diff === 0 ? 1 : diff; + + const startDate = + (arrow === 'right' + ? String(Number(selectedRangeStart[0]) + yearDiff) + : String(Number(selectedRangeStart[0]) - yearDiff)) + + '-' + + selectedRangeStart[1] + + '-' + + selectedRangeStart[2]; + const endDate = + (arrow === 'right' + ? String(Number(selectedRangeEnd[0]) + yearDiff) + : String(Number(selectedRangeEnd[0]) - yearDiff)) + + '-' + + selectedRangeEnd[1] + + '-' + + selectedRangeEnd[2]; + + return { + start: startDate, + end: endDate, + }; + + // relying on (end - start) dates only } else { - const startDate = new Date(new Date(selectedRange.start).getTime() - diff) - .toISOString() - .split('T')[0]; - if ( - new Date(startDate).getTime() < - new Date(extendedDisplayRange.start).getTime() - ) { - setDisableLeftArrow(true); - return selectedRange; - } else { - setDisableLeftArrow(false); - setDisableRightArrow(false); - return { - start: startDate, - end: new Date(new Date(selectedRange.end).getTime() - diff) - .toISOString() - .split('T')[0], - }; - } + const diff = + new Date(selectedRange.end).getTime() - + new Date(selectedRange.start).getTime(); + + const diffSign = arrow === 'right' ? diff : -diff; + + return { + start: new Date(new Date(selectedRange.start).getTime() + diffSign) + .toISOString() + .split('T')[0], + end: new Date(new Date(selectedRange.end).getTime() + diffSign) + .toISOString() + .split('T')[0], + }; } } else { return undefined; From f8801c50f92781fe834a0ef6bf7e607db102dfff Mon Sep 17 00:00:00 2001 From: "Dae Kun (DK) Kwon" Date: Tue, 30 Jul 2024 14:18:23 -0400 Subject: [PATCH 6/9] considering leap years --- .../map/analysis/TimeSliderQuickFilter.tsx | 130 +++++++++--------- 1 file changed, 62 insertions(+), 68 deletions(-) diff --git a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx index c189bb0b23..067e8ddfd3 100755 --- a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx +++ b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx @@ -261,9 +261,6 @@ export default function TimeSliderQuickFilter({ const [disableLeftArrow, setDisableLeftArrow] = useState(false); const [disableRightArrow, setDisableRightArrow] = useState(false); - // year-based range - const [enableYearRange, setEnableYearRange] = useState(false); - // control selectedRange const handleAxisRangeChange = useCallback( (newRange?: NumberOrDateRange) => { @@ -285,17 +282,11 @@ export default function TimeSliderQuickFilter({ const handleArrowClick = useCallback( (arrow: string) => { if ( - extendedDisplayRange && selectedRange && selectedRange.start != null && selectedRange.end != null ) { - const newSelectedRange = newArrowRange( - extendedDisplayRange, - selectedRange, - arrow, - enableYearRange - ); + const newSelectedRange = newArrowRange(selectedRange, arrow); updateConfig({ ...config, selectedRange: newSelectedRange }); } }, @@ -475,17 +466,8 @@ export default function TimeSliderQuickFilter({
- setEnableYearRange(toggle)} - /> -
= startDate && leapDay <= endDate) { + leapDayCount++; + } + } + } + + return leapDayCount; +} From d94500beb3616da16e42241f2e3f2cdbedebdb4f Mon Sep 17 00:00:00 2001 From: Bob Date: Wed, 31 Jul 2024 14:35:19 +0100 Subject: [PATCH 7/9] updated help tooltip --- .../libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx index 067e8ddfd3..5bced8c654 100755 --- a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx +++ b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx @@ -243,6 +243,8 @@ export default function TimeSliderQuickFilter({ change the date variable (currently{' '} {variableMetadata?.variable.displayName}) +
  • set start and end dates precisely
  • +
  • step the window forwards and backwards through the timeline
  • toggle the temporary time window filter on/off
  • From 16088c07ef00adc1dc3a1351832a38ae4f885990 Mon Sep 17 00:00:00 2001 From: "Dae Kun (DK) Kwon" Date: Tue, 6 Aug 2024 14:32:24 -0400 Subject: [PATCH 8/9] simplifying codes --- .../components/plotControls/TimeSlider.tsx | 88 +++++++++---------- .../map/analysis/TimeSliderQuickFilter.tsx | 20 ++--- 2 files changed, 49 insertions(+), 59 deletions(-) diff --git a/packages/libs/components/src/components/plotControls/TimeSlider.tsx b/packages/libs/components/src/components/plotControls/TimeSlider.tsx index 37f8f473d3..6312cdff73 100755 --- a/packages/libs/components/src/components/plotControls/TimeSlider.tsx +++ b/packages/libs/components/src/components/plotControls/TimeSlider.tsx @@ -44,9 +44,6 @@ export type TimeSliderProps = { debounceRateMs?: number; /** all user-interaction disabled */ disabled?: boolean; - /** for resetting disabled left and right arrow buttons when changing brush bounds */ - setDisableLeftArrow?: (value: boolean) => void; - setDisableRightArrow?: (value: boolean) => void; }; // using forwardRef @@ -66,8 +63,6 @@ function TimeSlider(props: TimeSliderProps) { debounceRateMs = 500, disabled = false, xAxisRange, - setDisableLeftArrow, - setDisableRightArrow, } = props; const resizeTriggerAreas: ResizeTriggerAreas[] = disabled @@ -96,49 +91,6 @@ function TimeSlider(props: TimeSliderProps) { const getXData = (d: TimeSliderDataProp) => new Date(d.x); const getYData = (d: TimeSliderDataProp) => d.y; - const onBrushChange = useMemo( - () => - debounce((domain: Bounds | null) => { - if (!domain) return; - const { x0, x1 } = domain; - - // computing the offset of 2 pixel (SAFE_PIXEL) in domain (milliseconds) - // https://github.com/airbnb/visx/blob/86a851cb3bf622b013b186f02f955bcd6548a87f/packages/visx-brush/src/Brush.tsx#L14 - const brushOffset = - xBrushScale.invert(2).getTime() - xBrushScale.invert(0).getTime(); - - // compensating the offset - // x0 and x1 are millisecond value - const startDate = millisecondTodate(x0 + brushOffset); - const endDate = millisecondTodate(x1 - brushOffset); - - setSelectedRange({ - // don't let range go outside the xAxisRange, if provided - start: xAxisRange - ? startDate < xAxisRange.start - ? xAxisRange.start - : startDate - : startDate, - end: xAxisRange - ? endDate > xAxisRange.end - ? xAxisRange.end - : endDate - : endDate, - }); - // resetting disabled left and arrow buttons - if (setDisableLeftArrow) setDisableLeftArrow(false); - if (setDisableRightArrow) setDisableRightArrow(false); - }, debounceRateMs), - [setSelectedRange, xAxisRange] - ); - - // Cancel any pending onBrushChange requests when this component is unmounted - useEffect(() => { - return () => { - onBrushChange.cancel(); - }; - }, []); - // bounds const xBrushMax = Math.max(width - margin.left - margin.right, 0); // take 70 % of given height considering axis tick/tick labels at the bottom @@ -189,6 +141,46 @@ function TimeSlider(props: TimeSliderProps) { ? selectedRange.start + ':' + selectedRange.end : 'no_brush'; + const onBrushChange = useMemo( + () => + debounce((domain: Bounds | null) => { + if (!domain) return; + const { x0, x1 } = domain; + + // computing the offset of 2 pixel (SAFE_PIXEL) in domain (milliseconds) + // https://github.com/airbnb/visx/blob/86a851cb3bf622b013b186f02f955bcd6548a87f/packages/visx-brush/src/Brush.tsx#L14 + const brushOffset = + xBrushScale.invert(2).getTime() - xBrushScale.invert(0).getTime(); + + // compensating the offset + // x0 and x1 are millisecond value + const startDate = millisecondTodate(x0 + brushOffset); + const endDate = millisecondTodate(x1 - brushOffset); + + setSelectedRange({ + // don't let range go outside the xAxisRange, if provided + start: xAxisRange + ? startDate < xAxisRange.start + ? xAxisRange.start + : startDate + : startDate, + end: xAxisRange + ? endDate > xAxisRange.end + ? xAxisRange.end + : endDate + : endDate, + }); + }, debounceRateMs), + [setSelectedRange, xAxisRange, debounceRateMs, xBrushScale] + ); + + // Cancel any pending onBrushChange requests when this component is unmounted + useEffect(() => { + return () => { + onBrushChange.cancel(); + }; + }, [onBrushChange]); + return (
    Expand the panel with the{' '} tab or click{' '} - setMinimized(false)}> + setMinimized(false)} + > here {' '} to reveal further controls that allow you to... @@ -270,9 +274,6 @@ export default function TimeSliderQuickFilter({ end: newRange.max as string, }; updateConfig({ ...config, selectedRange: newSelectedRange }); - // resetting disabled left and arrow buttons - setDisableLeftArrow(false); - setDisableRightArrow(false); } }, [config, updateConfig] @@ -290,7 +291,7 @@ export default function TimeSliderQuickFilter({ updateConfig({ ...config, selectedRange: newSelectedRange }); } }, - [config, updateConfig, extendedDisplayRange, selectedRange] + [config, updateConfig, selectedRange] ); // enabling/disabling date range arrows @@ -369,9 +370,6 @@ export default function TimeSliderQuickFilter({ axisColor={!active ? '#888' : '#000'} // disable user-interaction disabled={!active} - // for resetting disabled left and arrow buttons when changing brush bounds - setDisableLeftArrow={setDisableLeftArrow} - setDisableRightArrow={setDisableRightArrow} /> ) : ( From 9366590b44728bf6b2aabb899dcc6f9e14aa6dcb Mon Sep 17 00:00:00 2001 From: "Dae Kun (DK) Kwon" Date: Thu, 8 Aug 2024 11:38:10 -0400 Subject: [PATCH 9/9] remove href --- .../libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx index 4b33b431c6..d25e336abf 100755 --- a/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx +++ b/packages/libs/eda/src/lib/map/analysis/TimeSliderQuickFilter.tsx @@ -234,11 +234,7 @@ export default function TimeSliderQuickFilter({

    Expand the panel with the{' '} tab or click{' '} - setMinimized(false)} - > + setMinimized(false)}> here {' '} to reveal further controls that allow you to...