@stenajs-webui/calendar 23.23.0 → 24.0.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/README.md CHANGED
@@ -5,6 +5,18 @@ and input fields using calendar functionality.
5
5
 
6
6
  ## Components
7
7
 
8
+ ### Travel calendars
9
+
10
+ One month at a time, for picking travel dates. Each date can show custom
11
+ content, such as a price. See [AGENT_DOC.mdx](AGENT_DOC.mdx), which is also the
12
+ "AI Documentation/Calendar" page in Storybook.
13
+
14
+ * TravelDateInput
15
+ * TravelDateRangeInput
16
+ * TravelDateCalendar
17
+ * TravelDateCalendarPanel
18
+ * TravelDateRangeCalendar
19
+
8
20
  ### Calendars
9
21
 
10
22
  * [DateRangeCalendar](src/components/calendar-types/date-range-calendar/DateRangeCalendar.md)
@@ -7,7 +7,16 @@ import { SupportedLocaleCode } from "../../../features/localize-date-format/Loca
7
7
  export interface TravelDateCalendarProps extends ValueAndOnValueChangeProps<string> {
8
8
  localeCode?: SupportedLocaleCode;
9
9
  languageLocaleCode?: SupportedLocaleCode;
10
- initialMonthInFocus?: Date;
10
+ /**
11
+ * The month the calendar shows. Only its year and month are read, so any
12
+ * day of the month will do. The calendar never changes it by itself; see
13
+ * onVisibleMonthChange.
14
+ *
15
+ * It is not taken from the value. To open on the month of a value, pass
16
+ * that month, for example with
17
+ * useVisibleMonthState(parseLocalizedDateString(value, localeCode)).
18
+ */
19
+ visibleMonth: Date;
11
20
  label?: string;
12
21
  previousMonthButtonAriaLabel?: string;
13
22
  nextMonthButtonAriaLabel?: string;
@@ -23,12 +32,22 @@ export interface TravelDateCalendarProps extends ValueAndOnValueChangeProps<stri
23
32
  */
24
33
  renderDateContent?: RenderTravelDateContent;
25
34
  /**
26
- * Called with the first day of the month the calendar shows: once when the
27
- * component mounts, and again each time the user moves to another month.
28
- * For loading data one month at a time, such as prices for
29
- * renderDateContent.
35
+ * Called when the calendar should show another month: from the month
36
+ * buttons, the month picker, the arrow keys, or a value in another month.
37
+ * It gets the first day of that month, and the calendar moves once
38
+ * visibleMonth is set to it. It is not called for the month already shown.
39
+ *
40
+ * When it follows a value, it never asks for a month before the current
41
+ * one: a value in the past asks for the current month. The month buttons
42
+ * and the arrow keys are not limited that way. To keep the user out of
43
+ * past months, set prevMonthDisabled and isDateDisabled, for example with
44
+ * usePrevMonthDisabledBeforeToday() and useDisabledBeforeToday(). The
45
+ * month picker starts at firstMonthInMonthPicker, the current month by
46
+ * default.
47
+ *
48
+ * useVisibleMonthState() keeps this state, for when nothing else needs it.
30
49
  */
31
- onVisibleMonthChange?: (visibleMonth: Date) => void;
50
+ onVisibleMonthChange: (visibleMonth: Date) => void;
32
51
  prevMonthDisabled?: (visibleMonth: Date) => boolean;
33
52
  nextMonthDisabled?: (visibleMonth: Date) => boolean;
34
53
  size?: TravelCalendarSizeVariant;
@@ -6,7 +6,16 @@ import { RenderTravelDateContent } from "../../../features/travel-calendar/types
6
6
  export interface TravelDateCalendarPanelProps extends ValueAndOnValueChangeProps<string> {
7
7
  localeCode?: SupportedLocaleCode;
8
8
  languageLocaleCode?: SupportedLocaleCode;
9
- initialMonthInFocus?: Date;
9
+ /**
10
+ * The month the calendar shows. Only its year and month are read, so any
11
+ * day of the month will do. The calendar never changes it by itself; see
12
+ * onVisibleMonthChange.
13
+ *
14
+ * It is not taken from the value. To open on the month of a value, pass
15
+ * that month, for example with
16
+ * useVisibleMonthState(parseLocalizedDateString(value, localeCode)).
17
+ */
18
+ visibleMonth: Date;
10
19
  previousMonthButtonAriaLabel?: string;
11
20
  nextMonthButtonAriaLabel?: string;
12
21
  firstMonthInMonthPicker?: Date;
@@ -19,12 +28,22 @@ export interface TravelDateCalendarPanelProps extends ValueAndOnValueChangeProps
19
28
  */
20
29
  renderDateContent?: RenderTravelDateContent;
21
30
  /**
22
- * Called with the first day of the month the calendar shows: once when the
23
- * component mounts, and again each time the user moves to another month.
24
- * For loading data one month at a time, such as prices for
25
- * renderDateContent.
31
+ * Called when the calendar should show another month: from the month
32
+ * buttons, the month picker, the arrow keys, or a value in another month.
33
+ * It gets the first day of that month, and the calendar moves once
34
+ * visibleMonth is set to it. It is not called for the month already shown.
35
+ *
36
+ * When it follows a value, it never asks for a month before the current
37
+ * one: a value in the past asks for the current month. The month buttons
38
+ * and the arrow keys are not limited that way. To keep the user out of
39
+ * past months, set prevMonthDisabled and isDateDisabled, for example with
40
+ * usePrevMonthDisabledBeforeToday() and useDisabledBeforeToday(). The
41
+ * month picker starts at firstMonthInMonthPicker, the current month by
42
+ * default.
43
+ *
44
+ * useVisibleMonthState() keeps this state, for when nothing else needs it.
26
45
  */
27
- onVisibleMonthChange?: (visibleMonth: Date) => void;
46
+ onVisibleMonthChange: (visibleMonth: Date) => void;
28
47
  prevMonthDisabled?: (visibleMonth: Date) => boolean;
29
48
  nextMonthDisabled?: (visibleMonth: Date) => boolean;
30
49
  size?: TravelCalendarSizeVariant;
@@ -7,7 +7,16 @@ import { SupportedLocaleCode } from "../../../features/localize-date-format/Loca
7
7
  export interface TravelDateRangeCalendarProps extends ValueAndOnValueChangeProps<TravelDateRangeInputValue> {
8
8
  localeCode?: SupportedLocaleCode;
9
9
  languageLocaleCode?: SupportedLocaleCode;
10
- initialMonthInFocus?: Date;
10
+ /**
11
+ * The month the calendar shows. Only its year and month are read, so any
12
+ * day of the month will do. The calendar never changes it by itself; see
13
+ * onVisibleMonthChange.
14
+ *
15
+ * It is not taken from the value. To open on the month of a value, pass
16
+ * that month, for example with
17
+ * useVisibleMonthState(parseLocalizedDateString(value, localeCode)).
18
+ */
19
+ visibleMonth: Date;
11
20
  startDateLabel?: string;
12
21
  endDateLabel?: string;
13
22
  previousMonthButtonAriaLabel?: string;
@@ -24,12 +33,22 @@ export interface TravelDateRangeCalendarProps extends ValueAndOnValueChangeProps
24
33
  */
25
34
  renderDateContent?: RenderTravelDateContent;
26
35
  /**
27
- * Called with the first day of the month the calendar shows: once when the
28
- * component mounts, and again each time the user moves to another month.
29
- * For loading data one month at a time, such as prices for
30
- * renderDateContent.
36
+ * Called when the calendar should show another month: from the month
37
+ * buttons, the month picker, the arrow keys, or a value in another month.
38
+ * It gets the first day of that month, and the calendar moves once
39
+ * visibleMonth is set to it. It is not called for the month already shown.
40
+ *
41
+ * When it follows a value, it never asks for a month before the current
42
+ * one: a value in the past asks for the current month. The month buttons
43
+ * and the arrow keys are not limited that way. To keep the user out of
44
+ * past months, set prevMonthDisabled and isDateDisabled, for example with
45
+ * usePrevMonthDisabledBeforeToday() and useDisabledBeforeToday(). The
46
+ * month picker starts at firstMonthInMonthPicker, the current month by
47
+ * default.
48
+ *
49
+ * useVisibleMonthState() keeps this state, for when nothing else needs it.
31
50
  */
32
- onVisibleMonthChange?: (visibleMonth: Date) => void;
51
+ onVisibleMonthChange: (visibleMonth: Date) => void;
33
52
  prevMonthDisabled?: (visibleMonth: Date) => boolean;
34
53
  nextMonthDisabled?: (visibleMonth: Date) => boolean;
35
54
  size?: TravelCalendarSizeVariant;
@@ -11,7 +11,16 @@ export interface RenderBelowSingleDateCalendarArgs {
11
11
  export interface TravelDateInputProps extends ValueAndOnValueChangeProps<string> {
12
12
  localeCode?: SupportedLocaleCode;
13
13
  languageLocaleCode?: SupportedLocaleCode;
14
- initialMonthInFocus?: Date;
14
+ /**
15
+ * The month the calendar shows. Only its year and month are read, so any
16
+ * day of the month will do. The calendar never changes it by itself; see
17
+ * onVisibleMonthChange.
18
+ *
19
+ * It is not taken from the value. To open on the month of a value, pass
20
+ * that month, for example with
21
+ * useVisibleMonthState(parseLocalizedDateString(value, localeCode)).
22
+ */
23
+ visibleMonth: Date;
15
24
  label?: string;
16
25
  previousMonthButtonAriaLabel?: string;
17
26
  nextMonthButtonAriaLabel?: string;
@@ -27,12 +36,22 @@ export interface TravelDateInputProps extends ValueAndOnValueChangeProps<string>
27
36
  */
28
37
  renderDateContent?: RenderTravelDateContent;
29
38
  /**
30
- * Called with the first day of the month the calendar shows: once when the
31
- * component mounts, and again each time the user moves to another month.
32
- * For loading data one month at a time, such as prices for
33
- * renderDateContent.
39
+ * Called when the calendar should show another month: from the month
40
+ * buttons, the month picker, the arrow keys, or a value in another month.
41
+ * It gets the first day of that month, and the calendar moves once
42
+ * visibleMonth is set to it. It is not called for the month already shown.
43
+ *
44
+ * When it follows a value, it never asks for a month before the current
45
+ * one: a value in the past asks for the current month. The month buttons
46
+ * and the arrow keys are not limited that way. To keep the user out of
47
+ * past months, set prevMonthDisabled and isDateDisabled, for example with
48
+ * usePrevMonthDisabledBeforeToday() and useDisabledBeforeToday(). The
49
+ * month picker starts at firstMonthInMonthPicker, the current month by
50
+ * default.
51
+ *
52
+ * useVisibleMonthState() keeps this state, for when nothing else needs it.
34
53
  */
35
- onVisibleMonthChange?: (visibleMonth: Date) => void;
54
+ onVisibleMonthChange: (visibleMonth: Date) => void;
36
55
  prevMonthDisabled?: (visibleMonth: Date) => boolean;
37
56
  nextMonthDisabled?: (visibleMonth: Date) => boolean;
38
57
  zIndex?: number;
@@ -11,7 +11,16 @@ export interface RenderBelowCalendarArgs {
11
11
  export interface TravelDateRangeInputProps extends ValueAndOnValueChangeProps<TravelDateRangeInputValue> {
12
12
  localeCode?: SupportedLocaleCode;
13
13
  languageLocaleCode?: SupportedLocaleCode;
14
- initialMonthInFocus?: Date;
14
+ /**
15
+ * The month the calendar shows. Only its year and month are read, so any
16
+ * day of the month will do. The calendar never changes it by itself; see
17
+ * onVisibleMonthChange.
18
+ *
19
+ * It is not taken from the value. To open on the month of a value, pass
20
+ * that month, for example with
21
+ * useVisibleMonthState(parseLocalizedDateString(value, localeCode)).
22
+ */
23
+ visibleMonth: Date;
15
24
  startDateLabel?: string;
16
25
  endDateLabel?: string;
17
26
  previousMonthButtonAriaLabel?: string;
@@ -28,12 +37,22 @@ export interface TravelDateRangeInputProps extends ValueAndOnValueChangeProps<Tr
28
37
  */
29
38
  renderDateContent?: RenderTravelDateContent;
30
39
  /**
31
- * Called with the first day of the month the calendar shows: once when the
32
- * component mounts, and again each time the user moves to another month.
33
- * For loading data one month at a time, such as prices for
34
- * renderDateContent.
40
+ * Called when the calendar should show another month: from the month
41
+ * buttons, the month picker, the arrow keys, or a value in another month.
42
+ * It gets the first day of that month, and the calendar moves once
43
+ * visibleMonth is set to it. It is not called for the month already shown.
44
+ *
45
+ * When it follows a value, it never asks for a month before the current
46
+ * one: a value in the past asks for the current month. The month buttons
47
+ * and the arrow keys are not limited that way. To keep the user out of
48
+ * past months, set prevMonthDisabled and isDateDisabled, for example with
49
+ * usePrevMonthDisabledBeforeToday() and useDisabledBeforeToday(). The
50
+ * month picker starts at firstMonthInMonthPicker, the current month by
51
+ * default.
52
+ *
53
+ * useVisibleMonthState() keeps this state, for when nothing else needs it.
35
54
  */
36
- onVisibleMonthChange?: (visibleMonth: Date) => void;
55
+ onVisibleMonthChange: (visibleMonth: Date) => void;
37
56
  prevMonthDisabled?: (visibleMonth: Date) => boolean;
38
57
  nextMonthDisabled?: (visibleMonth: Date) => boolean;
39
58
  zIndex?: number;
@@ -10,6 +10,8 @@ export interface TravelDateCellProps {
10
10
  selectedEndDate: Date | undefined;
11
11
  isValidDateRange: boolean;
12
12
  onChangeVisibleMonth: (visibleMonth: Date) => void;
13
+ /** Focuses the date once its month is shown, which the app decides. */
14
+ focusDateWhenShown: (date: Date) => void;
13
15
  onStartHover: (date: Date) => void;
14
16
  onEndHover: (date: Date) => void;
15
17
  /** Current date that the mouse is hovering over. */
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ import { VisibleMonthState } from "../../hooks/UseVisibleMonthState";
3
+ /**
4
+ * The travel calendars with their visible month kept in state, the way an app
5
+ * using useVisibleMonthState would. For tests that are about something else,
6
+ * and still need the month buttons and the arrow keys to move the calendar.
7
+ */
8
+ type WithMonthState<P> = Omit<P, keyof VisibleMonthState> & {
9
+ initialMonth?: Date;
10
+ };
11
+ export declare const StatefulTravelDateInput: ({ initialMonth, ...props }: WithMonthState<import("../../../..").TravelDateInputProps>) => React.JSX.Element;
12
+ export declare const StatefulTravelDateRangeInput: ({ initialMonth, ...props }: WithMonthState<import("../../../..").TravelDateRangeInputProps>) => React.JSX.Element;
13
+ export declare const StatefulTravelDateCalendar: ({ initialMonth, ...props }: WithMonthState<import("../../../..").TravelDateCalendarProps>) => React.JSX.Element;
14
+ export declare const StatefulTravelDateCalendarPanel: ({ initialMonth, ...props }: WithMonthState<import("../../../..").TravelDateCalendarPanelProps>) => React.JSX.Element;
15
+ export declare const StatefulTravelDateRangeCalendar: ({ initialMonth, ...props }: WithMonthState<import("../../../..").TravelDateRangeCalendarProps>) => React.JSX.Element;
16
+ export {};
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The visible month of a travel calendar, which the app owns.
3
+ *
4
+ * The calendar never changes the month itself. Everything that would move it,
5
+ * the month buttons, the month picker, the arrow keys and a new value, asks the
6
+ * app instead, through onVisibleMonthChange.
7
+ *
8
+ * The request is always the first day of the month. The arrow keys and the
9
+ * month picker work with other days of the month, and the app should get the
10
+ * same month in the same form whichever way it was reached.
11
+ *
12
+ * A request for the month that is already visible is not made at all, so the
13
+ * app only hears about actual changes.
14
+ *
15
+ * The month is kept by year and month, so an app that passes a new Date on
16
+ * every render, such as new Date(), does not rebuild the calendar each time.
17
+ */
18
+ export declare const useControlledVisibleMonth: (visibleMonthValue: Date, onVisibleMonthChange: (visibleMonth: Date) => void, today: Date) => {
19
+ visibleMonth: Date;
20
+ setVisibleMonth: (requested: Date) => void;
21
+ setVisibleMonthClamped: (requested: Date) => void;
22
+ };
@@ -1,6 +1,6 @@
1
1
  import { SupportedLocaleCode } from "../../localize-date-format/LocaleMapper";
2
2
  import { VisiblePanel } from "../types";
3
- export declare const useTravelDateInput: (value: string | undefined, onValueChange: ((value: string) => void) | undefined, localeCode: SupportedLocaleCode, languageLocaleCode: SupportedLocaleCode, initialMonthInFocus: Date | undefined, onClickDate?: (date: Date) => void) => {
3
+ export declare const useTravelDateInput: (value: string | undefined, onValueChange: ((value: string) => void) | undefined, localeCode: SupportedLocaleCode, languageLocaleCode: SupportedLocaleCode, visibleMonthValue: Date, onVisibleMonthChange: (visibleMonth: Date) => void, onClickDate?: (date: Date) => void) => {
4
4
  onClickDate: (date: Date) => void;
5
5
  onValueChangeByInputs: (value: string) => void;
6
6
  monthPickerButtonRef: import("react").RefObject<HTMLButtonElement | null>;
@@ -8,7 +8,7 @@ export declare const useTravelDateInput: (value: string | undefined, onValueChan
8
8
  monthPickerButtonLabel: string;
9
9
  visiblePanel: VisiblePanel;
10
10
  setVisiblePanel: import("react").Dispatch<import("react").SetStateAction<VisiblePanel>>;
11
- setVisibleMonth: import("react").Dispatch<import("react").SetStateAction<Date>>;
11
+ setVisibleMonth: (requested: Date) => void;
12
12
  visibleMonthData: import("../../..").MonthData;
13
13
  todayIsInVisibleMonth: boolean;
14
14
  hoverDate: Date | undefined;
@@ -1,6 +1,6 @@
1
1
  import { SupportedLocaleCode } from "../../localize-date-format/LocaleMapper";
2
2
  import { TravelDateRangeInputValue, VisiblePanel } from "../types";
3
- export declare const useTravelDateRangeInput: (value: TravelDateRangeInputValue | undefined, onValueChange: ((value: TravelDateRangeInputValue) => void) | undefined, localeCode: SupportedLocaleCode, languageLocaleCode: SupportedLocaleCode, initialMonthInFocus: Date | undefined, fieldLastInFocus: "startDate" | "endDate" | undefined, setFieldLastInFocus: (field: "startDate" | "endDate" | undefined) => void, moveFocusToField: (field: "startDate" | "endDate") => void, hideCalendar: undefined | (() => void)) => {
3
+ export declare const useTravelDateRangeInput: (value: TravelDateRangeInputValue | undefined, onValueChange: ((value: TravelDateRangeInputValue) => void) | undefined, localeCode: SupportedLocaleCode, languageLocaleCode: SupportedLocaleCode, visibleMonthValue: Date, onVisibleMonthChange: (visibleMonth: Date) => void, fieldLastInFocus: "startDate" | "endDate" | undefined, setFieldLastInFocus: (field: "startDate" | "endDate" | undefined) => void, moveFocusToField: (field: "startDate" | "endDate") => void, hideCalendar: undefined | (() => void)) => {
4
4
  onClickDate: (date: Date) => void;
5
5
  onValueChangeByInputs: (value: TravelDateRangeInputValue) => void;
6
6
  isValidDateRange: boolean;
@@ -9,7 +9,7 @@ export declare const useTravelDateRangeInput: (value: TravelDateRangeInputValue
9
9
  monthPickerButtonLabel: string;
10
10
  visiblePanel: VisiblePanel;
11
11
  setVisiblePanel: import("react").Dispatch<import("react").SetStateAction<VisiblePanel>>;
12
- setVisibleMonth: import("react").Dispatch<import("react").SetStateAction<Date>>;
12
+ setVisibleMonth: (requested: Date) => void;
13
13
  visibleMonthData: import("../../..").MonthData;
14
14
  todayIsInVisibleMonth: boolean;
15
15
  hoverDate: Date | undefined;
@@ -0,0 +1,41 @@
1
+ export interface VisibleMonthState {
2
+ visibleMonth: Date;
3
+ onVisibleMonthChange: (visibleMonth: Date) => void;
4
+ }
5
+ /**
6
+ * Keeps the visible month of a travel calendar, for when nothing else needs to
7
+ * know it.
8
+ *
9
+ * The travel calendars do not keep their visible month themselves; the app
10
+ * passes it in as visibleMonth and changes it in onVisibleMonthChange. This is
11
+ * that state, returned as those two props, so it can be spread onto the
12
+ * calendar. Keep the state elsewhere instead when something else depends on
13
+ * the month, such as data loaded for it.
14
+ *
15
+ * It starts at the current month, not at the month of a value. Before the
16
+ * month was controlled, a calendar without initialMonthInFocus opened on the
17
+ * month of its value. To keep that, start from the value's date:
18
+ *
19
+ * const visibleMonthState = useVisibleMonthState(
20
+ * parseLocalizedDateString(value, localeCode),
21
+ * );
22
+ *
23
+ * parseLocalizedDateString returns undefined for an empty or incomplete
24
+ * value, which falls back to the current month. Only the first render's
25
+ * month is used, like any initial state.
26
+ *
27
+ * A calendar that had initialMonthInFocus passes that month instead:
28
+ * useVisibleMonthState(initialMonthInFocus).
29
+ *
30
+ * @param initialMonth The month to show first. Defaults to the current month.
31
+ *
32
+ * @example
33
+ * const visibleMonthState = useVisibleMonthState();
34
+ *
35
+ * <TravelDateInput
36
+ * value={value}
37
+ * onValueChange={setValue}
38
+ * {...visibleMonthState}
39
+ * />
40
+ */
41
+ export declare const useVisibleMonthState: (initialMonth?: Date) => VisibleMonthState;
package/dist/index.d.ts CHANGED
@@ -35,6 +35,7 @@ export * from "./components/calendar-types/travel-date-calendar-panel/TravelDate
35
35
  export type { TravelCalendarSizeVariant } from "./features/travel-calendar/components/TravelCalendar";
36
36
  export * from "./components/calendar-types/travel-date-range-calendar/TravelDateRangeCalendar";
37
37
  export * from "./features/travel-calendar/hooks/DisabledDateHooks";
38
+ export * from "./features/travel-calendar/hooks/UseVisibleMonthState";
38
39
  export * from "./features/travel-calendar/types";
39
40
  export * from "./features/localize-date-format/LocalizedDateFormatter";
40
41
  export * from "./features/localize-date-format/LocalizedDateParser";