@synerise/ds-date-picker 2.0.3 → 2.1.1

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/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.1.1](https://github.com/Synerise/synerise-design/compare/@synerise/ds-date-picker@2.1.0...@synerise/ds-date-picker@2.1.1) (2026-09-16)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **date-picker:** keep the local clock when a day click crosses a DST boundary ([81fb588](https://github.com/Synerise/synerise-design/commit/81fb5883286faa1a938d3ec5909a1e48a6cb68b7))
11
+
12
+ # [2.1.0](https://github.com/Synerise/synerise-design/compare/@synerise/ds-date-picker@2.0.3...@synerise/ds-date-picker@2.1.0) (2026-09-10)
13
+
14
+ ### Features
15
+
16
+ - **global:** move the pickers to date-fns 4 ([f878075](https://github.com/Synerise/synerise-design/commit/f878075940c2cd0d2d8a4a5603eb18d01f837640))
17
+ - new month, year and decade picker views, side testids, no title tooltip ([e00f29b](https://github.com/Synerise/synerise-design/commit/e00f29bfc16795bc0ceafa97746c99da1bf3ed9c))
18
+
6
19
  ## [2.0.3](https://github.com/Synerise/synerise-design/compare/@synerise/ds-date-picker@2.0.2...@synerise/ds-date-picker@2.0.3) (2026-09-08)
7
20
 
8
21
  **Note:** Version bump only for package @synerise/ds-date-picker
package/CLAUDE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DatePicker (`@synerise/ds-date-picker`)
2
2
 
3
- > Single-date picker with a dropdown calendar, optional time picker, quick-pick presets, and a formatted text input trigger — built on `react-day-picker` v10 and `date-fns` 2.
3
+ > Single-date picker with a dropdown calendar, optional time picker, quick-pick presets, and a formatted text input trigger — built on `react-day-picker` v10 and `date-fns` 4.
4
4
 
5
5
  ## Package structure
6
6
 
@@ -16,8 +16,8 @@ src/
16
16
  Elements/
17
17
  DayPicker/ — calendar grid (react-day-picker wrapper)
18
18
  MonthPicker/ — 12-month grid; clicking title switches to YearPicker
19
- YearPicker/ — decade grid; clicking title switches to DecadePicker
20
- DecadePicker/ — century grid
19
+ YearPicker/ — the decade's ten years; clicking title switches to DecadePicker
20
+ DecadePicker/ — century grid, plus a step cell either side
21
21
  TimePicker/ — HH:MM:SS column selector with prev/next day navigation
22
22
  PickerInput/ — formatted text input trigger (uses ds-input)
23
23
  Footer/ — Apply / Now / Date / Time mode-switch buttons
@@ -27,10 +27,10 @@ src/
27
27
  GridPicker/ — shared grid layout used by Month/Year/DecadePicker
28
28
  utils/
29
29
  getDefaultTexts.tsx — merges consumer texts with react-intl defaults
30
- fns.tsx — date-fns v2 wrappers
30
+ fns.tsx — date-fns re-exports (named, from the package root)
31
31
  format.ts — fnsFormat wrapper
32
32
  localeUtils.ts — weekday/month name tables; adapted into react-day-picker formatters/labels
33
- utils.ts — changeDayWithHoursPreserved and other helpers
33
+ utils.ts — range / getDecadeRange / getCenturyRange helpers
34
34
  index.ts — public exports
35
35
  ```
36
36
 
@@ -60,7 +60,6 @@ src/
60
60
  | `quickPicks` | `QuickPick[]` | `undefined` | Preset date buttons rendered in a left column beside the calendar. |
61
61
  | `texts` | `Partial<Texts>` | `undefined` | Override i18n strings. |
62
62
  | `valueFormatOptions` | `DateToFormatOptions` | `undefined` | Format options for displaying the selected date in the input. |
63
- | `format` | `string` | `undefined` | **Deprecated** — use `valueFormatOptions` instead. |
64
63
  | `popoverPlacement` | `'topLeft' \| 'topCenter' \| 'topRight' \| 'bottomLeft' \| 'bottomCenter' \| 'bottomRight'` | `undefined` | Dropdown placement. |
65
64
  | `prefixel` | `ReactNode` | `undefined` | Content shown before the date value in the input. |
66
65
  | `suffixel` | `ReactNode` | `undefined` | Content shown after the date value in the input. |
@@ -127,9 +126,9 @@ import { RawDatePicker } from '@synerise/ds-date-picker';
127
126
 
128
127
  ## Key dependencies
129
128
 
130
- - `react-day-picker` v10 — calendar grid and day modifiers (brings its own `date-fns` 4 + `@date-fns/tz`, isolated from this package's `date-fns` 2)
131
- - `date-fns` 2.16.1 — all date arithmetic (pinned version)
132
- - `@date-fns/upgrade` — `legacyParse` compat shim for date-fns v1→v2 migration in `RawDatePicker`
129
+ - `react-day-picker` v10 — calendar grid and day modifiers (shares this package's `date-fns` 4, plus `@date-fns/tz`)
130
+ - `date-fns` ^4 — all date arithmetic, imported by name from the package root
131
+ - Loosely-typed date inputs are normalised by `src/toDateValue.ts`, which replaced `@date-fns/upgrade`'s `legacyParse`. Use it rather than `new Date` or `toDate`: a naive string must be read as *local* time, which only `parseISO` does
133
132
  - `@synerise/ds-dropdown` — wraps `RawDatePicker` as a popover triggered by `PickerInput`
134
133
  - `@synerise/ds-input` — base for `PickerInput`
135
134
  - `react-intl` (peer dep) — default label strings; `IntlProvider` required in tree
@@ -141,10 +140,10 @@ import { RawDatePicker } from '@synerise/ds-date-picker';
141
140
  - **`readOnly` short-circuits the Dropdown** — when `readOnly={true}`, the component renders only the `PickerInput` (or `renderTrigger` output) with no calendar attached.
142
141
  - **Mode switching** — clicking the month name in `DayPicker` switches to `'month'` mode; clicking the year name switches to `'year'` mode. The back navigation in those modes returns to `'date'`. Selecting a day when `showTime=true` switches to `'time'` mode automatically.
143
142
  - **`useStartOfDay` / `useEndOfDay`** — applied on day click, before `onValueChange` fires. `useStartOfDay` takes precedence if both are set (implemented as `if/else if` in `handleDayClick`).
144
- - **Time preserved on day change** — when changing the day while already having a time selected (`changed=true`), hours/minutes/seconds from the previous value are preserved (`changeDayWithHoursPreserved`).
143
+ - **Time preserved on day change** — clicking a day rewrites only the calendar fields of the current `value` (`setYear`/`setMonth`/`setDate`), so its local clock carries over. Local fields are the whole mechanism, and they are what makes this correct across a DST boundary; do not reintroduce a seconds-difference round-trip here. The removed `changeDayWithHoursPreserved` did exactly that and multiplied a calendar-day count by a hard-coded `86400`, so any pair of dates straddling a transition came out an hour wrong — and with `useStartOfDay`/`useEndOfDay` that hour crossed midnight and moved the day.
145
144
  - **Apply vs onChange** — `onValueChange` fires on every day/time interaction. `onApply` fires only when the user explicitly clicks Apply (or selects a quick pick), and also closes the dropdown.
146
145
  - **`dropdownProps.open`** — if provided, it OR-s with internal `dropVisible` state: `open={(dropdownProps?.open || dropVisible) && !disabled}`. This means both sources can open the dropdown independently.
147
- - **`format` is deprecated** — use `valueFormatOptions: DateToFormatOptions` (from `@synerise/ds-core`) for display formatting.
146
+ - **`format` has been removed** — use `valueFormatOptions: DateToFormatOptions` (from `@synerise/ds-core`). It took a token pattern, which no consumer passed and which disagreed with `formatValue` for every locale. `PickerInput` now renders a `Date` and an ISO string identically; whether a value is an *instant* is signalled by `isInstantValue`, not inferred from its runtime type.
148
147
  - **`inputPlaceholder` i18n default is empty** — `getDefaultTexts` calls `intl.formatMessage({ id: 'DS.DATE-PICKER.SELECT-DATE' })` with no `defaultMessage`, so the placeholder is empty if the message is not in the IntlProvider's messages.
149
148
 
150
149
  ## react-day-picker v10 notes
@@ -168,3 +167,22 @@ import { RawDatePicker } from '@synerise/ds-date-picker';
168
167
  - **v10 renders twice on mount**, so `disabledDates` is evaluated twice per day. It is a pure
169
168
  predicate, so this is a cost rather than a correctness issue; assert on distinct dates, not on
170
169
  call counts.
170
+
171
+ ## Month, year and decade grids
172
+
173
+ The three grid views share `Elements/GridPicker` and follow the Figma `Date Picker` states
174
+ (`Months`, `Year`, `Years range`):
175
+
176
+ - **Every reachable cell carries a chip** — `grey-100` at rest, `grey-200` + `blue-600` text on
177
+ hover, `blue-600` + white when selected, at regular weight. A **disabled** cell drops the chip
178
+ and keeps only a `grey-400` label, matching the day grid, where the chip likewise marks a day as
179
+ reachable.
180
+ - **Layout** is three columns on a 24px inset with an 8px column gap and 32px rows spread by
181
+ `align-content: space-between`, so the same rules give Figma's spacing in `DatePicker`'s 304px
182
+ panel and fit `DateRangePicker`'s 290px side.
183
+ - **`YearPicker` renders the decade's ten years and nothing more.** The `±1 decade` cells it used
184
+ to append are gone — the navbar's chevrons already step by ten years. `DecadePicker` keeps its
185
+ two `cell--outside` cells because they are the only way to leave the century, and the design
186
+ draws them like any other cell.
187
+ - **`Navbar` takes `singleStep`** for these views: one step per side, drawn as a single angle in
188
+ the outer slot the day view's double angle occupies, with the inner slot left out.
@@ -1,4 +1,4 @@
1
1
  import { default as React } from 'react';
2
2
  import { DatePickerProps } from './DatePicker.types';
3
- declare const DatePicker: ({ autoFocus, disabled, texts, format, valueFormatOptions, value, onApply, showTime, onValueChange, onClear, errorText, popoverPlacement, prefixel, error, onDropdownVisibleChange, dropdownProps, suffixel, hideNow, readOnly, renderTrigger, inputProps, allowClear, overlayHTMLAttributes, triggerHTMLAttributes, ...rest }: DatePickerProps) => React.JSX.Element;
3
+ declare const DatePicker: ({ autoFocus, disabled, texts, valueFormatOptions, value, onApply, showTime, onValueChange, onClear, errorText, popoverPlacement, prefixel, error, onDropdownVisibleChange, dropdownProps, suffixel, hideNow, readOnly, renderTrigger, inputProps, allowClear, overlayHTMLAttributes, triggerHTMLAttributes, ...rest }: DatePickerProps) => React.JSX.Element;
4
4
  export default DatePicker;
@@ -11,7 +11,6 @@ const DatePicker = ({
11
11
  autoFocus,
12
12
  disabled,
13
13
  texts,
14
- format,
15
14
  valueFormatOptions,
16
15
  value,
17
16
  onApply,
@@ -61,7 +60,7 @@ const DatePicker = ({
61
60
  }, [onClear]);
62
61
  const trigger = renderTrigger?.() || /* @__PURE__ */ jsx(PickerInput, { ...inputProps, disabled, allowClear, autoFocus: !disabled && autoFocus, value: selectedDate, showTime, onClick: !readOnly ? () => {
63
62
  setDropVisible(!dropVisible);
64
- } : void 0, format, valueFormatOptions, onClear: handleClear, placeholder: allTexts.inputPlaceholder, prefixel, suffixel, clearTooltip: allTexts.clearTooltip, highlight: !!dropVisible && !disabled, error, errorText, readOnly, triggerHTMLAttributes });
63
+ } : void 0, valueFormatOptions, onClear: handleClear, placeholder: allTexts.inputPlaceholder, prefixel, suffixel, clearTooltip: allTexts.clearTooltip, highlight: !!dropVisible && !disabled, error, errorText, readOnly, triggerHTMLAttributes });
65
64
  return readOnly ? trigger : /* @__PURE__ */ jsx(Dropdown, { overlay: /* @__PURE__ */ jsx(OverlayContainer, { "data-testid": "date-picker-overlay-container", ...overlayHTMLAttributes, children: /* @__PURE__ */ jsx(RawDatePicker, { ...rest, showTime, texts: allTexts, onApply: onApplyCallback, onValueChange: onValueChangeCallback, value: selectedDate, hideNow }) }), trigger: ["click"], placement: popoverPlacement, disabled, size: "min-match-trigger", asChild: false, ...dropdownProps, onOpenChange: handleOpenChange, open: (dropdownProps?.open || dropVisible) && !disabled, popoverProps: {
66
65
  testId: "date-picker",
67
66
  ...dropdownProps?.popoverProps
@@ -16,10 +16,6 @@ export type DatePickerProps = {
16
16
  disabledMinutes?: number[];
17
17
  disabledSeconds?: number[];
18
18
  dropdownProps?: Partial<Omit<DropdownSharedProps, 'children' | 'overlay'>>;
19
- /**
20
- * @deprecated use `valueFormatOptions` instead
21
- */
22
- format?: string;
23
19
  valueFormatOptions?: DateToFormatOptions;
24
20
  /**
25
21
  * @deprecated - will be dropped in 1.0
@@ -3,28 +3,7 @@ import React from "react";
3
3
  import { SelectionState, DayFlag, UI } from "react-day-picker";
4
4
  import { injectIntl } from "react-intl";
5
5
  import { useDataFormat } from "@synerise/ds-core";
6
- import "date-fns/addDays";
7
- import fnsAddMonths from "date-fns/addMonths";
8
- import "date-fns/addSeconds";
9
- import fnsAddYears from "date-fns/addYears";
10
- import "date-fns/differenceInSeconds";
11
- import "date-fns/differenceInYears";
12
- import "date-fns/endOfDay";
13
- import "date-fns/endOfMonth";
14
- import "date-fns/format";
15
- import "date-fns/getSeconds";
16
- import "date-fns/getYear";
17
- import "date-fns/isAfter";
18
- import "date-fns/isBefore";
19
- import "date-fns/isEqual";
20
- import "date-fns/isSameDay";
21
- import "date-fns/isSameMonth";
22
- import "date-fns/isSameYear";
23
- import "date-fns/setDate";
24
- import "date-fns/setMonth";
25
- import "date-fns/setYear";
26
- import "date-fns/startOfDay";
27
- import "date-fns/startOfMonth";
6
+ import { addMonths, addYears } from "date-fns";
28
7
  import fnsFormat from "../../format.js";
29
8
  import localeUtils from "../../localeUtils.js";
30
9
  import Navbar from "../Navbar/Navbar.js";
@@ -102,7 +81,7 @@ const Picker = (props) => {
102
81
  /* @__PURE__ */ jsx(Link, { "data-testid": "datapicker-nav-title-monthpicker-link", onClick: onMonthNameClick, children: fnsFormat(month, "MMM", intl.locale) }),
103
82
  " ",
104
83
  /* @__PURE__ */ jsx(Link, { "data-testid": "datapicker-nav-title-yearpicker-link", onClick: onYearNameClick, children: fnsFormat(month, "yyyy", intl.locale) })
105
- ] }), onLongPrev: hideLongPrev ? void 0 : () => onMonthChange && onMonthChange(fnsAddYears(month, -1)), onShortPrev: hideShortPrev ? void 0 : () => onMonthChange && onMonthChange(fnsAddMonths(month, -1)), onLongNext: hideLongNext ? void 0 : () => onMonthChange && onMonthChange(fnsAddYears(month, 1)), onShortNext: hideShortNext ? void 0 : () => onMonthChange && onMonthChange(fnsAddMonths(month, 1)) }, "head"),
84
+ ] }), onLongPrev: hideLongPrev ? void 0 : () => onMonthChange && onMonthChange(addYears(month, -1)), onShortPrev: hideShortPrev ? void 0 : () => onMonthChange && onMonthChange(addMonths(month, -1)), onLongNext: hideLongNext ? void 0 : () => onMonthChange && onMonthChange(addYears(month, 1)), onShortNext: hideShortNext ? void 0 : () => onMonthChange && onMonthChange(addMonths(month, 1)) }, "head"),
106
85
  /* @__PURE__ */ jsx(
107
86
  DayPicker$1,
108
87
  {
@@ -28,7 +28,6 @@ export type DayPickerProps = {
28
28
  /** Day-of-month predicates or dates keyed by modifier name; each becomes a `DayPicker-Day--*` class. */
29
29
  modifiers?: Record<string, Matcher | Matcher[] | undefined>;
30
30
  localeUtils?: DateLocaleUtils;
31
- title?: string;
32
31
  renderDay?: (day: Date) => React.ReactNode;
33
32
  renderNavbar?: (props: DayPickerProps) => React.ReactNode;
34
33
  onDayClick?: (day: Date, modifiers: Modifiers, e: React.MouseEvent) => void;
@@ -1,29 +1,8 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import range from "lodash.range";
3
3
  import React from "react";
4
- import { legacyParse } from "@date-fns/upgrade/v2";
5
- import "date-fns/addDays";
6
- import "date-fns/addMonths";
7
- import "date-fns/addSeconds";
8
- import fnsAddYears from "date-fns/addYears";
9
- import "date-fns/differenceInSeconds";
10
- import "date-fns/differenceInYears";
11
- import "date-fns/endOfDay";
12
- import "date-fns/endOfMonth";
13
- import "date-fns/format";
14
- import "date-fns/getSeconds";
15
- import fnsGetYear from "date-fns/getYear";
16
- import "date-fns/isAfter";
17
- import "date-fns/isBefore";
18
- import "date-fns/isEqual";
19
- import "date-fns/isSameDay";
20
- import "date-fns/isSameMonth";
21
- import "date-fns/isSameYear";
22
- import "date-fns/setDate";
23
- import "date-fns/setMonth";
24
- import fnsSetYear from "date-fns/setYear";
25
- import "date-fns/startOfDay";
26
- import "date-fns/startOfMonth";
4
+ import { addYears, getYear, setYear } from "date-fns";
5
+ import toDateValue from "../../toDateValue.js";
27
6
  import { getCenturyRange, getDecadeRange } from "../../utils.js";
28
7
  import GridPicker from "../GridPicker/GridPicker.js";
29
8
  import Navbar from "../Navbar/Navbar.js";
@@ -35,7 +14,7 @@ function getInitialState(props) {
35
14
  function getCells(cursor) {
36
15
  const startYear = getCenturyRange(cursor)[0];
37
16
  return range(0, 10).map((index) => {
38
- const date = fnsAddYears(fnsSetYear(cursor, startYear), index * 10);
17
+ const date = addYears(setYear(cursor, startYear), index * 10);
39
18
  return {
40
19
  key: date.toISOString(),
41
20
  text: getDecadeRange(date).join("-")
@@ -51,7 +30,7 @@ class DecadePicker extends React.PureComponent {
51
30
  cursor
52
31
  } = this.state;
53
32
  this.setState({
54
- cursor: fnsAddYears(cursor, -100)
33
+ cursor: addYears(cursor, -100)
55
34
  });
56
35
  };
57
36
  this.handleLongNext = () => {
@@ -59,7 +38,7 @@ class DecadePicker extends React.PureComponent {
59
38
  cursor
60
39
  } = this.state;
61
40
  this.setState({
62
- cursor: fnsAddYears(cursor, 100)
41
+ cursor: addYears(cursor, 100)
63
42
  });
64
43
  };
65
44
  this.handleCellClick = (isoDate) => {
@@ -97,8 +76,8 @@ class DecadePicker extends React.PureComponent {
97
76
  const centuryRange = getCenturyRange(cursor);
98
77
  let cells = getCells(cursor);
99
78
  const valueCell = value ? cells.find((cell) => {
100
- const valueYear = fnsGetYear(value);
101
- const minYear = fnsGetYear(legacyParse(cell.key));
79
+ const valueYear = getYear(value);
80
+ const minYear = getYear(toDateValue(cell.key));
102
81
  const maxYear = minYear + 10;
103
82
  return valueYear >= minYear && valueYear < maxYear;
104
83
  }) : null;
@@ -112,7 +91,7 @@ class DecadePicker extends React.PureComponent {
112
91
  text: `${centuryRange[1] + 1}-${centuryRange[1] + 10}`,
113
92
  outside: true
114
93
  }];
115
- return [/* @__PURE__ */ jsx(Navbar, { title: centuryRange.join("-"), onTitleClick, onLongPrev: this.handleLongPrev, onLongNext: this.handleLongNext }, "head"), /* @__PURE__ */ jsx(GridPicker, { selectedKey, cells, onCellClick: this.handleCellClick }, "body")];
94
+ return [/* @__PURE__ */ jsx(Navbar, { singleStep: true, title: centuryRange.join("-"), onTitleClick, onLongPrev: this.handleLongPrev, onLongNext: this.handleLongNext }, "head"), /* @__PURE__ */ jsx(GridPicker, { selectedKey, cells, onCellClick: this.handleCellClick }, "body")];
116
95
  }
117
96
  }
118
97
  export {
@@ -1,2 +1,10 @@
1
- export declare const GridContainer: import('styled-components').StyledComponent<"div", any, {}, never>;
2
1
  export declare const CellContainer: import('styled-components').StyledComponent<"div", any, {}, never>;
2
+ /**
3
+ * The month, year and decade grids: three columns of chips, laid out on the same 24px inset and
4
+ * 8px column gap the day grid uses, with the rows spread over whatever height the host gives the
5
+ * grid (304px in `DatePicker`, 290px in `DateRangePicker`'s side).
6
+ *
7
+ * Every cell carries a resting chip, so an empty trailing slot — the year grid holds ten years in
8
+ * a twelve-slot grid — is simply an absent cell rather than a blank chip.
9
+ */
10
+ export declare const GridContainer: import('styled-components').StyledComponent<"div", any, {}, never>;
@@ -1,12 +1,12 @@
1
1
  import styled from "styled-components";
2
- const GridContainer = /* @__PURE__ */ styled.div.withConfig({
3
- displayName: "GridPickerstyles__GridContainer",
4
- componentId: "sc-i8w1ym-0"
5
- })(["display:grid;grid-template-columns:1fr 1fr 1fr;font-size:12px;padding:0 8px;.cell{height:32px;margin:auto 8px;cursor:pointer;position:relative;vertical-align:middle;text-align:center;> div{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;border-radius:16px;&:hover{background-color:", ";color:", ";}}&--selected{font-weight:500;}&--outside{color:", ";}&--disabled{cursor:default;color:", ";}}.cell--selected:not(.cell--disabled):not(.cell--outside){> div{background-color:", ";color:", ";&:hover{background-color:", ";}}}"], (props) => props.theme.palette["grey-050"], (props) => props.theme.palette["blue-600"], (props) => props.theme.palette["grey-400"], (props) => props.theme.palette["grey-400"], (props) => props.theme.palette["blue-600"], (props) => props.theme.palette.white, (props) => props.theme.palette["blue-600"]);
6
2
  const CellContainer = /* @__PURE__ */ styled.div.withConfig({
7
3
  displayName: "GridPickerstyles__CellContainer",
8
- componentId: "sc-i8w1ym-1"
4
+ componentId: "sc-i8w1ym-0"
9
5
  })([""]);
6
+ const GridContainer = /* @__PURE__ */ styled.div.withConfig({
7
+ displayName: "GridPickerstyles__GridContainer",
8
+ componentId: "sc-i8w1ym-1"
9
+ })(["display:grid;grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:32px;align-content:space-between;column-gap:8px;padding:24px;font-size:13px;line-height:18px;.cell{cursor:pointer;> div{height:100%;display:flex;align-items:center;justify-content:center;border-radius:60px;background-color:", ";color:", ";&:hover{background-color:", ";color:", ";}}&--disabled{cursor:default;> div,> div:hover{background-color:transparent;color:", ";}}}.cell--selected:not(.cell--disabled){> div,> div:hover{background-color:", ";color:", ";}}"], (props) => props.theme.palette["grey-100"], (props) => props.theme.palette["grey-800"], (props) => props.theme.palette["grey-200"], (props) => props.theme.palette["blue-600"], (props) => props.theme.palette["grey-400"], (props) => props.theme.palette["blue-600"], (props) => props.theme.palette.white);
10
10
  export {
11
11
  CellContainer,
12
12
  GridContainer
@@ -2,30 +2,9 @@ import { jsx } from "react/jsx-runtime";
2
2
  import range from "lodash.range";
3
3
  import React from "react";
4
4
  import { injectIntl } from "react-intl";
5
- import { legacyParse } from "@date-fns/upgrade/v2";
6
- import "date-fns/addDays";
7
- import "date-fns/addMonths";
8
- import "date-fns/addSeconds";
9
- import fnsAddYears from "date-fns/addYears";
10
- import "date-fns/differenceInSeconds";
11
- import "date-fns/differenceInYears";
12
- import "date-fns/endOfDay";
13
- import fnsEndOfMonth from "date-fns/endOfMonth";
14
- import "date-fns/format";
15
- import "date-fns/getSeconds";
16
- import "date-fns/getYear";
17
- import fnsIsAfter from "date-fns/isAfter";
18
- import fnsIsBefore from "date-fns/isBefore";
19
- import "date-fns/isEqual";
20
- import "date-fns/isSameDay";
21
- import fnsIsSameMonth from "date-fns/isSameMonth";
22
- import "date-fns/isSameYear";
23
- import "date-fns/setDate";
24
- import fnsSetMonth from "date-fns/setMonth";
25
- import "date-fns/setYear";
26
- import "date-fns/startOfDay";
27
- import fnsStartOfMonth from "date-fns/startOfMonth";
5
+ import { addYears, isSameMonth, startOfMonth, setMonth, endOfMonth, isAfter, isBefore } from "date-fns";
28
6
  import fnsFormat from "../../format.js";
7
+ import toDateValue from "../../toDateValue.js";
29
8
  import GridPicker from "../GridPicker/GridPicker.js";
30
9
  import Navbar from "../Navbar/Navbar.js";
31
10
  import YearPicker from "../YearPicker/YearPicker.js";
@@ -36,14 +15,14 @@ function getInitialState(props) {
36
15
  };
37
16
  }
38
17
  function getCells(cursor, min, max, locale) {
39
- const minDate = min ? fnsStartOfMonth(min) : fnsSetMonth(cursor, 0);
40
- const maxDate = max ? fnsEndOfMonth(max) : fnsSetMonth(cursor, 12);
18
+ const minDate = min ? startOfMonth(min) : setMonth(cursor, 0);
19
+ const maxDate = max ? endOfMonth(max) : setMonth(cursor, 12);
41
20
  return range(0, 12).map((index) => {
42
- const date = fnsSetMonth(cursor, index);
21
+ const date = setMonth(cursor, index);
43
22
  return {
44
23
  key: date.toISOString(),
45
24
  text: fnsFormat(date, "MMM", locale),
46
- disabled: fnsIsAfter(date, maxDate) || fnsIsBefore(date, minDate)
25
+ disabled: isAfter(date, maxDate) || isBefore(date, minDate)
47
26
  };
48
27
  });
49
28
  }
@@ -56,7 +35,7 @@ class MonthPicker extends React.PureComponent {
56
35
  cursor
57
36
  } = this.state;
58
37
  this.setState({
59
- cursor: fnsAddYears(cursor, -1)
38
+ cursor: addYears(cursor, -1)
60
39
  });
61
40
  };
62
41
  this.handleLongNext = () => {
@@ -64,7 +43,7 @@ class MonthPicker extends React.PureComponent {
64
43
  cursor
65
44
  } = this.state;
66
45
  this.setState({
67
- cursor: fnsAddYears(cursor, 1)
46
+ cursor: addYears(cursor, 1)
68
47
  });
69
48
  };
70
49
  }
@@ -90,7 +69,7 @@ class MonthPicker extends React.PureComponent {
90
69
  onChange
91
70
  } = this.props;
92
71
  const cells = getCells(cursor, min, max, intl?.locale);
93
- const valueCell = value ? cells.find((cell) => fnsIsSameMonth(value, legacyParse(cell.key))) : null;
72
+ const valueCell = value ? cells.find((cell) => isSameMonth(value, toDateValue(cell.key))) : null;
94
73
  const selectedKey = valueCell ? valueCell.key : null;
95
74
  if (yearMode) {
96
75
  return /* @__PURE__ */ jsx(YearPicker, { value: cursor, onChange: (changedDate) => this.setState({
@@ -98,7 +77,7 @@ class MonthPicker extends React.PureComponent {
98
77
  yearMode: false
99
78
  }) });
100
79
  }
101
- return [/* @__PURE__ */ jsx(Navbar, { onTitleClick: () => this.setState({
80
+ return [/* @__PURE__ */ jsx(Navbar, { singleStep: true, onTitleClick: () => this.setState({
102
81
  yearMode: true
103
82
  }), title: fnsFormat(cursor, "yyyy"), onLongPrev: this.handleLongPrev, onLongNext: this.handleLongNext }, "head"), /* @__PURE__ */ jsx(GridPicker, { selectedKey, cells, onCellClick: (isoDate) => onChange(new Date(isoDate)) }, "body")];
104
83
  }
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { v4 } from "uuid";
3
- import Icon, { DoubleAngleLeftS, AngleLeftS, AngleRightS, DoubleAngleRightS } from "@synerise/ds-icon";
3
+ import Icon, { AngleLeftS, DoubleAngleLeftS, AngleRightS, DoubleAngleRightS } from "@synerise/ds-icon";
4
4
  import { Container, ArrowContainer, NavButton, ArrowPlaceholder, Text, Link } from "./Navbar.styles.js";
5
5
  const Navbar = (props) => {
6
6
  const {
@@ -13,17 +13,18 @@ const Navbar = (props) => {
13
13
  onShortPrev,
14
14
  onShortNext,
15
15
  inactivePrev,
16
- inactiveNext
16
+ inactiveNext,
17
+ singleStep
17
18
  } = props;
18
19
  return /* @__PURE__ */ jsxs(Container, { className: "ds-date-picker-nav", children: [
19
20
  /* @__PURE__ */ jsxs(ArrowContainer, { children: [
20
- onLongPrev && !hidePrev ? /* @__PURE__ */ jsx(NavButton, { "data-testid": "datapicker-long-prev", disabled: inactivePrev, mode: "single-icon", role: "button", type: "ghost", onClick: onLongPrev, children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(DoubleAngleLeftS, {}) }) }, v4()) : /* @__PURE__ */ jsx(ArrowPlaceholder, { className: "arrow-placeholder long-prev" }),
21
- onShortPrev && !hidePrev ? /* @__PURE__ */ jsx(NavButton, { "data-testid": "datapicker-short-prev", disabled: inactivePrev, mode: "single-icon", role: "button", type: "ghost", onClick: onShortPrev, children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(AngleLeftS, {}) }) }, v4()) : /* @__PURE__ */ jsx(ArrowPlaceholder, { className: "arrow-placeholder short-prev" })
21
+ onLongPrev && !hidePrev ? /* @__PURE__ */ jsx(NavButton, { "data-testid": "datapicker-long-prev", disabled: inactivePrev, mode: "single-icon", role: "button", type: "ghost", onClick: onLongPrev, children: /* @__PURE__ */ jsx(Icon, { component: singleStep ? /* @__PURE__ */ jsx(AngleLeftS, {}) : /* @__PURE__ */ jsx(DoubleAngleLeftS, {}) }) }, v4()) : /* @__PURE__ */ jsx(ArrowPlaceholder, { className: "arrow-placeholder long-prev" }),
22
+ !singleStep && (onShortPrev && !hidePrev ? /* @__PURE__ */ jsx(NavButton, { "data-testid": "datapicker-short-prev", disabled: inactivePrev, mode: "single-icon", role: "button", type: "ghost", onClick: onShortPrev, children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(AngleLeftS, {}) }) }, v4()) : /* @__PURE__ */ jsx(ArrowPlaceholder, { className: "arrow-placeholder short-prev" }))
22
23
  ] }),
23
24
  /* @__PURE__ */ jsx(Text, { children: onTitleClick ? /* @__PURE__ */ jsx(Link, { onClick: onTitleClick, children: title }) : title }),
24
25
  /* @__PURE__ */ jsxs(ArrowContainer, { children: [
25
- onShortNext && !hideNext ? /* @__PURE__ */ jsx(NavButton, { "data-testid": "datapicker-short-next", disabled: inactiveNext, mode: "single-icon", role: "button", type: "ghost", onClick: onShortNext, children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(AngleRightS, {}) }) }, v4()) : /* @__PURE__ */ jsx(ArrowPlaceholder, { className: "arrow-placeholder short-next" }),
26
- onLongNext && !hideNext ? /* @__PURE__ */ jsx(NavButton, { "data-testid": "datapicker-long-next", disabled: inactiveNext, mode: "single-icon", role: "button", type: "ghost", onClick: onLongNext, children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(DoubleAngleRightS, {}) }) }, v4()) : /* @__PURE__ */ jsx(ArrowPlaceholder, { className: "arrow-placeholder long-next" })
26
+ !singleStep && (onShortNext && !hideNext ? /* @__PURE__ */ jsx(NavButton, { "data-testid": "datapicker-short-next", disabled: inactiveNext, mode: "single-icon", role: "button", type: "ghost", onClick: onShortNext, children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(AngleRightS, {}) }) }, v4()) : /* @__PURE__ */ jsx(ArrowPlaceholder, { className: "arrow-placeholder short-next" })),
27
+ onLongNext && !hideNext ? /* @__PURE__ */ jsx(NavButton, { "data-testid": "datapicker-long-next", disabled: inactiveNext, mode: "single-icon", role: "button", type: "ghost", onClick: onLongNext, children: /* @__PURE__ */ jsx(Icon, { component: singleStep ? /* @__PURE__ */ jsx(AngleRightS, {}) : /* @__PURE__ */ jsx(DoubleAngleRightS, {}) }) }, v4()) : /* @__PURE__ */ jsx(ArrowPlaceholder, { className: "arrow-placeholder long-next" })
27
28
  ] })
28
29
  ] });
29
30
  };
@@ -8,6 +8,12 @@ export type NavbarProps = {
8
8
  onTitleClick?: () => void;
9
9
  onLongPrev?: () => void;
10
10
  onLongNext?: () => void;
11
+ /**
12
+ * One step per side instead of two: the month, year and decade grids move by a single unit,
13
+ * so the step keeps the outer position the double angle holds in the day view but is drawn as
14
+ * a single angle, and the unused inner slot is left out rather than held open by a placeholder.
15
+ */
16
+ singleStep?: boolean;
11
17
  onShortPrev?: () => void;
12
18
  onShortNext?: () => void;
13
19
  };
@@ -2,7 +2,6 @@ import { default as React } from 'react';
2
2
  declare const PickerInput: React.ForwardRefExoticComponent<Pick<import('@synerise/ds-input').InputProps, "autoResize"> & {
3
3
  autoFocus?: boolean;
4
4
  size?: import('@synerise/ds-input').InputSize;
5
- format?: string;
6
5
  valueFormatOptions?: import('@synerise/ds-core').DateToFormatOptions;
7
6
  showTime?: boolean;
8
7
  allowClear?: boolean;
@@ -1,16 +1,14 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { parseISO, isValid } from "date-fns";
2
3
  import { forwardRef, useState, useCallback, useMemo } from "react";
3
- import { legacyParse } from "@date-fns/upgrade/v2";
4
4
  import { useDataFormat, useTheme, getDefaultDataTimeOptions } from "@synerise/ds-core";
5
5
  import Icon, { Close3S, CalendarM } from "@synerise/ds-icon";
6
6
  import Tooltip from "@synerise/ds-tooltip";
7
- import fnsFormat from "../../format.js";
8
7
  import { ClearIconWrapper, DefaultIconWrapper, PickerInputWrapper, Prefixel, Container, Input, Suffixel } from "./PickerInput.styles.js";
9
8
  const PickerInput = forwardRef(({
10
9
  disabled,
11
10
  readOnly,
12
11
  value,
13
- format: dateFormat,
14
12
  valueFormatOptions,
15
13
  onChange,
16
14
  showTime,
@@ -33,17 +31,15 @@ const PickerInput = forwardRef(({
33
31
  if (!value) {
34
32
  return "";
35
33
  }
36
- if (dateFormat) {
37
- return fnsFormat(legacyParse(value), dateFormat);
38
- }
39
- if (typeof value === "string") {
40
- return fnsFormat(legacyParse(value), dateFormat || showTime ? "MMM d, yyyy, HH:mm" : "MMM d, yyyy");
34
+ const date = typeof value === "string" ? parseISO(value) : value;
35
+ if (!isValid(date)) {
36
+ return "";
41
37
  }
42
- return formatValue(value, {
38
+ return formatValue(date, {
43
39
  ...getDefaultDataTimeOptions(showTime),
44
40
  ...valueFormatOptions
45
41
  });
46
- }, [value, dateFormat, showTime, formatValue, valueFormatOptions]);
42
+ }, [value, showTime, formatValue, valueFormatOptions]);
47
43
  const handleApply = useCallback((date) => {
48
44
  if (!onChange) {
49
45
  return;
@@ -7,10 +7,6 @@ type InputProps = DatePickerProps['inputProps'];
7
7
  export type Props = InputProps & {
8
8
  autoFocus?: boolean;
9
9
  size?: InputSize;
10
- /**
11
- * @deprecated use `valueFormatOptions` instead
12
- */
13
- format?: string;
14
10
  valueFormatOptions?: DateToFormatOptions;
15
11
  showTime?: boolean;
16
12
  allowClear?: boolean;
@@ -2,31 +2,10 @@ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { ButtonToggle } from "@synerise/ds-button";
3
3
  import { useDataFormat } from "@synerise/ds-core";
4
4
  import { Title } from "@synerise/ds-typography";
5
- import "date-fns/addDays";
6
- import "date-fns/addMonths";
7
- import "date-fns/addSeconds";
8
- import "date-fns/addYears";
9
- import "date-fns/differenceInSeconds";
10
- import "date-fns/differenceInYears";
11
- import "date-fns/endOfDay";
12
- import "date-fns/endOfMonth";
13
- import "date-fns/format";
14
- import "date-fns/getSeconds";
15
- import "date-fns/getYear";
16
- import "date-fns/isAfter";
17
- import "date-fns/isBefore";
18
- import "date-fns/isEqual";
19
- import fnsIsSameDay from "date-fns/isSameDay";
20
- import "date-fns/isSameMonth";
21
- import "date-fns/isSameYear";
22
- import "date-fns/setDate";
23
- import "date-fns/setMonth";
24
- import "date-fns/setYear";
25
- import "date-fns/startOfDay";
26
- import "date-fns/startOfMonth";
5
+ import { isSameDay } from "date-fns";
27
6
  import { QuickPicksWrapper } from "./QuickPicks.styles.js";
28
7
  const quickPickMatchesValue = (item, value) => {
29
- return value ? fnsIsSameDay(item.value, value) : false;
8
+ return value ? isSameDay(item.value, value) : false;
30
9
  };
31
10
  const QuickPicks = ({
32
11
  value,
@@ -1,29 +1,8 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import React from "react";
3
- import { legacyParse } from "@date-fns/upgrade/v2";
4
- import "date-fns/addDays";
5
- import "date-fns/addMonths";
6
- import "date-fns/addSeconds";
7
- import fnsAddYears from "date-fns/addYears";
8
- import "date-fns/differenceInSeconds";
9
- import "date-fns/differenceInYears";
10
- import "date-fns/endOfDay";
11
- import "date-fns/endOfMonth";
12
- import "date-fns/format";
13
- import "date-fns/getSeconds";
14
- import "date-fns/getYear";
15
- import "date-fns/isAfter";
16
- import "date-fns/isBefore";
17
- import "date-fns/isEqual";
18
- import "date-fns/isSameDay";
19
- import "date-fns/isSameMonth";
20
- import fnsIsSameYear from "date-fns/isSameYear";
21
- import "date-fns/setDate";
22
- import "date-fns/setMonth";
23
- import fnsSetYear from "date-fns/setYear";
24
- import "date-fns/startOfDay";
25
- import "date-fns/startOfMonth";
3
+ import { addYears, isSameYear, setYear } from "date-fns";
26
4
  import fnsFormat from "../../format.js";
5
+ import toDateValue from "../../toDateValue.js";
27
6
  import { getDecadeRange } from "../../utils.js";
28
7
  import DecadePicker from "../DecadePicker/DecadePicker.js";
29
8
  import GridPicker from "../GridPicker/GridPicker.js";
@@ -37,7 +16,7 @@ function getInitialState(props) {
37
16
  function getCells(cursor) {
38
17
  const startYear = getDecadeRange(cursor)[0];
39
18
  return Array.from(Array(10)).map((_, index) => {
40
- const date = fnsAddYears(fnsSetYear(cursor, startYear), index);
19
+ const date = addYears(setYear(cursor, startYear), index);
41
20
  return {
42
21
  key: date.toISOString(),
43
22
  text: fnsFormat(date, "yyyy")
@@ -53,7 +32,7 @@ class YearPicker extends React.PureComponent {
53
32
  cursor
54
33
  } = this.state;
55
34
  this.setState({
56
- cursor: fnsAddYears(cursor, -10)
35
+ cursor: addYears(cursor, -10)
57
36
  });
58
37
  };
59
38
  this.handleLongNext = () => {
@@ -61,21 +40,13 @@ class YearPicker extends React.PureComponent {
61
40
  cursor
62
41
  } = this.state;
63
42
  this.setState({
64
- cursor: fnsAddYears(cursor, 10)
43
+ cursor: addYears(cursor, 10)
65
44
  });
66
45
  };
67
46
  this.handleCellClick = (isoDate) => {
68
47
  const {
69
48
  onChange
70
49
  } = this.props;
71
- if (isoDate === -1) {
72
- this.handleLongPrev();
73
- return;
74
- }
75
- if (isoDate === 1) {
76
- this.handleLongNext();
77
- return;
78
- }
79
50
  onChange && onChange(new Date(isoDate));
80
51
  };
81
52
  }
@@ -97,18 +68,9 @@ class YearPicker extends React.PureComponent {
97
68
  value
98
69
  } = this.props;
99
70
  const decadeRange = getDecadeRange(cursor);
100
- let cells = getCells(cursor);
101
- const valueCell = value ? cells.find((cell) => fnsIsSameYear(value, legacyParse(cell.key))) : null;
71
+ const cells = getCells(cursor);
72
+ const valueCell = value ? cells.find((cell) => isSameYear(value, toDateValue(cell.key))) : null;
102
73
  const selectedKey = valueCell ? valueCell.key : null;
103
- cells = [{
104
- key: -1,
105
- text: decadeRange[0] - 1,
106
- outside: true
107
- }, ...cells, {
108
- key: 1,
109
- text: decadeRange[1] + 1,
110
- outside: true
111
- }];
112
74
  if (decadeMode) {
113
75
  return /* @__PURE__ */ jsx(DecadePicker, { value: cursor, onChange: (selectedDecade) => this.setState({
114
76
  cursor: selectedDecade,
@@ -116,7 +78,7 @@ class YearPicker extends React.PureComponent {
116
78
  }) });
117
79
  }
118
80
  return /* @__PURE__ */ jsxs(Fragment, { children: [
119
- /* @__PURE__ */ jsx(Navbar, { onTitleClick: () => this.setState({
81
+ /* @__PURE__ */ jsx(Navbar, { singleStep: true, onTitleClick: () => this.setState({
120
82
  decadeMode: true
121
83
  }), title: decadeRange.join("-"), onLongPrev: this.handleLongPrev, onLongNext: this.handleLongNext }, "head"),
122
84
  /* @__PURE__ */ jsx(GridPicker, { selectedKey, cells, onCellClick: this.handleCellClick }, "body")
@@ -1,7 +1,6 @@
1
1
  import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { injectIntl } from "react-intl";
4
- import { legacyParse } from "@date-fns/upgrade/v2";
5
4
  import { Container, ContentWrapper, PickerWrapper } from "../DatePicker.styles.js";
6
5
  import DayPicker from "../Elements/DayPicker/DayPicker.js";
7
6
  import { DayBackground, DayText, DayForeground } from "../Elements/DayPicker/DayPicker.styles.js";
@@ -10,30 +9,8 @@ import MonthPicker from "../Elements/MonthPicker/MonthPicker.js";
10
9
  import { QuickPicks } from "../Elements/QuickPicks/QuickPicks.js";
11
10
  import TimePicker from "../Elements/TimePicker/TimePicker.js";
12
11
  import YearPicker from "../Elements/YearPicker/YearPicker.js";
13
- import fnsAddDays from "date-fns/addDays";
14
- import "date-fns/addMonths";
15
- import "date-fns/addSeconds";
16
- import "date-fns/addYears";
17
- import "date-fns/differenceInSeconds";
18
- import "date-fns/differenceInYears";
19
- import fnsEndOfDay from "date-fns/endOfDay";
20
- import "date-fns/endOfMonth";
21
- import "date-fns/format";
22
- import "date-fns/getSeconds";
23
- import "date-fns/getYear";
24
- import "date-fns/isAfter";
25
- import "date-fns/isBefore";
26
- import "date-fns/isEqual";
27
- import "date-fns/isSameDay";
28
- import "date-fns/isSameMonth";
29
- import "date-fns/isSameYear";
30
- import fnsSetDate from "date-fns/setDate";
31
- import fnsSetMonth from "date-fns/setMonth";
32
- import fnsSetYear from "date-fns/setYear";
33
- import fnsStartOfDay from "date-fns/startOfDay";
34
- import fnsStartOfMonth from "date-fns/startOfMonth";
35
- import fnsFormat from "../format.js";
36
- import { changeDayWithHoursPreserved } from "../utils.js";
12
+ import { setYear, setMonth, setDate, startOfDay, endOfDay, addDays, startOfMonth } from "date-fns";
13
+ import toDateValue from "../toDateValue.js";
37
14
  import { getDefaultTexts } from "../utils/getDefaultTexts.js";
38
15
  const _RawDatePicker = class _RawDatePicker extends React.Component {
39
16
  constructor(props) {
@@ -42,24 +19,11 @@ const _RawDatePicker = class _RawDatePicker extends React.Component {
42
19
  const {
43
20
  onValueChange
44
21
  } = this.props;
45
- const {
46
- mode,
47
- value: valueFromState
48
- } = this.state;
49
- if (mode === "date" && !!valueFromState && !!value) {
50
- const dateToBeUpdated = changeDayWithHoursPreserved(valueFromState, value);
51
- this.setState({
52
- value: dateToBeUpdated,
53
- changed: true
54
- });
55
- onValueChange && onValueChange(dateToBeUpdated);
56
- } else {
57
- this.setState({
58
- value,
59
- changed: true
60
- });
61
- onValueChange && onValueChange(value);
62
- }
22
+ this.setState({
23
+ value,
24
+ changed: true
25
+ });
26
+ onValueChange && onValueChange(value);
63
27
  };
64
28
  this.handleDayMouseEnter = (day) => this.setState({
65
29
  enteredTo: day
@@ -69,7 +33,6 @@ const _RawDatePicker = class _RawDatePicker extends React.Component {
69
33
  });
70
34
  this.handleDayClick = (day, modifiers) => {
71
35
  const {
72
- changed: isChanged,
73
36
  value
74
37
  } = this.state;
75
38
  const {
@@ -80,14 +43,14 @@ const _RawDatePicker = class _RawDatePicker extends React.Component {
80
43
  if (modifiers.disabled) {
81
44
  return;
82
45
  }
83
- let nextDateWithCurrentTime = isChanged && value ? value : /* @__PURE__ */ new Date();
84
- nextDateWithCurrentTime = fnsSetYear(nextDateWithCurrentTime, day.getFullYear());
85
- nextDateWithCurrentTime = fnsSetMonth(nextDateWithCurrentTime, day.getMonth());
86
- nextDateWithCurrentTime = fnsSetDate(nextDateWithCurrentTime, day.getDate());
46
+ let nextDateWithCurrentTime = value || /* @__PURE__ */ new Date();
47
+ nextDateWithCurrentTime = setYear(nextDateWithCurrentTime, day.getFullYear());
48
+ nextDateWithCurrentTime = setMonth(nextDateWithCurrentTime, day.getMonth());
49
+ nextDateWithCurrentTime = setDate(nextDateWithCurrentTime, day.getDate());
87
50
  if (useStartOfDay) {
88
- this.handleChange(fnsStartOfDay(nextDateWithCurrentTime));
51
+ this.handleChange(startOfDay(nextDateWithCurrentTime));
89
52
  } else if (useEndOfDay) {
90
- this.handleChange(fnsEndOfDay(nextDateWithCurrentTime));
53
+ this.handleChange(endOfDay(nextDateWithCurrentTime));
91
54
  } else {
92
55
  this.handleChange(nextDateWithCurrentTime);
93
56
  }
@@ -159,7 +122,7 @@ const _RawDatePicker = class _RawDatePicker extends React.Component {
159
122
  const {
160
123
  month
161
124
  } = this.state;
162
- return /* @__PURE__ */ jsx(DayPicker, { fixedWeeks: true, showOutsideDays: true, canChangeMonth: false, disabledDays: disabledDates, selectedDays, month, title: fnsFormat(month, "MMM yyyy"), renderDay: this.renderDay, onDayClick: this.handleDayClick, onDayMouseEnter: this.handleDayMouseEnter, onDayMouseLeave: this.handleDayMouseLeave, onMonthNameClick: () => this.handleModeSwitch("month"), onYearNameClick: () => this.handleModeSwitch("year"), onMonthChange: (selectedMonth) => this.handleMonthChange(selectedMonth, "date"), modifiers });
125
+ return /* @__PURE__ */ jsx(DayPicker, { fixedWeeks: true, showOutsideDays: true, canChangeMonth: false, disabledDays: disabledDates, selectedDays, month, renderDay: this.renderDay, onDayClick: this.handleDayClick, onDayMouseEnter: this.handleDayMouseEnter, onDayMouseLeave: this.handleDayMouseLeave, onMonthNameClick: () => this.handleModeSwitch("month"), onYearNameClick: () => this.handleModeSwitch("year"), onMonthChange: (selectedMonth) => this.handleMonthChange(selectedMonth, "date"), modifiers });
163
126
  };
164
127
  this.handleDaySwitch = (day) => {
165
128
  const {
@@ -179,15 +142,15 @@ const _RawDatePicker = class _RawDatePicker extends React.Component {
179
142
  disabledSeconds,
180
143
  disabledDates
181
144
  } = this.props;
182
- const prevDay = fnsAddDays(legacyParse(value), -1);
183
- const nextDay = fnsAddDays(legacyParse(value), 1);
145
+ const prevDay = addDays(toDateValue(value), -1);
146
+ const nextDay = addDays(toDateValue(value), 1);
184
147
  const inactivePrev = disabledDates ? disabledDates(prevDay) : false;
185
148
  const inactiveNext = disabledDates ? disabledDates(nextDay) : false;
186
149
  return /* @__PURE__ */ jsx(TimePicker, { value, onChange: this.handleChange, disabledHours, disabledMinutes, disabledSeconds, inactiveNext, inactivePrev, onShortNext: () => this.handleDaySwitch(nextDay), onShortPrev: () => this.handleDaySwitch(prevDay) });
187
150
  };
188
151
  this.state = {
189
152
  mode: "date",
190
- month: fnsStartOfMonth(props.value || /* @__PURE__ */ new Date()),
153
+ month: startOfMonth(props.value || /* @__PURE__ */ new Date()),
191
154
  value: props.value,
192
155
  changed: false,
193
156
  enteredTo: void 0,
@@ -211,7 +174,7 @@ const _RawDatePicker = class _RawDatePicker extends React.Component {
211
174
  if (prevProps?.value !== value) {
212
175
  this.setState({
213
176
  value,
214
- month: fnsStartOfMonth(value || /* @__PURE__ */ new Date()),
177
+ month: startOfMonth(value || /* @__PURE__ */ new Date()),
215
178
  changed: true,
216
179
  mode: value === void 0 ? "date" : mode
217
180
  });
package/dist/fns.d.ts CHANGED
@@ -1,25 +1,4 @@
1
- import { default as fnsAddDays } from 'date-fns/addDays';
2
- import { default as fnsAddMonths } from 'date-fns/addMonths';
3
- import { default as fnsAddSeconds } from 'date-fns/addSeconds';
4
- import { default as fnsAddYears } from 'date-fns/addYears';
5
- import { default as fnsDifferenceInSeconds } from 'date-fns/differenceInSeconds';
6
- import { default as fnsDifferenceInYears } from 'date-fns/differenceInYears';
7
- import { default as fnsEndOfDay } from 'date-fns/endOfDay';
8
- import { default as fnsEndOfMonth } from 'date-fns/endOfMonth';
9
- import { default as fnsFormat } from 'date-fns/format';
10
- import { default as fnsGetSeconds } from 'date-fns/getSeconds';
11
- import { default as fnsGetYear } from 'date-fns/getYear';
12
- import { default as fnsIsAfter } from 'date-fns/isAfter';
13
- import { default as fnsIsBefore } from 'date-fns/isBefore';
14
- import { default as fnsIsEqual } from 'date-fns/isEqual';
15
- import { default as fnsIsSameDay } from 'date-fns/isSameDay';
16
- import { default as fnsIsSameMonth } from 'date-fns/isSameMonth';
17
- import { default as fnsIsSameYear } from 'date-fns/isSameYear';
18
- import { default as fnsSetDate } from 'date-fns/setDate';
19
- import { default as fnsSetMonth } from 'date-fns/setMonth';
20
- import { default as fnsSetYear } from 'date-fns/setYear';
21
- import { default as fnsStartOfDay } from 'date-fns/startOfDay';
22
- import { default as fnsStartOfMonth } from 'date-fns/startOfMonth';
1
+ import { addDays as fnsAddDays, addMonths as fnsAddMonths, addSeconds as fnsAddSeconds, addYears as fnsAddYears, differenceInSeconds as fnsDifferenceInSeconds, differenceInYears as fnsDifferenceInYears, endOfDay as fnsEndOfDay, endOfMonth as fnsEndOfMonth, format as fnsFormat, getSeconds as fnsGetSeconds, getYear as fnsGetYear, isAfter as fnsIsAfter, isBefore as fnsIsBefore, isEqual as fnsIsEqual, isSameDay as fnsIsSameDay, isSameMonth as fnsIsSameMonth, isSameYear as fnsIsSameYear, setDate as fnsSetDate, setMonth as fnsSetMonth, setYear as fnsSetYear, startOfDay as fnsStartOfDay, startOfMonth as fnsStartOfMonth } from 'date-fns';
23
2
  export { fnsGetSeconds };
24
3
  export { fnsDifferenceInSeconds };
25
4
  export { fnsStartOfMonth };
package/dist/fns.js CHANGED
@@ -1,46 +1,25 @@
1
- import { default as default2 } from "date-fns/addDays";
2
- import { default as default3 } from "date-fns/addMonths";
3
- import { default as default4 } from "date-fns/addSeconds";
4
- import { default as default5 } from "date-fns/addYears";
5
- import { default as default6 } from "date-fns/differenceInSeconds";
6
- import { default as default7 } from "date-fns/differenceInYears";
7
- import { default as default8 } from "date-fns/endOfDay";
8
- import { default as default9 } from "date-fns/endOfMonth";
9
- import { default as default10 } from "date-fns/format";
10
- import { default as default11 } from "date-fns/getSeconds";
11
- import { default as default12 } from "date-fns/getYear";
12
- import { default as default13 } from "date-fns/isAfter";
13
- import { default as default14 } from "date-fns/isBefore";
14
- import { default as default15 } from "date-fns/isEqual";
15
- import { default as default16 } from "date-fns/isSameDay";
16
- import { default as default17 } from "date-fns/isSameMonth";
17
- import { default as default18 } from "date-fns/isSameYear";
18
- import { default as default19 } from "date-fns/setDate";
19
- import { default as default20 } from "date-fns/setMonth";
20
- import { default as default21 } from "date-fns/setYear";
21
- import { default as default22 } from "date-fns/startOfDay";
22
- import { default as default23 } from "date-fns/startOfMonth";
1
+ import { addDays, addMonths, addSeconds, addYears, differenceInSeconds, differenceInYears, endOfDay, endOfMonth, format, getSeconds, getYear, isAfter, isBefore, isEqual, isSameDay, isSameMonth, isSameYear, setDate, setMonth, setYear, startOfDay, startOfMonth } from "date-fns";
23
2
  export {
24
- default2 as fnsAddDays,
25
- default3 as fnsAddMonths,
26
- default4 as fnsAddSeconds,
27
- default5 as fnsAddYears,
28
- default6 as fnsDifferenceInSeconds,
29
- default7 as fnsDifferenceInYears,
30
- default8 as fnsEndOfDay,
31
- default9 as fnsEndOfMonth,
32
- default10 as fnsFormat,
33
- default11 as fnsGetSeconds,
34
- default12 as fnsGetYear,
35
- default13 as fnsIsAfter,
36
- default14 as fnsIsBefore,
37
- default15 as fnsIsEqual,
38
- default16 as fnsIsSameDay,
39
- default17 as fnsIsSameMonth,
40
- default18 as fnsIsSameYear,
41
- default19 as fnsSetDate,
42
- default20 as fnsSetMonth,
43
- default21 as fnsSetYear,
44
- default22 as fnsStartOfDay,
45
- default23 as fnsStartOfMonth
3
+ addDays as fnsAddDays,
4
+ addMonths as fnsAddMonths,
5
+ addSeconds as fnsAddSeconds,
6
+ addYears as fnsAddYears,
7
+ differenceInSeconds as fnsDifferenceInSeconds,
8
+ differenceInYears as fnsDifferenceInYears,
9
+ endOfDay as fnsEndOfDay,
10
+ endOfMonth as fnsEndOfMonth,
11
+ format as fnsFormat,
12
+ getSeconds as fnsGetSeconds,
13
+ getYear as fnsGetYear,
14
+ isAfter as fnsIsAfter,
15
+ isBefore as fnsIsBefore,
16
+ isEqual as fnsIsEqual,
17
+ isSameDay as fnsIsSameDay,
18
+ isSameMonth as fnsIsSameMonth,
19
+ isSameYear as fnsIsSameYear,
20
+ setDate as fnsSetDate,
21
+ setMonth as fnsSetMonth,
22
+ setYear as fnsSetYear,
23
+ startOfDay as fnsStartOfDay,
24
+ startOfMonth as fnsStartOfMonth
46
25
  };
package/dist/format.js CHANGED
@@ -1,27 +1,5 @@
1
- import fnsIsValid from "date-fns/isValid";
1
+ import { isValid, format } from "date-fns";
2
2
  import { es, pl, enGB } from "date-fns/locale";
3
- import "date-fns/addDays";
4
- import "date-fns/addMonths";
5
- import "date-fns/addSeconds";
6
- import "date-fns/addYears";
7
- import "date-fns/differenceInSeconds";
8
- import "date-fns/differenceInYears";
9
- import "date-fns/endOfDay";
10
- import "date-fns/endOfMonth";
11
- import fnsFormat$1 from "date-fns/format";
12
- import "date-fns/getSeconds";
13
- import "date-fns/getYear";
14
- import "date-fns/isAfter";
15
- import "date-fns/isBefore";
16
- import "date-fns/isEqual";
17
- import "date-fns/isSameDay";
18
- import "date-fns/isSameMonth";
19
- import "date-fns/isSameYear";
20
- import "date-fns/setDate";
21
- import "date-fns/setMonth";
22
- import "date-fns/setYear";
23
- import "date-fns/startOfDay";
24
- import "date-fns/startOfMonth";
25
3
  const locales = {
26
4
  en: enGB,
27
5
  pl,
@@ -32,11 +10,11 @@ const isLocaleKey = (key) => {
32
10
  };
33
11
  const defaultLocale = "en";
34
12
  function fnsFormat(date, formatStr, locale = defaultLocale) {
35
- if (!fnsIsValid(date)) {
13
+ if (!isValid(date)) {
36
14
  return "";
37
15
  }
38
16
  const localeKey = locale.substring(0, 2);
39
- return fnsFormat$1(date, formatStr, {
17
+ return format(date, formatStr, {
40
18
  locale: isLocaleKey(localeKey) ? locales[localeKey] : locales[defaultLocale]
41
19
  });
42
20
  }
package/dist/index.js CHANGED
@@ -6,28 +6,7 @@ import { default as default7 } from "./Elements/DayPicker/DayPicker.js";
6
6
  import { default as default8 } from "./Elements/TimePicker/TimePicker.js";
7
7
  import { default as default9 } from "./format.js";
8
8
  import { default as default10 } from "./localeUtils.js";
9
- import { default as default11 } from "date-fns/addDays";
10
- import { default as default12 } from "date-fns/addMonths";
11
- import { default as default13 } from "date-fns/addSeconds";
12
- import { default as default14 } from "date-fns/addYears";
13
- import { default as default15 } from "date-fns/differenceInSeconds";
14
- import { default as default16 } from "date-fns/differenceInYears";
15
- import { default as default17 } from "date-fns/endOfDay";
16
- import { default as default18 } from "date-fns/endOfMonth";
17
- import { default as default19 } from "date-fns/format";
18
- import { default as default20 } from "date-fns/getSeconds";
19
- import { default as default21 } from "date-fns/getYear";
20
- import { default as default22 } from "date-fns/isAfter";
21
- import { default as default23 } from "date-fns/isBefore";
22
- import { default as default24 } from "date-fns/isEqual";
23
- import { default as default25 } from "date-fns/isSameDay";
24
- import { default as default26 } from "date-fns/isSameMonth";
25
- import { default as default27 } from "date-fns/isSameYear";
26
- import { default as default28 } from "date-fns/setDate";
27
- import { default as default29 } from "date-fns/setMonth";
28
- import { default as default30 } from "date-fns/setYear";
29
- import { default as default31 } from "date-fns/startOfDay";
30
- import { default as default32 } from "date-fns/startOfMonth";
9
+ import { addDays, addMonths, addSeconds, addYears, differenceInSeconds, differenceInYears, endOfDay, endOfMonth, format, getSeconds, getYear, isAfter, isBefore, isEqual, isSameDay, isSameMonth, isSameYear, setDate, setMonth, setYear, startOfDay, startOfMonth } from "date-fns";
31
10
  import { getDefaultTexts } from "./utils/getDefaultTexts.js";
32
11
  export {
33
12
  default2 as DatePicker,
@@ -38,28 +17,28 @@ export {
38
17
  default6 as YearPicker,
39
18
  default9 as datePickerFormat,
40
19
  default3 as default,
41
- default11 as fnsAddDays,
42
- default12 as fnsAddMonths,
43
- default13 as fnsAddSeconds,
44
- default14 as fnsAddYears,
45
- default15 as fnsDifferenceInSeconds,
46
- default16 as fnsDifferenceInYears,
47
- default17 as fnsEndOfDay,
48
- default18 as fnsEndOfMonth,
49
- default19 as fnsFormat,
50
- default20 as fnsGetSeconds,
51
- default21 as fnsGetYear,
52
- default22 as fnsIsAfter,
53
- default23 as fnsIsBefore,
54
- default24 as fnsIsEqual,
55
- default25 as fnsIsSameDay,
56
- default26 as fnsIsSameMonth,
57
- default27 as fnsIsSameYear,
58
- default28 as fnsSetDate,
59
- default29 as fnsSetMonth,
60
- default30 as fnsSetYear,
61
- default31 as fnsStartOfDay,
62
- default32 as fnsStartOfMonth,
20
+ addDays as fnsAddDays,
21
+ addMonths as fnsAddMonths,
22
+ addSeconds as fnsAddSeconds,
23
+ addYears as fnsAddYears,
24
+ differenceInSeconds as fnsDifferenceInSeconds,
25
+ differenceInYears as fnsDifferenceInYears,
26
+ endOfDay as fnsEndOfDay,
27
+ endOfMonth as fnsEndOfMonth,
28
+ format as fnsFormat,
29
+ getSeconds as fnsGetSeconds,
30
+ getYear as fnsGetYear,
31
+ isAfter as fnsIsAfter,
32
+ isBefore as fnsIsBefore,
33
+ isEqual as fnsIsEqual,
34
+ isSameDay as fnsIsSameDay,
35
+ isSameMonth as fnsIsSameMonth,
36
+ isSameYear as fnsIsSameYear,
37
+ setDate as fnsSetDate,
38
+ setMonth as fnsSetMonth,
39
+ setYear as fnsSetYear,
40
+ startOfDay as fnsStartOfDay,
41
+ startOfMonth as fnsStartOfMonth,
63
42
  getDefaultTexts,
64
43
  default10 as localeUtils
65
44
  };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Normalises the loosely-typed date values this package accepts into a `Date`.
3
+ *
4
+ * Replaces `legacyParse` from `@date-fns/upgrade`, a shim reimplementing date-fns v1's `parse()`,
5
+ * and reproduces its branches exactly — verified identical across every input kind the call sites
6
+ * reach, including the two that surprise:
7
+ *
8
+ * - `null` yields the **epoch**, which `isValid` reports as a valid date, not a rejected one.
9
+ * - `undefined`, `''` and unparseable strings yield an `Invalid Date`.
10
+ *
11
+ * The string branch is load-bearing. `legacyParse` reads a naive string as *local* time, including
12
+ * date-only forms where the ECMAScript spec says UTC. `parseISO` agrees; `toDate` and `new Date`
13
+ * do not, and would shift `'2024-01-15'` by the local offset — a whole day either side of UTC.
14
+ */
15
+ export declare const toDateValue: (value: Date | string | number | null | undefined) => Date;
16
+ export default toDateValue;
@@ -0,0 +1,14 @@
1
+ import { parseISO } from "date-fns";
2
+ const toDateValue = (value) => {
3
+ if (value instanceof Date) {
4
+ return new Date(value.getTime());
5
+ }
6
+ if (typeof value === "string") {
7
+ return parseISO(value);
8
+ }
9
+ return new Date(value);
10
+ };
11
+ export {
12
+ toDateValue as default,
13
+ toDateValue
14
+ };
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export declare const range: (start: number, end: number) => number[];
2
2
  export declare function getDecadeRange(cursor: Date): number[];
3
3
  export declare function getCenturyRange(cursor: Date): number[];
4
- export declare function changeDayWithHoursPreserved(oldDate: Date, chosenDate: Date): Date;
package/dist/utils.js CHANGED
@@ -1,27 +1,4 @@
1
- import { differenceInCalendarDays } from "date-fns";
2
- import { legacyParse } from "@date-fns/upgrade/v2";
3
- import "date-fns/addDays";
4
- import "date-fns/addMonths";
5
- import fnsAddSeconds from "date-fns/addSeconds";
6
- import "date-fns/addYears";
7
- import fnsDifferenceInSeconds from "date-fns/differenceInSeconds";
8
- import "date-fns/differenceInYears";
9
- import "date-fns/endOfDay";
10
- import "date-fns/endOfMonth";
11
- import "date-fns/format";
12
- import "date-fns/getSeconds";
13
- import fnsGetYear from "date-fns/getYear";
14
- import "date-fns/isAfter";
15
- import "date-fns/isBefore";
16
- import "date-fns/isEqual";
17
- import "date-fns/isSameDay";
18
- import "date-fns/isSameMonth";
19
- import "date-fns/isSameYear";
20
- import "date-fns/setDate";
21
- import "date-fns/setMonth";
22
- import "date-fns/setYear";
23
- import "date-fns/startOfDay";
24
- import "date-fns/startOfMonth";
1
+ import { getYear } from "date-fns";
25
2
  const range = (start, end) => {
26
3
  if (end <= start) {
27
4
  return [];
@@ -30,23 +7,16 @@ const range = (start, end) => {
30
7
  return [...Array(size).keys()].map((i) => i + start);
31
8
  };
32
9
  function getDecadeRange(cursor) {
33
- const startYear = Math.floor(fnsGetYear(cursor) / 10) * 10;
10
+ const startYear = Math.floor(getYear(cursor) / 10) * 10;
34
11
  const endYear = startYear + 9;
35
12
  return [startYear, endYear];
36
13
  }
37
14
  function getCenturyRange(cursor) {
38
- const startYear = Math.floor(fnsGetYear(cursor) / 100) * 100;
15
+ const startYear = Math.floor(getYear(cursor) / 100) * 100;
39
16
  const endYear = startYear + 99;
40
17
  return [startYear, endYear];
41
18
  }
42
- function changeDayWithHoursPreserved(oldDate, chosenDate) {
43
- const dayDifferenceInSeconds = differenceInCalendarDays(legacyParse(oldDate), legacyParse(chosenDate)) * 24 * 60 * 60;
44
- const difference = fnsDifferenceInSeconds(oldDate, chosenDate) - dayDifferenceInSeconds;
45
- const dateToBeUpdated = fnsAddSeconds(chosenDate, difference);
46
- return dateToBeUpdated;
47
- }
48
19
  export {
49
- changeDayWithHoursPreserved,
50
20
  getCenturyRange,
51
21
  getDecadeRange,
52
22
  range
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synerise/ds-date-picker",
3
- "version": "2.0.3",
3
+ "version": "2.1.1",
4
4
  "description": "DatePicker UI Component for the Synerise Design System",
5
5
  "license": "ISC",
6
6
  "repository": "Synerise/synerise-design",
@@ -30,7 +30,8 @@
30
30
  "build:watch": "vite build --watch",
31
31
  "pack:ci": "pnpm pack --pack-destination ../../storybook/storybook-static/static",
32
32
  "prepublish": "pnpm run build",
33
- "test": "vitest run",
33
+ "test": "vitest run && pnpm test:dst",
34
+ "test:dst": "TZ=Europe/Warsaw vitest run -t DaylightSavings",
34
35
  "test:watch": "vitest",
35
36
  "types": "tsc --noEmit",
36
37
  "check:circular-dependencies": "madge --circular --extensions ts,tsx,js,jsx --ts-config tsconfig.json src/ --exclude '/dist/'",
@@ -42,17 +43,16 @@
42
43
  ],
43
44
  "types": "dist/index.d.ts",
44
45
  "dependencies": {
45
- "@date-fns/upgrade": "^1.0.3",
46
- "@synerise/ds-button": "^2.0.2",
47
- "@synerise/ds-dropdown": "^2.0.3",
48
- "@synerise/ds-icon": "^2.1.0",
49
- "@synerise/ds-input": "^2.0.3",
50
- "@synerise/ds-time-picker": "^2.0.3",
51
- "@synerise/ds-tooltip": "^2.0.2",
52
- "@synerise/ds-typography": "^2.0.2",
53
- "@synerise/ds-utils": "^2.0.0",
46
+ "@synerise/ds-button": "^2.0.3",
47
+ "@synerise/ds-dropdown": "^2.1.0",
48
+ "@synerise/ds-icon": "^2.1.1",
49
+ "@synerise/ds-input": "^2.0.4",
50
+ "@synerise/ds-time-picker": "^2.0.4",
51
+ "@synerise/ds-tooltip": "^2.0.3",
52
+ "@synerise/ds-typography": "^2.0.3",
53
+ "@synerise/ds-utils": "^2.1.0",
54
54
  "classnames": "^2.5.1",
55
- "date-fns": "2.16.1",
55
+ "date-fns": "^4.1.0",
56
56
  "lodash.range": "^3.2.0",
57
57
  "react-day-picker": "^10.0.1",
58
58
  "uuid": "^8.3.2"
@@ -67,5 +67,5 @@
67
67
  "react-intl": ">=3.12.0 <= 6.8",
68
68
  "styled-components": "^5.3.3"
69
69
  },
70
- "gitHead": "e0dced8fd3a1194eff2fafc1b7e385b29238af88"
70
+ "gitHead": "71f08fdaba8e7b80731e29a03883dc6a33f2b9db"
71
71
  }