in src/data-table/column-datetime.tsx [217:560]
function DatetimeFilter(props) {
const [css, theme] = useStyletron();
const locale = React.useContext(LocaleContext);
const mountNode = React.useRef();
const initialState = filterParamsToInitialState(props.filterParams);
const datesSorted = React.useMemo(() => {
return props.data.sort(sortDates);
}, [props.data]);
const presentYears = React.useMemo(() => {
const dict = {};
// @ts-ignore
props.data.forEach((date) => {
// @ts-ignore
dict[getYear(date)] = true;
});
return Object.keys(dict).map((n) => parseInt(n));
}, [props.data]);
const startOfWeek = React.useMemo(() => {
return getStartOfWeek(new Date(), props.locale);
}, [props.locale]);
const localizedWeekdays = React.useMemo(() => {
return [...WEEKDAYS.slice(getDay(startOfWeek), 7), ...WEEKDAYS.slice(0, getDay(startOfWeek))];
}, [props.locale]);
const [exclude, setExclude] = React.useState(initialState.exclude);
const [comparatorIndex, setComparatorIndex] = React.useState(initialState.comparatorIndex);
const [rangeOperator, setRangeOperator] = React.useState<Value>([initialState.rangeOperator]);
const [categoricalOperator, setCategoricalOperator] = React.useState<Value>([
initialState.categoricalOperator,
]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [rangeDates, setRangeDates] = React.useState<any>(
initialState.rangeDates.length
? initialState.rangeDates
: [new Date(datesSorted[0]), new Date(datesSorted[datesSorted.length - 1])]
);
const [years, setYears] = React.useState<number[]>(initialState.years);
const [halves, setHalves] = React.useState<number[]>(initialState.halves);
const [quarters, setQuarters] = React.useState<number[]>(initialState.quarters);
const [months, setMonths] = React.useState<number[]>(initialState.months);
const [weekdays, setWeekdays] = React.useState<number[]>(initialState.weekdays);
const isRange = comparatorIndex === 0;
const isCategorical = comparatorIndex === 1;
return (
<FilterShell
exclude={exclude}
onExcludeChange={() => setExclude(!exclude)}
onApply={() => {
if (isRange) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const op: DatetimeOperations = rangeOperator[0].id as any;
let description = '';
if (op === DATETIME_OPERATIONS.RANGE_DATETIME) {
const left = format(rangeDates[0], FORMAT_STRING);
const right = format(rangeDates[1], FORMAT_STRING);
description = `${left} - ${right}`;
} else if (op === DATETIME_OPERATIONS.RANGE_DATE) {
const left = format(rangeDates[0], DATE_FORMAT);
const right = format(rangeDates[1], DATE_FORMAT);
description = `${left} - ${right}`;
} else if (op === DATETIME_OPERATIONS.RANGE_TIME) {
const left = format(rangeDates[0], TIME_FORMAT);
const right = format(rangeDates[1], TIME_FORMAT);
description = `${left} - ${right}`;
}
props.setFilter({
operation: op,
range: rangeDates,
selection: [],
description: description,
exclude,
});
}
if (isCategorical) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const op: DatetimeOperations = categoricalOperator[0].id as any;
let selection: number[] = [];
let operatorLocaleLabelKey = '';
let description = '';
if (op === DATETIME_OPERATIONS.WEEKDAY) {
selection = weekdays;
operatorLocaleLabelKey = CATEGORICAL_OPERATIONS[0].localeLabelKey;
description = weekdays
.map((w) => {
const day = addDays(startOfWeek, localizedWeekdays.indexOf(w));
return getWeekdayInLocale(day, props.locale);
})
.join(', ');
} else if (op === DATETIME_OPERATIONS.MONTH) {
selection = months;
operatorLocaleLabelKey = CATEGORICAL_OPERATIONS[1].localeLabelKey;
description = months.map((m) => getMonthInLocale(m, props.locale)).join(', ');
} else if (op === DATETIME_OPERATIONS.QUARTER) {
selection = quarters;
operatorLocaleLabelKey = CATEGORICAL_OPERATIONS[2].localeLabelKey;
description = quarters.map((q) => getQuarterInLocale(q, props.locale)).join(', ');
} else if (op === DATETIME_OPERATIONS.HALF) {
selection = halves;
operatorLocaleLabelKey = CATEGORICAL_OPERATIONS[3].localeLabelKey;
description = halves
.map((h) =>
h === 0
? locale.datatable.datetimeFilterCategoricalFirstHalf
: locale.datatable.datetimeFilterCategoricalSecondHalf
)
.join(', ');
} else if (op === DATETIME_OPERATIONS.YEAR) {
selection = years;
operatorLocaleLabelKey = CATEGORICAL_OPERATIONS[4].localeLabelKey;
description = years.join(', ');
}
if (operatorLocaleLabelKey) {
// @ts-ignore
description = `${locale.datatable[operatorLocaleLabelKey]} - ${description}`;
}
props.setFilter({
operation: op,
range: [],
selection,
description,
exclude,
});
}
props.close();
}}
>
{/* @ts-ignore */}
<div ref={mountNode}>
<ButtonGroup
size={SIZE.compact}
mode={MODE.radio}
selected={comparatorIndex}
onClick={(_, index) => setComparatorIndex(index)}
overrides={{
Root: {
style: ({ $theme }) => ({ marginBottom: $theme.sizing.scale300 }),
},
}}
>
<Button type="button" overrides={{ BaseButton: { style: { width: '100%' } } }}>
{locale.datatable.datetimeFilterRange}
</Button>
<Button type="button" overrides={{ BaseButton: { style: { width: '100%' } } }}>
{locale.datatable.datetimeFilterCategorical}
</Button>
</ButtonGroup>
{isRange && (
<div>
<Select
value={rangeOperator}
onChange={(params) => setRangeOperator(params.value)}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mountNode={mountNode.current as any}
options={RANGE_OPERATIONS.map((op) => ({
// @ts-ignore
label: locale.datatable[op.localeLabelKey],
id: op.id,
}))}
size="compact"
clearable={false}
/>
<div className={css({ paddingTop: theme.sizing.scale600 })}>
{(rangeOperator[0].id === DATETIME_OPERATIONS.RANGE_DATETIME ||
rangeOperator[0].id === DATETIME_OPERATIONS.RANGE_DATE) && (
<Datepicker
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mountNode={mountNode.current as any}
value={rangeDates}
onChange={({ date }) => {
if (Array.isArray(date)) {
if (!date.length) return;
const nextDates = date.map((d, i) =>
d ? applyDateToTime(rangeDates[i], d) : null
);
setRangeDates(nextDates);
}
}}
formatString={DATE_FORMAT}
mask={MASK}
placeholder="MM-DD-YYYY - MM-DD-YYYY"
minDate={datesSorted[0]}
maxDate={datesSorted[datesSorted.length - 1]}
timeSelectStart={rangeOperator[0].id === DATETIME_OPERATIONS.RANGE_DATETIME}
timeSelectEnd={rangeOperator[0].id === DATETIME_OPERATIONS.RANGE_DATETIME}
overrides={{ TimeSelect: { props: { size: 'compact' } } }}
range
size="compact"
locale={props.locale}
/>
)}
</div>
{(rangeOperator[0].id === DATETIME_OPERATIONS.RANGE_DATETIME ||
rangeOperator[0].id === DATETIME_OPERATIONS.RANGE_TIME) && (
<div
className={css({
display: 'flex',
paddingTop: theme.sizing.scale100,
})}
>
<div
className={css({
width: '100%',
marginRight: theme.sizing.scale300,
})}
>
<TimePicker
format="24"
value={rangeDates[0]}
onChange={(time) =>
time && setRangeDates([applyTimeToDate(rangeDates[0], time), rangeDates[1]])
}
creatable
size="compact"
/>
</div>
<div
className={css({
width: '100%',
})}
>
<TimePicker
format="24"
value={rangeDates[1]}
onChange={(time) =>
time && setRangeDates([rangeDates[0], applyTimeToDate(rangeDates[1], time)])
}
creatable
size="compact"
/>
</div>
</div>
)}
</div>
)}
{isCategorical && (
<div>
<Select
value={categoricalOperator}
onChange={(params) => setCategoricalOperator(params.value)}
options={CATEGORICAL_OPERATIONS.map((op) => ({
// @ts-ignore
label: locale.datatable[op.localeLabelKey],
id: op.id,
}))}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mountNode={mountNode.current as any}
size="compact"
clearable={false}
/>
<div
className={css({
paddingLeft: theme.sizing.scale300,
paddingTop: theme.sizing.scale500,
})}
>
{categoricalOperator[0].id === DATETIME_OPERATIONS.WEEKDAY && (
<Checks
value={weekdays}
setValue={setWeekdays}
options={localizedWeekdays.map((w, offset) => {
const day = addDays(startOfWeek, offset);
return {
label: getWeekdayInLocale(day, props.locale),
id: w,
};
})}
/>
)}
{categoricalOperator[0].id === DATETIME_OPERATIONS.MONTH && (
<Checks
value={months}
setValue={setMonths}
options={MONTHS.map((m) => ({
label: getMonthInLocale(m, props.locale),
id: m,
}))}
/>
)}
{categoricalOperator[0].id === DATETIME_OPERATIONS.QUARTER && (
<Checks
value={quarters}
setValue={setQuarters}
options={QUARTERS.map((q) => ({
label: getQuarterInLocale(q, props.locale),
id: q,
}))}
/>
)}
{categoricalOperator[0].id === DATETIME_OPERATIONS.HALF && (
<Checks
value={halves}
setValue={setHalves}
options={[
{
label: locale.datatable.datetimeFilterCategoricalFirstHalf,
id: 0,
},
{
label: locale.datatable.datetimeFilterCategoricalSecondHalf,
id: 1,
},
]}
/>
)}
{categoricalOperator[0].id === DATETIME_OPERATIONS.YEAR && (
<Checks
value={years}
setValue={setYears}
options={presentYears.map((year) => ({
label: year,
id: year,
}))}
/>
)}
</div>
</div>
)}
</div>
</FilterShell>
);
}