schummar-table 0.40.5 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as _emotion_react_jsx_runtime from '@emotion/react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import { ComponentType, ReactNode, HTMLProps } from 'react';
4
- import { T as TableTheme, C as CommonFilterProps, S as SerializableValue, a as TableProps, F as FilterImplementation, M as MemoizedTableTheme, I as Id, b as TableItem, c as InternalTableState, P as PartialTableTheme } from './types-CYMVvUGJ.cjs';
5
- export { e as Column, f as FunctionWithDeps, g as InternalColumn, h as Sort, i as SortDirection, d as TableStateStorage } from './types-CYMVvUGJ.cjs';
4
+ import { T as TableTheme, C as CommonFilterProps, a as TableProps, F as FilterImplementation, M as MemoizedTableTheme, I as Id, b as TableItem, c as InternalTableState, P as PartialTableTheme } from './types-xTAreHv_.cjs';
5
+ export { e as Column, f as FunctionWithDeps, g as InternalColumn, S as Sort, h as SortDirection, d as TableStateStorage } from './types-xTAreHv_.cjs';
6
6
  import { Store } from 'schummar-state/react';
7
7
  import { E as ExporterEntry } from './exporter-u6SatTo6.cjs';
8
8
  import '@emotion/react';
@@ -18,11 +18,12 @@ type DatePickerQuickOption = 'today' | 'thisWeek' | 'thisMonth' | 'thisYear' | '
18
18
  label: ReactNode;
19
19
  value: Date | DateRange | (() => Date | DateRange);
20
20
  } | ((onChange: (value: Date | DateRange | null) => void) => ReactNode);
