Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Range picker hydration #147

Merged
merged 2 commits into from
Feb 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 128 additions & 61 deletions components/DataRangePicker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { throttle } from 'lodash';
import { scale } from '../../helpers/data';
import { isMobile } from '../../helpers/layout';
import 'rc-slider/assets/index.css';
import styles from './DataRangePicker.module.css';
import useIsMobile from '../../hooks/useIsMobile';

dayjs.extend(relativeTime);

Expand All @@ -32,82 +32,149 @@ const handle = ({
);
};

class DataRangePicker extends React.Component {
constructor (props) {
super(props);

this.onChange = throttle(this.onChange, 1000 / 60);
}
const DataRangePicker = (props) => {
const isMobile = useIsMobile();

onChange (value) {
const { setTimestamp } = this.props;
setTimestamp(this.scaleFrom(value));
}
const { timestamp, range: { start, end }} = props

// Scale from Slider value to our timestamp
scaleFrom (value) {
const {
range: { start, end },
} = this.props;
const scaleFrom = (value) => {
// On mobile we need to flip our min and max in order to have the slider start from the left
// Without doing this the slider would put "Today" at the right side
return isMobile()
return isMobile
? scale(value, SLIDER_MAX, SLIDER_MIN, start, end)
: scale(value, SLIDER_MIN, SLIDER_MAX, start, end);
}

// Scale our timestamp to our Slider value
scaleTo (value) {
const {
range: { start, end },
} = this.props;
const scaleTo = (value) => {
// On mobile we need to flip our min and max in order to have the slider start from the left
// Without doing this the slider would put "Today" at the right side
return isMobile()
return isMobile
? scale(value, start, end, SLIDER_MAX, SLIDER_MIN)
: scale(value, start, end, SLIDER_MIN, SLIDER_MAX);
}

render () {
const {
timestamp,
range: { start, end },
} = this.props;

const timestampDate = dayjs(timestamp);
const formattedDate = dayjs(timestamp).format('ddd MMM D, YYYY');

const startDate = dayjs(start);
const atStart = startDate.isSame(timestampDate);

const endDate = dayjs(end);
const atEnd = endDate.isSame(timestampDate);

return (
<div className={styles.container}>
<div className={styles.sliderContainer}>
<RcSlider
vertical={!isMobile()}
min={SLIDER_MIN}
max={SLIDER_MAX}
value={this.scaleTo(timestamp)}
onChange={(value) => this.onChange(value)}
railStyle={{ width: '100%' }}
handleRender={(node) => handle({
label: formattedDate,
atStart,
atEnd,
...node.props
})}
/>
</div>
<div className={styles.labels}>
<label className={styles.label}>Latest</label>
<label className={styles.label}>Oldest</label>
</div>
</div>
);
const throttledOnchange = (value) => {
const { setTimestamp } = props;
setTimestamp(scaleFrom(value));
}

const onChange = throttle(throttledOnchange, 1000 / 600)

const timestampDate = dayjs(timestamp);
const formattedDate = dayjs(timestamp).format('ddd MMM D, YYYY');

const startDate = dayjs(start);
const atStart = startDate.isSame(timestampDate);

const endDate = dayjs(end);
const atEnd = endDate.isSame(timestampDate);

return (
<div className={styles.container}>
<div className={styles.sliderContainer}>
<RcSlider
vertical={!isMobile}
min={SLIDER_MIN}
max={SLIDER_MAX}
value={scaleTo(timestamp)}
onChange={(value) => onChange(value)}
railStyle={{ width: '100%' }}
handleRender={(node) => handle({
label: formattedDate,
atStart,
atEnd,
...node.props
})}
/>
</div>
<div className={styles.labels}>
<label className={styles.label}>Latest</label>
<label className={styles.label}>Oldest</label>
</div>
</div>
);

}

// leaving this commented out for now for reference ::
//
// class DataRangePicker extends React.Component {
// constructor (props) {
// super(props);
//
// this.onChange = throttle(this.onChange, 1000 / 60);
// }
//
// onChange (value) {
// const { setTimestamp } = this.props;
// setTimestamp(this.scaleFrom(value));
// }
//
// // Scale from Slider value to our timestamp
// scaleFrom (value) {
// const {
// range: { start, end },
// } = this.props;
// // On mobile we need to flip our min and max in order to have the slider start from the left
// // Without doing this the slider would put "Today" at the right side
// return isMobile()
// ? scale(value, SLIDER_MAX, SLIDER_MIN, start, end)
// : scale(value, SLIDER_MIN, SLIDER_MAX, start, end);
// }
//
// // Scale our timestamp to our Slider value
// scaleTo (value) {
// const {
// range: { start, end },
// } = this.props;
// // On mobile we need to flip our min and max in order to have the slider start from the left
// // Without doing this the slider would put "Today" at the right side
// return isMobile()
// ? scale(value, start, end, SLIDER_MAX, SLIDER_MIN)
// : scale(value, start, end, SLIDER_MIN, SLIDER_MAX);
// }
//
// render () {
// const {
// timestamp,
// range: { start, end },
// } = this.props;
//
// const timestampDate = dayjs(timestamp);
// const formattedDate = dayjs(timestamp).format('ddd MMM D, YYYY');
//
// const startDate = dayjs(start);
// const atStart = startDate.isSame(timestampDate);
//
// const endDate = dayjs(end);
// const atEnd = endDate.isSame(timestampDate);
//
// return (
// <div className={styles.container}>
// <div className={styles.sliderContainer}>
// <RcSlider
// vertical={!isMobile()}
// min={SLIDER_MIN}
// max={SLIDER_MAX}
// value={this.scaleTo(timestamp)}
// onChange={(value) => this.onChange(value)}
// railStyle={{ width: '100%' }}
// handleRender={(node) => handle({
// label: formattedDate,
// atStart,
// atEnd,
// ...node.props
// })}
// />
// </div>
// <div className={styles.labels}>
// <label className={styles.label}>Latest</label>
// <label className={styles.label}>Oldest</label>
// </div>
// </div>
// );
// }
// }

export default DataRangePicker;
8 changes: 7 additions & 1 deletion helpers/layout.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
const MOBILE_BREAKPOINT = 560

/**
* **To be deprecated, do not use.**
*
* Use `useIsMobile` hook in `hooks` directory, instead
*/
export const isMobile = () => {
const width = (typeof window !== 'undefined') ? window.innerWidth : 1200
return width < MOBILE_BREAKPOINT
}
return true;
}
24 changes: 24 additions & 0 deletions hooks/useIsMobile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { useEffect, useState } from 'react';

const breakpoint = 560;

export default function useIsMobile() {
const [state, setState] = useState(false);

useEffect(() => {

//check the window width right on mount to see if it's already in mobile -- navigated to the page using a mobile viewport
setState(window.innerWidth < breakpoint)
function handleWidth() {
setState(window.innerWidth < breakpoint);
}

window.addEventListener('resize', handleWidth)

//clean up the event listener on un-mount and between re-renders
return () => window.removeEventListener('resize', handleWidth);
}, [setState]);

return state;
}