Skip to content

Commit

Permalink
Merge branch 'datepicker-all'
Browse files Browse the repository at this point in the history
  • Loading branch information
Shamann committed Nov 10, 2021
2 parents 3233953 + 18e1f73 commit 326887a
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 48 deletions.
8 changes: 6 additions & 2 deletions src/DateRangePickerMenu/DateRangePickerMenu.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@
flex-shrink: 0;
}

.activeRange {
font-weight: 700;
.range {
cursor: 'pointer';

&--active {
font-weight: 700;
}
}
}
86 changes: 55 additions & 31 deletions src/DateRangePickerMenu/DateRangePickerMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,61 +10,89 @@

import { CalendarTwoTone } from '@ant-design/icons';
import { DatePicker, Divider, Space, Typography } from 'antd';
import { mapValues } from 'lodash-es';
import moment, { Duration, Moment } from 'moment';
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useState } from 'react';
import styles from './DateRangePickerMenu.module.scss';

declare type Dates = [Moment, Moment] | null;
interface DateRangePickerMenuProps {
dateFormat?: string;
ranges?: Record<string, Duration>;
onChange(dates: [Moment, Moment]): void;
ranges?: Record<string, Duration | null>;
activeRange?: string;
onChange(dates: Dates): void;
}
interface DateRangeListProps {
value: Dates;
ranges: Record<string, Dates>;
renderItem: (label: string, dates: Dates) => JSX.Element;
}

const defaultRanges: Record<string, Duration> = {
const defaultRanges: Record<string, Duration | null> = {
'1D': moment.duration(1, 'd'),
'1W': moment.duration(1, 'w'),
'1M': moment.duration(1, 'M'),
'3M': moment.duration(3, 'M'),
'6M': moment.duration(6, 'M'),
'1Y': moment.duration(1, 'y'),
All: null,
};

const DateRangesList = ({ value, ranges, renderItem }: DateRangeListProps) => {
const isActive = (selectedRange: Dates, currentRange: Dates) => {
if (selectedRange && currentRange) {
return selectedRange[0].isSame(currentRange[0], 'day') && selectedRange[1].isSame(currentRange[1], 'day');
} else if (!selectedRange) {
return currentRange === null;
}
return false;
};

return (
<Space split={<Divider type='vertical' style={{ borderWidth: 2, margin: 0, borderColor: '#dadce0' }} />}>
{Object.entries(ranges).map(([label, dates]) => (
<div key={label} className={isActive(value, dates) ? styles['range--active'] : styles['range']}>
{renderItem(label, dates)}
</div>
))}
</Space>
);
};

const DateRangePickerMenu: React.FC<DateRangePickerMenuProps> = ({
dateFormat = 'DD.MM.YYYY',
ranges = defaultRanges,
activeRange,
onChange,
}) => {
const [dates, setDates] = useState<[Moment, Moment] | null>(null);
const activeRangeRef = useRef<any>(null);
const dateRanges = mapValues(ranges, (duration) =>
duration ? ([moment().subtract(duration), moment()] as [Moment, Moment]) : null,
);
const selectedRange = (activeRange && dateRanges[activeRange]) || null;
const [dates, setDates] = useState<Dates>(selectedRange);

const { RangePicker } = DatePicker;

useEffect(() => {
if (dates) {
if (dates !== undefined) {
onChange(dates);
}
}, [dates, onChange]);

return (
<div className={styles.controlPanel} style={{ order: 3 }}>
<div>
<Space split={<Divider type='vertical' style={{ borderWidth: 2, margin: 0, borderColor: '#dadce0' }} />}>
{Object.keys(ranges).map((range: string) => (
<Typography.Link
key={range}
onClick={(e) => {
const calendarDates: [Moment, Moment] = [moment().subtract(ranges[range]), moment()];
if (activeRangeRef.current !== null) {
activeRangeRef.current.classList.remove(styles.activeRange);
}
activeRangeRef.current = e.target;
activeRangeRef.current.classList.add(styles.activeRange);
setDates(calendarDates);
}}>
{range}
</Typography.Link>
))}
</Space>
</div>
<div className={styles.controlPanel}>
<DateRangesList
value={dates}
ranges={dateRanges}
renderItem={(label, dates) => (
<Typography.Link
onClick={() => {
setDates(dates);
}}>
{label}
</Typography.Link>
)}
/>

<div className={styles.rangePicker}>
<RangePicker
Expand All @@ -74,11 +102,7 @@ const DateRangePickerMenu: React.FC<DateRangePickerMenuProps> = ({
showNow
separator='-'
onChange={(calendarDates: any) => {
if (activeRangeRef.current !== null) {
activeRangeRef.current.classList.remove(styles.activeRange);
}
setDates(calendarDates);
onChange(calendarDates);
}}
disabledDate={(d) => !d || d.isAfter(moment())}
className={styles.picker}
Expand Down
49 changes: 34 additions & 15 deletions src/DateRangePickerMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,43 @@ function getXYScales(scales: Record<string, Meta>) {
}

const ChartDateRangePicker = ({ plot, options }: any) => {
const handleDatesChanged = (dates: [Moment, Moment]) => {
const { timeUnit = null } = options;
const { chart } = plot;
const chartViews = [...(chart.views || []), ...(chart.options.scales ? [chart] : [])];
chartViews.forEach((view: any) => {
const scales = view.options.scales || chart.options.scales;
const { xScales } = getXYScales(scales);
const timeScalesName = Object.keys(xScales)[0];
view.filter('*', (value: any, data: Datum) => {
const resultDate = moment(data[timeScalesName]);
return resultDate.isSameOrAfter(dates[0], timeUnit) && resultDate.isSameOrBefore(dates[1], timeUnit);
const handleDatesChanged = (dates: [Moment, Moment] | null) => {
if (plot) {
const { timeUnit = null } = options;
const { chart } = plot;
const chartViews = [...(chart.views || []), ...(chart.options.scales ? [chart] : [])];
chartViews.forEach((view: G2.View) => {
const scales = view.getOptions().scales || chart.options.scales;
const { xScales } = getXYScales(scales);
const timeScalesName = Object.keys(xScales)[0];
if (dates === null) {
view.filter('*', null);
} else {
view.filter('*', (value: any, data: Datum) => {
const resultDate = moment(data[timeScalesName]);
return resultDate.isSameOrAfter(dates[0], timeUnit) && resultDate.isSameOrBefore(dates[1], timeUnit);
});
}
});
});
chart.render();
chart.emit('data:filter', G2.Event.fromData(plot?.chart, 'data:filter', { timestamp: Date.now() }));
chart.render();
chart.emit('data:filter', G2.Event.fromData(plot?.chart, 'data:filter', { timestamp: Date.now() }));
}
};

return <DateRangePickerMenu onChange={handleDatesChanged} />;
return (
<DateRangePickerMenu
ranges={{
'1 нед.': moment.duration(1, 'w'),
'1 мес.': moment.duration(1, 'M'),
'3 мес.': moment.duration(3, 'M'),
'6 мес.': moment.duration(6, 'M'),
'1 год': moment.duration(1, 'y'),
'2 года': moment.duration(2, 'y'),
'Макс.': null,
}}
onChange={handleDatesChanged}
/>
);
};

export default ChartDateRangePicker;

0 comments on commit 326887a

Please sign in to comment.