21
+ type DatePickerChangeSource = 'input' | 'calendarWeek' | 'calendar' | 'quickOption';
21
22
  type DatePickerProps = {
22
23
  /** Currently selected day or range of days. */
23
24
  value: Date | DateRange | null;
24
25
  /** Callback for when the day (range) changes. */
25
- onChange: (value: Date | DateRange | null) => void;
26
+ onChange: (value: Date | DateRange | null, source: DatePickerChangeSource) => void;
26
27
  /** Currently visible date. */
27
28
  dateInView?: Date;
28
29
  /** Callback for when the currently visible date changes. */
@@ -92,16 +93,16 @@ declare function dateIntersect(a: Date | DateRange | null | undefined, b: Date |
92
93
  declare function dateClamp(date: Date, min?: Date, max?: Date): Date;
93
94
  declare function DatePicker(props: DatePickerProps): _emotion_react_jsx_runtime.JSX.Element;
94
95
 
95
- declare function DateFilter<T, V>({ locale, firstDayOfWeek, defaultDateInView, quickOptions, singleSelect, filterBy, minDate, maxDate, showCalendarWeek, ...props }: {
96
+ declare function DateFilter<TItem, TColumnValue>({ locale, firstDayOfWeek, defaultDateInView, quickOptions, singleSelect, filterBy, minDate, maxDate, showCalendarWeek, ...props }: {
96
97
  /** If enabled, only single days can be selected. Ranges otherwise. */
97
98
  singleSelect?: boolean;
98
- } & Pick<DatePickerProps, 'locale' | 'firstDayOfWeek' | 'defaultDateInView' | 'quickOptions' | 'minDate' | 'maxDate' | 'showCalendarWeek'> & CommonFilterProps<T, V, Date | DateRange | null, Date | DateRange | null>): JSX.Element;
99
+ } & Pick<DatePickerProps, 'locale' | 'firstDayOfWeek' | 'defaultDateInView' | 'quickOptions' | 'minDate' | 'maxDate' | 'showCalendarWeek'> & CommonFilterProps<TItem, TColumnValue, Date | DateRange | null, Date | DateRange | null>): JSX.Element;
99
100
 
100
101
  interface RangeFilterProps<T, V> extends CommonFilterProps<T, V, number | null, [number | null, number | null] | null> {
101
102
  min?: number;
102
103
  max?: number;
103
104
  }
104
- declare function RangeFilter<T, V>({ min, max, filterBy, ...props }: RangeFilterProps<T, V>): JSX.Element;
105
+ declare function RangeFilter<TItem, TColumnValue>({ min, max, filterBy, ...props }: RangeFilterProps<TItem, TColumnValue>): JSX.Element;
105
106
 
106
107
  interface VirtualListProps<T> extends Omit<HTMLProps<HTMLDivElement>, 'children'> {
107
108
  virtual?: boolean | {
@@ -116,13 +117,13 @@ interface VirtualListProps<T> extends Omit<HTMLProps<HTMLDivElement>, 'children'
116
117
  children: (item: T, index: number) => ReactNode;
117
118
  }
118
119
 
119
- declare function SelectFilter<T, V, F extends SerializableValue>({ options: providedOptions, stringValue, render, singleSelect, hideSearchField, hideResetButton, ...props }: {
120
+ declare function SelectFilter<TItem, TColumnValue, TFilterBy>({ options: providedOptions, stringValue, render, singleSelect, hideSearchField, hideResetButton, ...props }: {
120
121
  /** Which options are provided to select. By default all unique item values are used. */
121
- options?: F[];
122
+ options?: TFilterBy[];
122
123
  /** String representation of a value. Used to filter options via the text field. */
123
- stringValue?: (value: F) => string;
124
+ stringValue?: (value: TFilterBy) => string;
124
125
  /** Render values. By default a string representation of the value is used. */
125
- render?: (value: F) => ReactNode;
126
+ render?: (value: TFilterBy) => ReactNode;
126
127
  /** If enabled, only one option can be selected at a time. */
127
128
  singleSelect?: boolean;
128
129
  /** If enabled, the search field is hidden. */
@@ -132,24 +133,24 @@ declare function SelectFilter<T, V, F extends SerializableValue>({ options: prov
132
133
  /** Virtual list props.
133
134
  * @default true */
134
135
  virtual?: VirtualListProps<unknown>['virtual'];
135
- } & CommonFilterProps<T, V, F, Set<F>>): JSX.Element;
136
+ } & CommonFilterProps<TItem, TColumnValue, TFilterBy, Set<TFilterBy>>): JSX.Element;
136
137
 
137
138
  declare function Table<T>(props: TableProps<T>): JSX.Element;
138
139
 
139
- declare function TextFilter<T, V>({ compare, filterBy, placeholder, ...props }: {
140
+ declare function TextFilter<TItem, TColumnValue>({ compare, filterBy, placeholder, ...props }: {
140
141
  /** Custom comparison function. Should return true if an item value matches the current filter value.
141
142
  * By default a fuzzy text comparison is used.
142
143
  */
143
144
  compare?: (itemValue: string, filterValue: string) => boolean;
144
145
  placeholder?: string;
145
- } & CommonFilterProps<T, V, string, string>): JSX.Element;
146
+ } & CommonFilterProps<TItem, TColumnValue, string, string>): JSX.Element;
146
147
 
147
- declare function useFilter<T, V, F, S extends SerializableValue>(impl: FilterImplementation<T, V, F, S>): {
148
+ declare function useFilter<TItem, TColumnValue, TFilterBy, TFilterValue>(impl: FilterImplementation<TItem, TColumnValue, TFilterBy, TFilterValue>): {
148
149
  isActive: boolean;
149
150
  close: () => void;
150
- value: S | undefined;
151
- onChange: (value?: S) => void;
152
- filterBy: (value: V, item: T) => F | F[];
151
+ value: TFilterValue | undefined;
152
+ onChange: (value?: TFilterValue) => void;
153
+ filterBy: (value: TColumnValue, item: TItem) => TFilterBy | TFilterBy[];
153
154
  };
154
155
 
155
156
  declare function useTheme<T, S>(selector: (theme: MemoizedTableTheme<T>) => S): S;
@@ -216,4 +217,4 @@ declare const termMatch: (text: string, query: string) => boolean;
216
217
  declare function configureTableTheme(tableTheme: PartialTableTheme): void;
217
218
  declare function mergeThemes<T>(...themes: PartialTableTheme<T>[]): PartialTableTheme<T>;
218
219
 
219
- export { AutoFocusTextField, ColumnContext, CommonFilterProps, DateFilter, DatePicker, type DatePickerProps, DatePickerProvider, type DatePickerQuickOption, type DateRange, FilterImplementation, Id, PartialTableTheme, RangeFilter, SelectFilter, Table, TableContext, TableProps, TableResetContext, TableSettingsContext, TableSettingsProvider, TextFilter, commonQuickOptions, configureTableTheme, dateClamp, dateIntersect, endOfDay, getCalendarWeek, helpers, lastDays, mergeThemes, startOfDay, termMatch, textMatch, thisMonth, thisWeek, thisYear, today, useColumnContext, useFilter, useTableContext, useTheme };
220
+ export { AutoFocusTextField, ColumnContext, CommonFilterProps, DateFilter, DatePicker, type DatePickerChangeSource, type DatePickerProps, DatePickerProvider, type DatePickerQuickOption, type DateRange, FilterImplementation, Id, PartialTableTheme, RangeFilter, SelectFilter, Table, TableContext, TableProps, TableResetContext, TableSettingsContext, TableSettingsProvider, TextFilter, commonQuickOptions, configureTableTheme, dateClamp, dateIntersect, endOfDay, getCalendarWeek, helpers, lastDays, mergeThemes, startOfDay, termMatch, textMatch, thisMonth, thisWeek, thisYear, today, useColumnContext, useFilter, useTableContext, useTheme };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as _emotion_react_jsx_runtime from '@emotion/react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import { ComponentType, ReactNode, HTMLProps } from 'react';
4
- import { T as TableTheme, C as CommonFilterProps, S as SerializableValue, a as TableProps, F as FilterImplementation, M as MemoizedTableTheme, I as Id, b as TableItem, c as InternalTableState, P as PartialTableTheme } from './types-3d4ARU6a.js';
5
- export { e as Column, f as FunctionWithDeps, g as InternalColumn, h as Sort, i as SortDirection, d as TableStateStorage } from './types-3d4ARU6a.js';
4
+ import { T as TableTheme, C as CommonFilterProps, a as TableProps, F as FilterImplementation, M as MemoizedTableTheme, I as Id, b as TableItem, c as InternalTableState, P as PartialTableTheme } from './types-DcnzwYBU.js';
5
+ export { e as Column, f as FunctionWithDeps, g as InternalColumn, S as Sort, h as SortDirection, d as TableStateStorage } from './types-DcnzwYBU.js';
6
6
  import { Store } from 'schummar-state/react';
7
7
  import { E as ExporterEntry } from './exporter-u6SatTo6.js';
8
8
  import '@emotion/react';
@@ -18,11 +18,12 @@ type DatePickerQuickOption = 'today' | 'thisWeek' | 'thisMonth' | 'thisYear' | '
18
18
  label: ReactNode;
19
19
  value: Date | DateRange | (() => Date | DateRange);
20
20
  } | ((onChange: (value: Date | DateRange | null) => void) => ReactNode);
21
+ type DatePickerChangeSource = 'input' | 'calendarWeek' | 'calendar' | 'quickOption';
21
22
  type DatePickerProps = {
22
23
  /** Currently selected day or range of days. */
23
24
  value: Date | DateRange | null;
24
25
  /** Callback for when the day (range) changes. */
25
- onChange: (value: Date | DateRange | null) => void;
26
+ onChange: (value: Date | DateRange | null, source: DatePickerChangeSource) => void;
26
27
  /** Currently visible date. */
27
28
  dateInView?: Date;
28
29
  /** Callback for when the currently visible date changes. */
@@ -92,16 +93,16 @@ declare function dateIntersect(a: Date | DateRange | null | undefined, b: Date |
92
93
  declare function dateClamp(date: Date, min?: Date, max?: Date): Date;
93
94
  declare function DatePicker(props: DatePickerProps): _emotion_react_jsx_runtime.JSX.Element;
94
95
 
95
- declare function DateFilter<T, V>({ locale, firstDayOfWeek, defaultDateInView, quickOptions, singleSelect, filterBy, minDate, maxDate, showCalendarWeek, ...props }: {
96
+ declare function DateFilter<TItem, TColumnValue>({ locale, firstDayOfWeek, defaultDateInView, quickOptions, singleSelect, filterBy, minDate, maxDate, showCalendarWeek, ...props }: {
96
97
  /** If enabled, only single days can be selected. Ranges otherwise. */
97
98
  singleSelect?: boolean;
98
- } & Pick<DatePickerProps, 'locale' | 'firstDayOfWeek' | 'defaultDateInView' | 'quickOptions' | 'minDate' | 'maxDate' | 'showCalendarWeek'> & CommonFilterProps<T, V, Date | DateRange | null, Date | DateRange | null>): JSX.Element;
99
+ } & Pick<DatePickerProps, 'locale' | 'firstDayOfWeek' | 'defaultDateInView' | 'quickOptions' | 'minDate' | 'maxDate' | 'showCalendarWeek'> & CommonFilterProps<TItem, TColumnValue, Date | DateRange | null, Date | DateRange | null>): JSX.Element;
99
100
 
100
101
  interface RangeFilterProps<T, V> extends CommonFilterProps<T, V, number | null, [number | null, number | null] | null> {
101
102
  min?: number;
102
103
  max?: number;
103
104
  }
104
- declare function RangeFilter<T, V>({ min, max, filterBy, ...props }: RangeFilterProps<T, V>): JSX.Element;
105
+ declare function RangeFilter<TItem, TColumnValue>({ min, max, filterBy, ...props }: RangeFilterProps<TItem, TColumnValue>): JSX.Element;
105
106
 
106
107
  interface VirtualListProps<T> extends Omit<HTMLProps<HTMLDivElement>, 'children'> {
107
108
  virtual?: boolean | {
@@ -116,13 +117,13 @@ interface VirtualListProps<T> extends Omit<HTMLProps<HTMLDivElement>, 'children'
116
117
  children: (item: T, index: number) => ReactNode;
117
118
  }
118
119
 
119
- declare function SelectFilter<T, V, F extends SerializableValue>({ options: providedOptions, stringValue, render, singleSelect, hideSearchField, hideResetButton, ...props }: {
120
+ declare function SelectFilter<TItem, TColumnValue, TFilterBy>({ options: providedOptions, stringValue, render, singleSelect, hideSearchField, hideResetButton, ...props }: {
120
121
  /** Which options are provided to select. By default all unique item values are used. */
121
- options?: F[];
122
+ options?: TFilterBy[];
122
123
  /** String representation of a value. Used to filter options via the text field. */
123
- stringValue?: (value: F) => string;
124
+ stringValue?: (value: TFilterBy) => string;
124
125
  /** Render values. By default a string representation of the value is used. */
125
- render?: (value: F) => ReactNode;
126
+ render?: (value: TFilterBy) => ReactNode;
126
127
  /** If enabled, only one option can be selected at a time. */
127
128
  singleSelect?: boolean;
128
129
  /** If enabled, the search field is hidden. */
@@ -132,24 +133,24 @@ declare function SelectFilter<T, V, F extends SerializableValue>({ options: prov
132
133
  /** Virtual list props.
133
134
  * @default true */
134
135
  virtual?: VirtualListProps<unknown>['virtual'];
135
- } & CommonFilterProps<T, V, F, Set<F>>): JSX.Element;
136
+ } & CommonFilterProps<TItem, TColumnValue, TFilterBy, Set<TFilterBy>>): JSX.Element;
136
137
 
137
138
  declare function Table<T>(props: TableProps<T>): JSX.Element;
138
139
 
139
- declare function TextFilter<T, V>({ compare, filterBy, placeholder, ...props }: {
140
+ declare function TextFilter<TItem, TColumnValue>({ compare, filterBy, placeholder, ...props }: {
140
141
  /** Custom comparison function. Should return true if an item value matches the current filter value.
141
142
  * By default a fuzzy text comparison is used.
142
143
  */
143
144
  compare?: (itemValue: string, filterValue: string) => boolean;
144
145
  placeholder?: string;
145
- } & CommonFilterProps<T, V, string, string>): JSX.Element;
146
+ } & CommonFilterProps<TItem, TColumnValue, string, string>): JSX.Element;
146
147
 
147
- declare function useFilter<T, V, F, S extends SerializableValue>(impl: FilterImplementation<T, V, F, S>): {
148
+ declare function useFilter<TItem, TColumnValue, TFilterBy, TFilterValue>(impl: FilterImplementation<TItem, TColumnValue, TFilterBy, TFilterValue>): {
148
149
  isActive: boolean;
149
150
  close: () => void;
150
- value: S | undefined;
151
- onChange: (value?: S) => void;
152
- filterBy: (value: V, item: T) => F | F[];
151
+ value: TFilterValue | undefined;
152
+ onChange: (value?: TFilterValue) => void;
153
+ filterBy: (value: TColumnValue, item: TItem) => TFilterBy | TFilterBy[];
153
154
  };
154
155
 
155
156
  declare function useTheme<T, S>(selector: (theme: MemoizedTableTheme<T>) => S): S;
@@ -216,4 +217,4 @@ declare const termMatch: (text: string, query: string) => boolean;
216
217
  declare function configureTableTheme(tableTheme: PartialTableTheme): void;
217
218
  declare function mergeThemes<T>(...themes: PartialTableTheme<T>[]): PartialTableTheme<T>;
218
219
 
219
- export { AutoFocusTextField, ColumnContext, CommonFilterProps, DateFilter, DatePicker, type DatePickerProps, DatePickerProvider, type DatePickerQuickOption, type DateRange, FilterImplementation, Id, PartialTableTheme, RangeFilter, SelectFilter, Table, TableContext, TableProps, TableResetContext, TableSettingsContext, TableSettingsProvider, TextFilter, commonQuickOptions, configureTableTheme, dateClamp, dateIntersect, endOfDay, getCalendarWeek, helpers, lastDays, mergeThemes, startOfDay, termMatch, textMatch, thisMonth, thisWeek, thisYear, today, useColumnContext, useFilter, useTableContext, useTheme };
220
+ export { AutoFocusTextField, ColumnContext, CommonFilterProps, DateFilter, DatePicker, type DatePickerChangeSource, type DatePickerProps, DatePickerProvider, type DatePickerQuickOption, type DateRange, FilterImplementation, Id, PartialTableTheme, RangeFilter, SelectFilter, Table, TableContext, TableProps, TableResetContext, TableSettingsContext, TableSettingsProvider, TextFilter, commonQuickOptions, configureTableTheme, dateClamp, dateIntersect, endOfDay, getCalendarWeek, helpers, lastDays, mergeThemes, startOfDay, termMatch, textMatch, thisMonth, thisWeek, thisYear, today, useColumnContext, useFilter, useTableContext, useTheme };
package/dist/index.js CHANGED
@@ -908,8 +908,8 @@ function AutoFocusTextField(props) {
908
908
 
909
909
  // src/hooks/useFilter.ts
910
910
  import { castDraft } from "immer";
911
- import { useContext as useContext5, useEffect, useLayoutEffect as useLayoutEffect2, useMemo as useMemo3, useRef as useRef2, useState as useState3 } from "react";
912
911
  import { nanoid } from "nanoid";
912
+ import { useContext as useContext5, useEffect, useLayoutEffect as useLayoutEffect2, useMemo as useMemo3, useRef as useRef2, useState as useState3 } from "react";
913
913
 
914
914
  // src/misc/debounce.ts
915
915
  function debounce(function_, ms) {
@@ -973,7 +973,9 @@ function useFilter(impl) {
973
973
  });
974
974
  };
975
975
  }, [table, columnId, impl]);
976
- const value = table.useState((state) => state.filterValues.get(columnId));
976
+ const value = table.useState(
977
+ (state) => state.filterValues.get(columnId)
978
+ );
977
979
  const [dirtyValue, setDirtyValue] = useState3();
978
980
  const implRef = useRef2(impl);
979
981
  useLayoutEffect2(() => {
@@ -1293,7 +1295,7 @@ function DatePicker(props) {
1293
1295
  showCalendarWeek,
1294
1296
  blockedRanges = []
1295
1297
  } = defaults(props, context);
1296
- function onChange(value2) {
1298
+ function onChange(value2, source) {
1297
1299
  if (value2 instanceof Date) {
1298
1300
  value2 = dateClamp(value2, minDate, maxDate);
1299
1301
  } else if (value2) {
@@ -1302,7 +1304,7 @@ function DatePicker(props) {
1302
1304
  max: dateClamp(value2.max, minDate, maxDate)
1303
1305
  };
1304
1306
  }
1305
- props.onChange(value2);
1307
+ props.onChange(value2, source);
1306
1308
  }
1307
1309
  const Button2 = useTheme((t) => t.components.Button);
1308
1310
  const IconButton2 = useTheme((t) => t.components.IconButton);
@@ -1376,7 +1378,7 @@ function DatePicker(props) {
1376
1378
  if (option instanceof Function) {
1377
1379
  return option((value3) => {
1378
1380
  setDirty(void 0);
1379
- onChange(value3);
1381
+ onChange(value3, "quickOption");
1380
1382
  });
1381
1383
  }
1382
1384
  const { label, value: value2 } = typeof option === "string" ? commonQuickOptions[option] : option;
@@ -1391,9 +1393,9 @@ function DatePicker(props) {
1391
1393
  onClick: () => {
1392
1394
  setDirty(void 0);
1393
1395
  if (value2 instanceof Function) {
1394
- onChange(value2(props));
1396
+ onChange(value2(props), "quickOption");
1395
1397
  } else {
1396
- onChange(value2);
1398
+ onChange(value2, "quickOption");
1397
1399
  }
1398
1400
  },
1399
1401
  children: label
@@ -1428,9 +1430,9 @@ function DatePicker(props) {
1428
1430
  setDirty(void 0);
1429
1431
  }
1430
1432
  }, [rangeSelect]);
1431
- function set(min2, max2, edit) {
1433
+ function set(source, min2, max2, edit) {
1432
1434
  if (!rangeSelect) {
1433
- onChange(min2 != null ? min2 : null);
1435
+ onChange(min2 != null ? min2 : null, source);
1434
1436
  return;
1435
1437
  }
1436
1438
  if (min2 && max2 && min2 > max2) {
@@ -1444,7 +1446,7 @@ function DatePicker(props) {
1444
1446
  }
1445
1447
  if (!min2 === !max2) {
1446
1448
  setDirty(void 0);
1447
- onChange(min2 && max2 ? { min: min2, max: endOfDay(max2) } : null);
1449
+ onChange(min2 && max2 ? { min: min2, max: endOfDay(max2) } : null, source);
1448
1450
  } else {
1449
1451
  setDirty({ min: min2, max: max2 });
1450
1452
  }
@@ -1482,7 +1484,7 @@ function DatePicker(props) {
1482
1484
  DateInput,
1483
1485
  {
1484
1486
  value: min != null ? min : null,
1485
- onChange: (date) => set(date != null ? date : void 0, max, "min"),
1487
+ onChange: (date) => set("input", date != null ? date : void 0, max, "min"),
1486
1488
  locale
1487
1489
  }
1488
1490
  ),
@@ -1492,7 +1494,7 @@ function DatePicker(props) {
1492
1494
  DateInput,
1493
1495
  {
1494
1496
  value: max != null ? max : null,
1495
- onChange: (date) => set(min, date != null ? date : void 0, "max"),
1497
+ onChange: (date) => set("input", min, date != null ? date : void 0, "max"),
1496
1498
  locale
1497
1499
  }
1498
1500
  )
@@ -1577,7 +1579,7 @@ function DatePicker(props) {
1577
1579
  }
1578
1580
  const min2 = minDate && weekStart.date < minDate ? minDate : weekStart.date;
1579
1581
  const max2 = maxDate && weekEnd.date > maxDate ? maxDate : weekEnd.date;
1580
- set(min2, max2);
1582
+ set("calendarWeek", min2, max2);
1581
1583
  },
1582
1584
  onPointerOver: () => !weekDisabled && setHovered({ min: weekStart.date, max: weekEnd.date }),
1583
1585
  onPointerOut: () => !weekDisabled && setHovered(void 0),
@@ -1613,10 +1615,10 @@ function DatePicker(props) {
1613
1615
  }, getDateProps({ dateObj: dateObject })), {
1614
1616
  onClick: () => {
1615
1617
  if (dirty) {
1616
- if (min) set(min, date);
1617
- else set(date, max);
1618
+ if (min) set("calendar", min, date);
1619
+ else set("calendar", date, max);
1618
1620
  } else {
1619
- set(date);
1621
+ set("calendar", date);
1620
1622
  }
1621
1623
  },
1622
1624
  onPointerOver: () => setHovered({ min: date, max: date }),