import { useState, useEffect, Component, Fragment, ComponentType, FC } from 'react';
import { DatePicker, TimePicker } from '@progress/kendo-react-dateinputs';
import { TableFilterCellProps } from '@servicetitan/design-system';
import { NumericTextBox, NumericTextBoxProps } from '@progress/kendo-react-inputs';
import moment from 'moment';
import { renderCustomColumnMenuFilter } from '../column-menu-filters';
// TODO: remove when Kendo fix the propType bug of DatePicker
type ComponentClassWithBrokenPropTypes
= ComponentType
& { defaultProps?: any };
interface Range {
min: T;
max: T;
}
interface BaseControlProps {
value?: T | null;
width?: number | string;
onChange?: (ev: any) => void;
}
type RangeControlProps = BaseControlProps & TProps;
type RangeControl = Component> & { value: T | null };
const NoSSR: FC<{ children: JSX.Element }> = ({ children }) => {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
return isMounted ? children : null;
};
class RangeFilterCellBase extends Component {
private minValueBox: RangeControl | null = null;
private maxValueBox: RangeControl | null = null;
constructor(
props: TableFilterCellProps,
private controlClass: ComponentClassWithBrokenPropTypes>,
private controlProps: TProps = {} as any
) {
super(props);
}
/** override this if additional type support needed */
inRange(current: T, values: Range) {
return (
(values.min === null || values.min === undefined || current >= values.min) &&
(values.max === null || values.max === undefined || current <= values.max)
);
}
onChange = (ev: any) => {
if (!this.minValueBox || !this.maxValueBox) {
return;
}
this.props.onChange({
value: { min: this.minValueBox.value, max: this.maxValueBox.value },
operator: this.inRange,
syntheticEvent: ev.syntheticEvent,
});
};
render() {
const filterValue = this.props.value;
const Control = this.controlClass;
return (
From:
{/* KendoNumericTextBox uses useLayoutEffect which is incompatible with SSR */}
To:
{/* KendoNumericTextBox uses useLayoutEffect which is incompatible with SSR */}
);
}
protected setMinValueRef = (el: RangeControl | null) => {
this.minValueBox = el;
};
protected setMaxValueRef = (el: RangeControl | null) => {
this.maxValueBox = el;
};
}
class NumericRangeFilterCell extends RangeFilterCellBase {
constructor(props: TableFilterCellProps) {
super(props, NumericTextBox);
}
}
class CurrencyRangeFilterCell extends RangeFilterCellBase {
constructor(props: TableFilterCellProps) {
super(props, NumericTextBox, {
format: 'c',
});
}
}
class DateRangeFilterCell extends RangeFilterCellBase {
constructor(props: TableFilterCellProps) {
super(props, DatePicker);
}
}
function getTimeString(date: Date) {
return `${date.getHours()}:${date.getMinutes()}`;
}
class TimeRangeFilterCell extends RangeFilterCellBase {
constructor(props: TableFilterCellProps) {
super(props, TimePicker);
}
inRange(current: Date, values: Range) {
return moment(getTimeString(current), 'h:mma').isBetween(
moment(getTimeString(values.min || current), 'h:mma'),
moment(getTimeString(values.max || current), 'h:mma'),
'minutes',
'[]'
);
}
}
export const NumericRangeColumnMenuFilter = renderCustomColumnMenuFilter(NumericRangeFilterCell);
export const CurrencyRangeColumnMenuFilter = renderCustomColumnMenuFilter(CurrencyRangeFilterCell);
export const DateRangeColumnMenuFilter = renderCustomColumnMenuFilter(DateRangeFilterCell);
export const TimeRangeColumnMenuFilter = renderCustomColumnMenuFilter(TimeRangeFilterCell);