compote-ui
Version:
An opinionated UI component library for Svelte, built on top of [Ark UI](https://ark-ui.com) with additional components and features not available in the core Ark UI library.
59 lines (58 loc) • 2.39 kB
JavaScript
/**
* Calendar-day range filters for date and date-time columns.
*
* The filter value is `[from?, to?]`, each an ISO calendar date (`YYYY-MM-DD`) or empty — no
* time, no zone, so it serializes cleanly. Both bounds are inclusive days. A cell matches
* when the day it shows in the display zone (see `readDateInZone`) falls inside the range.
* For an instant that is the half-open interval [start of `from`, start of the day after
* `to`) in that zone, with day starts taken from the calendar — so 23- and 25-hour DST days
* need no special handling.
*/
import { CalendarDate, parseDate, toCalendarDate } from '@internationalized/date';
import { readDateInZone } from './date';
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
/** A bound as a calendar date; anything but a valid `YYYY-MM-DD` reads as no bound. */
function readBound(value) {
if (typeof value !== 'string' || !DATE_ONLY.test(value))
return undefined;
try {
return parseDate(value);
}
catch {
return undefined;
}
}
/**
* The range a filter value stands for. Invalid bounds are dropped and a reversed range is
* swapped (as TanStack's `inNumberRange` does). Always returns a range — never undefined —
* because the table falls back to the raw value when `resolveFilterValue` returns nullish.
*/
export function resolveDateRange(filterValue) {
if (!Array.isArray(filterValue))
return {};
const from = readBound(filterValue[0]);
const to = readBound(filterValue[1]);
if (from && to && from.compare(to) > 0)
return { from: to, to: from };
return { from, to };
}
/** Whether a filter value has no usable bound, so the filter should be removed. */
export function isEmptyDateRange(filterValue) {
const { from, to } = resolveDateRange(filterValue);
return !from && !to;
}
/**
* Whether a cell value falls in `range`, judged by the calendar day it shows in `timeZone`.
* Empty or unparseable cell values never match while the filter is active.
*/
export function matchesDateRange(value, range, timeZone) {
const date = readDateInZone(value, timeZone);
if (!date)
return false;
const day = date instanceof CalendarDate ? date : toCalendarDate(date);
if (range.from && day.compare(range.from) < 0)
return false;
if (range.to && day.compare(range.to) > 0)
return false;
return true;
}