@povio/ui 2.2.10 → 2.2.12

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.
Files changed (25) hide show
  1. package/dist/components/inputs/DateTime/DatePicker/DatePicker.d.ts +1 -0
  2. package/dist/components/inputs/DateTime/DatePicker/DatePicker.js +27 -6
  3. package/dist/components/inputs/DateTime/DateRangePicker/DateRangePicker.js +1 -1
  4. package/dist/components/inputs/DateTime/DateTimePicker/DateTimePicker.d.ts +1 -0
  5. package/dist/components/inputs/DateTime/DateTimePicker/DateTimePicker.js +4 -3
  6. package/dist/components/inputs/DateTime/TimePicker/TimePicker.js +1 -1
  7. package/dist/components/inputs/DateTime/shared/Calendar.d.ts +3 -1
  8. package/dist/components/inputs/DateTime/shared/Calendar.js +42 -8
  9. package/dist/components/inputs/DateTime/shared/DateField.d.ts +3 -1
  10. package/dist/components/inputs/DateTime/shared/DateField.js +36 -30
  11. package/dist/components/inputs/DateTime/shared/DatePickerInput.d.ts +2 -1
  12. package/dist/components/inputs/DateTime/shared/DatePickerInput.js +7 -3
  13. package/dist/components/inputs/DateTime/shared/MonthPicker.d.ts +3 -1
  14. package/dist/components/inputs/DateTime/shared/MonthPicker.js +40 -9
  15. package/dist/components/inputs/DateTime/shared/YearPicker.d.ts +3 -1
  16. package/dist/components/inputs/DateTime/shared/YearPicker.js +42 -9
  17. package/dist/components/overlays/BottomSheet/BottomSheet.js +1 -1
  18. package/dist/components/segment/Segment.js +2 -1
  19. package/dist/components/segment/SegmentItem.d.ts +1 -1
  20. package/dist/components/segment/SegmentItem.js +5 -2
  21. package/dist/components/segment/segment.types.d.ts +1 -0
  22. package/dist/hooks/useIntersectionObserver.d.ts +1 -1
  23. package/dist/hooks/useIntersectionObserver.js +11 -5
  24. package/dist/index.js +1 -1
  25. package/package.json +3 -3
@@ -20,6 +20,7 @@ interface DatePickerBaseProps extends FormFieldProps, InputVariantProps, Omit<Da
20
20
  shouldUpdateDateOnMonthYearChange?: boolean;
21
21
  granularity?: DatePickerGranularity;
22
22
  autoFixYear?: boolean;
23
+ format?: string;
23
24
  }
24
25
  export interface DatePickerProps extends Omit<DatePickerBaseProps, "value" | "onChange" | "minValue" | "maxValue"> {
25
26
  value?: string | null;
@@ -1,14 +1,14 @@
1
1
  import { UIConfig } from "../../../../config/uiConfig.context.js";
2
2
  import { Calendar } from "../shared/Calendar.js";
3
+ import { DateTimeUtils } from "../../../../utils/date-time.utils.js";
3
4
  import { DatePickerInput } from "../shared/DatePickerInput.js";
4
5
  import { DateTimeDialog } from "../shared/DateTimeDialog.js";
5
6
  import { DateTimeDialogFooter } from "../shared/DateTimeDialogFooter.js";
6
7
  import { FormField } from "../../FormField/FormField.js";
7
8
  import { TooltipWrapper } from "../../shared/TooltipWrapper.js";
8
- import { DateTimeUtils } from "../../../../utils/date-time.utils.js";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { clsx } from "clsx";
11
- import { useImperativeHandle, useMemo, useRef } from "react";
11
+ import { useCallback, useEffect, useImperativeHandle, useMemo, useRef } from "react";
12
12
  import { useDatePicker, useLocale } from "react-aria";
13
13
  import { mergeRefs } from "@react-aria/utils";
14
14
  import { Controller } from "react-hook-form";
@@ -18,15 +18,15 @@ import { useCalendarState, useDatePickerState } from "react-stately";
18
18
  //#region src/components/inputs/DateTime/DatePicker/DatePicker.tsx
19
19
  var DatePickerBase = (props) => {
20
20
  const ui = UIConfig.useConfig();
21
- const { ref, label, tooltipText, helperText, isRequired, rightContent, isDirty, isDisabled, headerClassName, errorClassName, isHeaderHidden, error, onChange, value, disableDropdown, className, placeholder, inputClassName, as = ui.input.as, hideLabel = ui.input.hideLabel, variant = ui.input.variant, size = ui.input.size, isClearable = ui.input.isClearable, todayIcon = ui.dateInput.todayIcon, shouldForceLeadingZeros = ui.dateInput.shouldForceLeadingZeros, disableManualEntry = ui.dateInput.disableManualEntry, shouldUpdateDateOnMonthYearChange = ui.dateInput.shouldUpdateDateOnMonthYearChange, granularity = ui.dateInput.granularity, autoFixYear = ui.dateInput.autoFixYear, ...rest } = props;
22
- const normalizeByGranularity = (date) => {
21
+ const { ref, label, tooltipText, helperText, isRequired, rightContent, isDirty, isDisabled, headerClassName, errorClassName, isHeaderHidden, error, onChange, value, disableDropdown, className, placeholder, inputClassName, as = ui.input.as, hideLabel = ui.input.hideLabel, variant = ui.input.variant, size = ui.input.size, isClearable = ui.input.isClearable, todayIcon = ui.dateInput.todayIcon, shouldForceLeadingZeros = ui.dateInput.shouldForceLeadingZeros, disableManualEntry = ui.dateInput.disableManualEntry, shouldUpdateDateOnMonthYearChange = ui.dateInput.shouldUpdateDateOnMonthYearChange, granularity = ui.dateInput.granularity, autoFixYear = ui.dateInput.autoFixYear, format, ...rest } = props;
22
+ const normalizeByGranularity = useCallback((date) => {
23
23
  if (granularity === "year") return date.set({
24
24
  month: 1,
25
25
  day: 1
26
26
  });
27
27
  if (granularity === "month") return date.set({ day: 1 });
28
28
  return date;
29
- };
29
+ }, [granularity]);
30
30
  const formFieldProps = {
31
31
  error,
32
32
  label,
@@ -115,11 +115,29 @@ var DatePickerBase = (props) => {
115
115
  dialogState.setValue(updatedDate);
116
116
  calendarState.setFocusedDate(updatedDate);
117
117
  };
118
+ const onMonthYearCommit = () => {
119
+ onApply();
120
+ };
118
121
  const onTodayPress = () => {
119
122
  const todayValue = normalizeByGranularity(today(getLocalTimeZone()));
120
123
  dialogState.setValue(todayValue);
121
124
  calendarState.setFocusedDate(todayValue);
122
125
  };
126
+ useEffect(() => {
127
+ if (!state.isOpen) return;
128
+ if (granularity !== "year") return;
129
+ if (dialogState.value) return;
130
+ const defaultYearValue = normalizeByGranularity(today(getLocalTimeZone()));
131
+ dialogState.setValue(defaultYearValue);
132
+ calendarState.setFocusedDate(defaultYearValue);
133
+ }, [
134
+ state.isOpen,
135
+ granularity,
136
+ dialogState.value,
137
+ dialogState,
138
+ calendarState,
139
+ normalizeByGranularity
140
+ ]);
123
141
  const onOpenChange = (isOpen) => {
124
142
  state.toggle();
125
143
  if (state.value) calendarState.setFocusedDate(normalizeByGranularity(state.value));
@@ -156,7 +174,8 @@ var DatePickerBase = (props) => {
156
174
  placeholder,
157
175
  className: inputClassName,
158
176
  dateGranularity: granularity,
159
- autoFixYear
177
+ autoFixYear,
178
+ format
160
179
  }), (!disableDropdown || disableManualEntry) && /* @__PURE__ */ jsx(DateTimeDialog, {
161
180
  footer: /* @__PURE__ */ jsx(DateTimeDialogFooter, {
162
181
  isDisabled,
@@ -174,6 +193,8 @@ var DatePickerBase = (props) => {
174
193
  calendarProps,
175
194
  onApply,
176
195
  onMonthYearChange,
196
+ onMonthYearCommit,
197
+ selectedDate: dialogState.value ?? null,
177
198
  granularity
178
199
  })
179
200
  })]
@@ -1,12 +1,12 @@
1
1
  import { Typography } from "../../../text/Typography/Typography.js";
2
2
  import { UIConfig } from "../../../../config/uiConfig.context.js";
3
3
  import "../../../../config/i18n.js";
4
+ import { DateTimeUtils } from "../../../../utils/date-time.utils.js";
4
5
  import { DatePickerInput } from "../shared/DatePickerInput.js";
5
6
  import { DateTimeDialog } from "../shared/DateTimeDialog.js";
6
7
  import { DateTimeDialogFooter } from "../shared/DateTimeDialogFooter.js";
7
8
  import { FormField } from "../../FormField/FormField.js";
8
9
  import { TooltipWrapper } from "../../shared/TooltipWrapper.js";
9
- import { DateTimeUtils } from "../../../../utils/date-time.utils.js";
10
10
  import { RangeCalendar } from "../shared/RangeCalendar.js";
11
11
  import { jsx, jsxs } from "react/jsx-runtime";
12
12
  import { clsx } from "clsx";
@@ -19,6 +19,7 @@ interface DateTimePickerBaseProps extends FormFieldProps, InputVariantProps, Omi
19
19
  shouldUpdateDateOnMonthYearChange?: boolean;
20
20
  timeZone?: string;
21
21
  autoFixYear?: boolean;
22
+ format?: string;
22
23
  }
23
24
  export interface DateTimePickerProps extends Omit<DateTimePickerBaseProps, "value" | "onChange"> {
24
25
  value?: string | null;
@@ -1,11 +1,11 @@
1
1
  import { UIConfig } from "../../../../config/uiConfig.context.js";
2
2
  import { Calendar } from "../shared/Calendar.js";
3
+ import { DateTimeUtils } from "../../../../utils/date-time.utils.js";
3
4
  import { DatePickerInput } from "../shared/DatePickerInput.js";
4
5
  import { DateTimeDialog } from "../shared/DateTimeDialog.js";
5
6
  import { DateTimeDialogFooter } from "../shared/DateTimeDialogFooter.js";
6
7
  import { FormField } from "../../FormField/FormField.js";
7
8
  import { TooltipWrapper } from "../../shared/TooltipWrapper.js";
8
- import { DateTimeUtils } from "../../../../utils/date-time.utils.js";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { useEffect, useImperativeHandle, useMemo, useRef } from "react";
11
11
  import { useDatePicker, useLocale } from "react-aria";
@@ -16,7 +16,7 @@ import { useCalendarState, useDatePickerState } from "react-stately";
16
16
  //#region src/components/inputs/DateTime/DateTimePicker/DateTimePicker.tsx
17
17
  var DateTimePickerBase = (props) => {
18
18
  const ui = UIConfig.useConfig();
19
- const { ref, label, tooltipText, helperText, isRequired, rightContent, isDisabled, isDirty, headerClassName, errorClassName, isHeaderHidden, error, onChange, value, disableDropdown, isTimeOptional, placeholder, inputClassName, hideLabel = ui.input.hideLabel, variant = ui.input.variant, as = ui.input.as, size = ui.input.size, isClearable = ui.input.isClearable, todayIcon = ui.dateInput.todayIcon, shouldForceLeadingZeros = ui.dateInput.shouldForceLeadingZeros, disableManualEntry = ui.dateInput.disableManualEntry, shouldUpdateDateOnMonthYearChange = ui.dateInput.shouldUpdateDateOnMonthYearChange, timeZone, autoFixYear = ui.dateInput.autoFixYear, ...rest } = props;
19
+ const { ref, label, tooltipText, helperText, isRequired, rightContent, isDisabled, isDirty, headerClassName, errorClassName, isHeaderHidden, error, onChange, value, disableDropdown, isTimeOptional, placeholder, inputClassName, hideLabel = ui.input.hideLabel, variant = ui.input.variant, as = ui.input.as, size = ui.input.size, isClearable = ui.input.isClearable, todayIcon = ui.dateInput.todayIcon, shouldForceLeadingZeros = ui.dateInput.shouldForceLeadingZeros, disableManualEntry = ui.dateInput.disableManualEntry, shouldUpdateDateOnMonthYearChange = ui.dateInput.shouldUpdateDateOnMonthYearChange, timeZone, autoFixYear = ui.dateInput.autoFixYear, format, ...rest } = props;
20
20
  let effectiveTimeZone = getLocalTimeZone();
21
21
  if (timeZone) effectiveTimeZone = timeZone;
22
22
  const formFieldProps = {
@@ -173,7 +173,8 @@ var DateTimePickerBase = (props) => {
173
173
  className: inputClassName,
174
174
  timeZone: effectiveTimeZone,
175
175
  autoFixYear,
176
- isTimeOptional
176
+ isTimeOptional,
177
+ format
177
178
  }), (!disableDropdown || disableManualEntry) && /* @__PURE__ */ jsx(DateTimeDialog, {
178
179
  footer: /* @__PURE__ */ jsx(DateTimeDialogFooter, {
179
180
  isDisabled,
@@ -1,10 +1,10 @@
1
1
  import { UIConfig } from "../../../../config/uiConfig.context.js";
2
2
  import { TimePickerForm } from "../shared/TimePickerForm.js";
3
+ import { DateTimeUtils } from "../../../../utils/date-time.utils.js";
3
4
  import { DateTimeDialog } from "../shared/DateTimeDialog.js";
4
5
  import { DateTimeDialogFooter } from "../shared/DateTimeDialogFooter.js";
5
6
  import { FormField } from "../../FormField/FormField.js";
6
7
  import { TooltipWrapper } from "../../shared/TooltipWrapper.js";
7
- import { DateTimeUtils } from "../../../../utils/date-time.utils.js";
8
8
  import { TimePickerInput } from "../shared/TimePickerInput.js";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { clsx } from "clsx";
@@ -16,8 +16,10 @@ type CalendarProps = DateTimeCalendarProps & {
16
16
  calendarProps: Omit<CalendarStateOptions, "locale" | "createCalendar">;
17
17
  onApply: () => void;
18
18
  onMonthYearChange?: (selectedDate: CalendarDate) => void;
19
+ onMonthYearCommit?: () => void;
20
+ selectedDate?: CalendarDate | null;
19
21
  granularity?: "day" | "month" | "year";
20
22
  };
21
23
  export type ToggleState = "month" | "year" | "time";
22
- export declare const Calendar: ({ className, includesTime, datePickerState, hourCycle, onApply, onMonthYearChange, granularity, ...props }: CalendarProps) => import("react/jsx-runtime").JSX.Element;
24
+ export declare const Calendar: ({ className, includesTime, datePickerState, hourCycle, onApply, onMonthYearChange, onMonthYearCommit, selectedDate, granularity, ...props }: CalendarProps) => import("react/jsx-runtime").JSX.Element;
23
25
  export {};
@@ -8,7 +8,7 @@ import { clsx } from "clsx";
8
8
  import { useEffect, useState } from "react";
9
9
  import { useCalendar } from "@react-aria/calendar";
10
10
  //#region src/components/inputs/DateTime/shared/Calendar.tsx
11
- var Calendar = ({ className, includesTime, datePickerState, hourCycle, onApply, onMonthYearChange, granularity = "day", ...props }) => {
11
+ var Calendar = ({ className, includesTime, datePickerState, hourCycle, onApply, onMonthYearChange, onMonthYearCommit, selectedDate, granularity = "day", ...props }) => {
12
12
  const [toggleState, setToggleState] = useState(() => {
13
13
  if (granularity === "year") return "year";
14
14
  if (granularity === "month") return "month";
@@ -52,6 +52,30 @@ var Calendar = ({ className, includesTime, datePickerState, hourCycle, onApply,
52
52
  }
53
53
  setToggleState("year");
54
54
  };
55
+ const handleHeaderMonthNavigation = (months) => {
56
+ if (toggleState) return;
57
+ if (!onMonthYearChange) return;
58
+ const focusedDate = props.state.focusedDate;
59
+ if (months === -1) {
60
+ onMonthYearChange(focusedDate.subtract({ months: 1 }));
61
+ return;
62
+ }
63
+ onMonthYearChange(focusedDate.add({ months: 1 }));
64
+ };
65
+ const headerPrevButtonProps = {
66
+ ...prevButtonProps,
67
+ onPress: (event) => {
68
+ prevButtonProps.onPress?.(event);
69
+ handleHeaderMonthNavigation(-1);
70
+ }
71
+ };
72
+ const headerNextButtonProps = {
73
+ ...nextButtonProps,
74
+ onPress: (event) => {
75
+ nextButtonProps.onPress?.(event);
76
+ handleHeaderMonthNavigation(1);
77
+ }
78
+ };
55
79
  const handleDateChange = () => {
56
80
  if (includesTime && !datePickerState.value) setToggleState("time");
57
81
  else onApply();
@@ -61,12 +85,16 @@ var Calendar = ({ className, includesTime, datePickerState, hourCycle, onApply,
61
85
  if (granularity === "month") return /* @__PURE__ */ jsx(MonthPicker, {
62
86
  state: props.state,
63
87
  onSelectionChange: onMonthSelectionChange,
64
- onDateChange: onMonthYearChange
88
+ onDateChange: onMonthYearChange,
89
+ onCommit: onMonthYearCommit,
90
+ selectedDate
65
91
  });
66
92
  if (granularity === "year") return /* @__PURE__ */ jsx(YearPicker, {
67
93
  state: props.state,
68
94
  onSelectionChange: onYearSelectionChange,
69
- onDateChange: onMonthYearChange
95
+ onDateChange: onMonthYearChange,
96
+ onCommit: onMonthYearCommit,
97
+ selectedDate
70
98
  });
71
99
  return /* @__PURE__ */ jsx(CalendarGrid, {
72
100
  state: props.state,
@@ -77,18 +105,24 @@ var Calendar = ({ className, includesTime, datePickerState, hourCycle, onApply,
77
105
  if (granularity === "year") return /* @__PURE__ */ jsx(YearPicker, {
78
106
  state: props.state,
79
107
  onSelectionChange: onYearSelectionChange,
80
- onDateChange: onMonthYearChange
108
+ onDateChange: onMonthYearChange,
109
+ onCommit: onMonthYearCommit,
110
+ selectedDate
81
111
  });
82
112
  return /* @__PURE__ */ jsx(MonthPicker, {
83
113
  state: props.state,
84
114
  onSelectionChange: onMonthSelectionChange,
85
- onDateChange: onMonthYearChange
115
+ onDateChange: onMonthYearChange,
116
+ onCommit: onMonthYearCommit,
117
+ selectedDate
86
118
  });
87
119
  }
88
120
  if (toggleState === "year") return /* @__PURE__ */ jsx(YearPicker, {
89
121
  state: props.state,
90
122
  onSelectionChange: onYearSelectionChange,
91
- onDateChange: onMonthYearChange
123
+ onDateChange: onMonthYearChange,
124
+ onCommit: onMonthYearCommit,
125
+ selectedDate
92
126
  });
93
127
  if (toggleState === "time" && includesTime) return /* @__PURE__ */ jsx("div", {
94
128
  className: "flex h-72 w-80 items-center justify-center",
@@ -102,8 +136,8 @@ var Calendar = ({ className, includesTime, datePickerState, hourCycle, onApply,
102
136
  children: [/* @__PURE__ */ jsx(CalendarHeader, {
103
137
  calendarState: props.state,
104
138
  datePickerState,
105
- prevButtonProps,
106
- nextButtonProps,
139
+ prevButtonProps: headerPrevButtonProps,
140
+ nextButtonProps: headerNextButtonProps,
107
141
  includesTime,
108
142
  hourCycle,
109
143
  granularity,
@@ -11,6 +11,8 @@ interface DateFieldProps extends Omit<DateFieldStateOptions, "locale" | "createC
11
11
  disableManualEntry?: boolean;
12
12
  dateGranularity?: "day" | "month" | "year";
13
13
  isTimeOptional?: boolean;
14
+ format?: string;
15
+ timeZone?: string;
14
16
  }
15
- export declare const DateField: ({ ref, onClearChange, hidePlaceholder, isDisabled, disableManualEntry, dateGranularity, isTimeOptional, ...props }: DateFieldProps) => import("react/jsx-runtime").JSX.Element;
17
+ export declare const DateField: ({ ref, onClearChange, hidePlaceholder, isDisabled, disableManualEntry, dateGranularity, isTimeOptional, format, timeZone, ...props }: DateFieldProps) => import("react/jsx-runtime").JSX.Element;
16
18
  export {};
@@ -1,12 +1,14 @@
1
1
  import { DateSegmentItem } from "./DateSegmentItem.js";
2
- import { jsx } from "react/jsx-runtime";
2
+ import { DateTimeUtils } from "../../../../utils/date-time.utils.js";
3
+ import { Fragment, jsx } from "react/jsx-runtime";
3
4
  import { clsx } from "clsx";
4
- import { useEffect, useImperativeHandle, useRef } from "react";
5
+ import { useCallback, useEffect, useImperativeHandle, useMemo, useRef } from "react";
5
6
  import { useDateField, useLocale } from "react-aria";
6
7
  import { createCalendar } from "@internationalized/date";
8
+ import { DateTime } from "luxon";
7
9
  import { useDateFieldState } from "react-stately";
8
10
  //#region src/components/inputs/DateTime/shared/DateField.tsx
9
- var DateField = ({ ref, onClearChange, hidePlaceholder, isDisabled, disableManualEntry, dateGranularity = "day", isTimeOptional = false, ...props }) => {
11
+ var DateField = ({ ref, onClearChange, hidePlaceholder, isDisabled, disableManualEntry, dateGranularity = "day", isTimeOptional = false, format, timeZone, ...props }) => {
10
12
  const { locale } = useLocale();
11
13
  const state = useDateFieldState({
12
14
  ...props,
@@ -21,31 +23,20 @@ var DateField = ({ ref, onClearChange, hidePlaceholder, isDisabled, disableManua
21
23
  isDisabled,
22
24
  isReadOnly: disableManualEntry
23
25
  }, state, dataFieldRef);
24
- const isSegmentVisible = (segmentType) => {
26
+ const isSegmentVisible = useCallback((segmentType) => {
25
27
  if (dateGranularity === "day") return true;
26
28
  if (dateGranularity === "month") return segmentType !== "day";
27
29
  if (dateGranularity === "year") return segmentType === "year";
28
30
  return true;
29
- };
30
- const segmentsToRender = state.segments.filter((segment, index, segments) => {
31
- if (segment.type !== "literal") return isSegmentVisible(segment.type);
32
- let hasVisiblePrevious = false;
33
- for (let i = index - 1; i >= 0; i--) {
34
- const previousSegment = segments[i];
35
- if (previousSegment.type === "literal") continue;
36
- hasVisiblePrevious = isSegmentVisible(previousSegment.type);
37
- break;
38
- }
39
- if (!hasVisiblePrevious) return false;
40
- let hasVisibleNext = false;
41
- for (let i = index + 1; i < segments.length; i++) {
42
- const nextSegment = segments[i];
43
- if (nextSegment.type === "literal") continue;
44
- hasVisibleNext = isSegmentVisible(nextSegment.type);
45
- break;
46
- }
47
- return hasVisibleNext;
48
- });
31
+ }, [dateGranularity]);
32
+ const getSegmentsToRender = useCallback(() => {
33
+ const segments = [];
34
+ for (const segment of state.segments) if (segment.type !== "literal") {
35
+ if (isSegmentVisible(segment.type)) segments.push(segment);
36
+ } else if (segments.length > 0 && segments.at(-1)?.type !== "literal") segments.push(segment);
37
+ return segments;
38
+ }, [state.segments, isSegmentVisible]);
39
+ const segmentsToRender = useMemo(() => getSegmentsToRender(), [getSegmentsToRender]);
49
40
  const autoFixYear = () => {
50
41
  const monthSegment = state.segments.find((segment) => segment.type === "month");
51
42
  const daySegment = state.segments.find((segment) => segment.type === "day");
@@ -105,6 +96,26 @@ var DateField = ({ ref, onClearChange, hidePlaceholder, isDisabled, disableManua
105
96
  useEffect(() => {
106
97
  onClearChange?.(state.segments.some((segment) => segment.type !== "literal" && segment.isPlaceholder === false));
107
98
  }, [state.segments, onClearChange]);
99
+ const getFormattedDisplayValue = () => {
100
+ if (!disableManualEntry) return null;
101
+ if (!format) return null;
102
+ if (!state.value) return null;
103
+ const jsDate = DateTimeUtils.fromDateValueToLocal(state.value, timeZone);
104
+ const formattedDate = DateTime.fromJSDate(jsDate);
105
+ if (!formattedDate.isValid) return null;
106
+ return formattedDate.toFormat(format);
107
+ };
108
+ const formattedDisplayValue = getFormattedDisplayValue();
109
+ let fieldContent = /* @__PURE__ */ jsx(Fragment, { children: segmentsToRender.map((segment, i) => /* @__PURE__ */ jsx(DateSegmentItem, {
110
+ segment,
111
+ state,
112
+ isDisabled,
113
+ hidePlaceholder
114
+ }, i)) });
115
+ if (formattedDisplayValue) fieldContent = /* @__PURE__ */ jsx("span", {
116
+ className: clsx("select-none", isDisabled && "text-interactive-text-secondary-disabled"),
117
+ children: formattedDisplayValue
118
+ });
108
119
  return /* @__PURE__ */ jsx("div", {
109
120
  ...fieldProps,
110
121
  ref: dataFieldRef,
@@ -112,12 +123,7 @@ var DateField = ({ ref, onClearChange, hidePlaceholder, isDisabled, disableManua
112
123
  className: clsx("relative w-full", disableManualEntry && "pointer-events-none"),
113
124
  children: /* @__PURE__ */ jsx("div", {
114
125
  className: "flex",
115
- children: segmentsToRender.map((segment, i) => /* @__PURE__ */ jsx(DateSegmentItem, {
116
- segment,
117
- state,
118
- isDisabled,
119
- hidePlaceholder
120
- }, i))
126
+ children: fieldContent
121
127
  })
122
128
  });
123
129
  };
@@ -29,6 +29,7 @@ interface DatePickerInputProps extends InputVariantProps {
29
29
  timeZone?: string;
30
30
  autoFixYear?: boolean;
31
31
  isTimeOptional?: boolean;
32
+ format?: string;
32
33
  }
33
- export declare const DatePickerInput: ({ ref, as, groupProps, fieldProps, endFieldProps, buttonProps, isDisabled, isInvalid, disableDropdown, variant, size, isDateTime, isClearable, headerProps, todayIcon, isDirty, isRequired, disableManualEntry, placeholder, className, onOpenDropdown, dateGranularity, timeZone, autoFixYear, isTimeOptional, ...props }: DatePickerInputProps) => import("react/jsx-runtime").JSX.Element;
34
+ export declare const DatePickerInput: ({ ref, as, groupProps, fieldProps, endFieldProps, buttonProps, isDisabled, isInvalid, disableDropdown, variant, size, isDateTime, isClearable, headerProps, todayIcon, isDirty, isRequired, disableManualEntry, placeholder, className, onOpenDropdown, dateGranularity, timeZone, autoFixYear, isTimeOptional, format, ...props }: DatePickerInputProps) => import("react/jsx-runtime").JSX.Element;
34
35
  export {};
@@ -16,7 +16,7 @@ import { Button } from "react-aria-components";
16
16
  import { useFocusVisible, useFocusWithin, useHover } from "react-aria";
17
17
  import { getLocalTimeZone, now, toCalendarDateTime, today } from "@internationalized/date";
18
18
  //#region src/components/inputs/DateTime/shared/DatePickerInput.tsx
19
- var DatePickerInput = ({ ref, as, groupProps, fieldProps, endFieldProps, buttonProps, isDisabled, isInvalid, disableDropdown, variant, size, isDateTime, isClearable, headerProps, todayIcon, isDirty, isRequired, disableManualEntry, placeholder, className, onOpenDropdown, dateGranularity = "day", timeZone, autoFixYear, isTimeOptional = false, ...props }) => {
19
+ var DatePickerInput = ({ ref, as, groupProps, fieldProps, endFieldProps, buttonProps, isDisabled, isInvalid, disableDropdown, variant, size, isDateTime, isClearable, headerProps, todayIcon, isDirty, isRequired, disableManualEntry, placeholder, className, onOpenDropdown, dateGranularity = "day", timeZone, autoFixYear, isTimeOptional = false, format, ...props }) => {
20
20
  let effectiveTimeZone = getLocalTimeZone();
21
21
  if (timeZone) effectiveTimeZone = timeZone;
22
22
  const inputBaseCva = UIStyle.useCva("input.baseCva", inputBase);
@@ -123,7 +123,9 @@ var DatePickerInput = ({ ref, as, groupProps, fieldProps, endFieldProps, buttonP
123
123
  hidePlaceholder,
124
124
  disableManualEntry,
125
125
  dateGranularity,
126
- isTimeOptional
126
+ isTimeOptional,
127
+ format,
128
+ timeZone: effectiveTimeZone
127
129
  }), endFieldProps && /* @__PURE__ */ jsxs(Fragment, { children: [!((as === "floating" || as === "filter") && hidePlaceholder) && /* @__PURE__ */ jsx("span", {
128
130
  className: clsx("pointer-events-none select-none", isDisabled && "text-interactive-text-secondary-disabled"),
129
131
  children: "–"
@@ -135,7 +137,9 @@ var DatePickerInput = ({ ref, as, groupProps, fieldProps, endFieldProps, buttonP
135
137
  onClearChange,
136
138
  hidePlaceholder,
137
139
  disableManualEntry,
138
- isTimeOptional
140
+ isTimeOptional,
141
+ format,
142
+ timeZone: effectiveTimeZone
139
143
  })] })] })
140
144
  ]
141
145
  })]
@@ -5,5 +5,7 @@ export interface MonthPickerProps {
5
5
  state: CalendarState | RangeCalendarState;
6
6
  onSelectionChange: (key: Set<Key>) => void;
7
7
  onDateChange?: (selectedDate: CalendarDate) => void;
8
+ onCommit?: () => void;
9
+ selectedDate?: CalendarDate | null;
8
10
  }
9
- export declare const MonthPicker: ({ state, onSelectionChange, onDateChange }: MonthPickerProps) => import("react/jsx-runtime").JSX.Element;
11
+ export declare const MonthPicker: ({ state, onSelectionChange, onDateChange, onCommit, selectedDate }: MonthPickerProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,10 +1,11 @@
1
1
  import { Typography } from "../../../text/Typography/Typography.js";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { clsx } from "clsx";
4
+ import { useRef } from "react";
4
5
  import { ListBox, ListBoxItem } from "react-aria-components";
5
6
  import { useDateFormatter } from "@react-aria/i18n";
6
7
  //#region src/components/inputs/DateTime/shared/MonthPicker.tsx
7
- var MonthPicker = ({ state, onSelectionChange, onDateChange }) => {
8
+ var MonthPicker = ({ state, onSelectionChange, onDateChange, onCommit, selectedDate }) => {
8
9
  const formatter = useDateFormatter({
9
10
  month: "long",
10
11
  timeZone: state.timeZone
@@ -23,35 +24,65 @@ var MonthPicker = ({ state, onSelectionChange, onDateChange }) => {
23
24
  if (state.maxValue && state.focusedDate.set({ month }) > state.maxValue) return true;
24
25
  return false;
25
26
  };
27
+ const getSelectedMonth = () => {
28
+ if (!selectedDate) return null;
29
+ return selectedDate.month;
30
+ };
31
+ const selectedMonth = getSelectedMonth();
32
+ const lastPressedMonthRef = useRef(null);
33
+ const listBoxSelectionProps = {};
34
+ if (selectedMonth !== null) listBoxSelectionProps.selectedKeys = [selectedMonth];
35
+ const parseMonthFromKey = (key) => {
36
+ if (typeof key === "number") return key;
37
+ if (typeof key === "string") {
38
+ const parsedMonth = Number.parseInt(key, 10);
39
+ if (!Number.isNaN(parsedMonth)) return parsedMonth;
40
+ }
41
+ return null;
42
+ };
26
43
  return /* @__PURE__ */ jsx(ListBox, {
27
44
  autoFocus: true,
28
45
  "aria-label": "Month",
29
46
  selectionMode: "single",
30
47
  layout: "grid",
31
- selectedKeys: [state.focusedDate.month],
48
+ ...listBoxSelectionProps,
32
49
  onSelectionChange: (key) => {
33
50
  if (key === "all") return;
34
- if (typeof [...key][0] === "number") {
35
- const selectedDate = state.focusedDate.set({ month: [...key][0] });
36
- state.setFocusedDate(selectedDate);
37
- onDateChange?.(selectedDate);
51
+ const selectedKey = [...key][0];
52
+ const selectedMonthValue = parseMonthFromKey(selectedKey);
53
+ if (selectedMonthValue !== null) {
54
+ const nextSelectedDate = state.focusedDate.set({ month: selectedMonthValue });
55
+ state.setFocusedDate(nextSelectedDate);
56
+ onDateChange?.(nextSelectedDate);
38
57
  }
39
58
  onSelectionChange(key);
40
59
  },
41
60
  className: "grid size-full min-h-72 grid-cols-3 grid-rows-4 gap-2 p-3 md:w-80",
42
61
  children: getMonths().map((month, index) => {
43
- const isDisabled = isMonthDisabled(index + 1);
62
+ const monthNumber = index + 1;
63
+ const isDisabled = isMonthDisabled(monthNumber);
44
64
  return /* @__PURE__ */ jsx(ListBoxItem, {
45
- id: index + 1,
65
+ id: monthNumber,
46
66
  textValue: month,
47
67
  isDisabled,
68
+ onPress: () => {
69
+ const isSecondConsecutivePress = lastPressedMonthRef.current === monthNumber;
70
+ lastPressedMonthRef.current = monthNumber;
71
+ const nextSelectedDate = state.focusedDate.set({ month: monthNumber });
72
+ state.setFocusedDate(nextSelectedDate);
73
+ onDateChange?.(nextSelectedDate);
74
+ if (!isSecondConsecutivePress) return;
75
+ if (selectedMonth === null) return;
76
+ if (monthNumber !== selectedMonth) return;
77
+ onCommit?.();
78
+ },
48
79
  className: clsx("flex items-center justify-center text-interactive-text-secondary-idle", "rounded-button-rounding-m border border-elevation-outline-default-1 border-solid bg-elevation-fill-default-1", "hover:text-interactive-text-secondary-hover", "selected:border-interactive-contained-primary-idle selected:bg-interactive-contained-primary-idle selected:text-interactive-text-secondary-idle-inverted", "focus:outline-none focus-visible:outline-1 focus-visible:outline-interactive-contained-primary-focus focus-visible:outline-offset-1", isDisabled ? "cursor-default opacity-50" : "cursor-pointer"),
49
80
  children: /* @__PURE__ */ jsx(Typography, {
50
81
  as: "span",
51
82
  size: "label-2",
52
83
  children: month
53
84
  })
54
- }, month);
85
+ }, monthNumber);
55
86
  })
56
87
  });
57
88
  };
@@ -5,6 +5,8 @@ interface YearPickerProps {
5
5
  state: CalendarState | RangeCalendarState;
6
6
  onSelectionChange: (key: Set<Key>) => void;
7
7
  onDateChange?: (selectedDate: CalendarDate) => void;
8
+ onCommit?: () => void;
9
+ selectedDate?: CalendarDate | null;
8
10
  }
9
- export declare const YearPicker: ({ state, onSelectionChange, onDateChange }: YearPickerProps) => import("react/jsx-runtime").JSX.Element;
11
+ export declare const YearPicker: ({ state, onSelectionChange, onDateChange, onCommit, selectedDate }: YearPickerProps) => import("react/jsx-runtime").JSX.Element;
10
12
  export {};
@@ -2,12 +2,12 @@ import { Typography } from "../../../text/Typography/Typography.js";
2
2
  import { useScrollableListBox } from "../../../../hooks/useScrollableListBox.js";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  import { clsx } from "clsx";
5
- import { useMemo } from "react";
5
+ import { useMemo, useRef } from "react";
6
6
  import { ListBox, ListBoxItem } from "react-aria-components";
7
7
  import { today } from "@internationalized/date";
8
8
  import { useDateFormatter } from "@react-aria/i18n";
9
9
  //#region src/components/inputs/DateTime/shared/YearPicker.tsx
10
- var YearPicker = ({ state, onSelectionChange, onDateChange }) => {
10
+ var YearPicker = ({ state, onSelectionChange, onDateChange, onCommit, selectedDate }) => {
11
11
  const formatter = useDateFormatter({
12
12
  year: "numeric",
13
13
  timeZone: state.timeZone
@@ -28,21 +28,42 @@ var YearPicker = ({ state, onSelectionChange, onDateChange }) => {
28
28
  if (state.maxValue && year > state.maxValue.year) return true;
29
29
  return false;
30
30
  };
31
- const selectedYear = years.findIndex((year) => year.value === state.focusedDate.year);
31
+ const getSelectedYearIndex = () => {
32
+ if (selectedDate) {
33
+ const selectedIndexFromValue = years.findIndex((year) => year.value === selectedDate.year);
34
+ if (selectedIndexFromValue >= 0) return selectedIndexFromValue;
35
+ }
36
+ const focusedYearIndex = years.findIndex((year) => year.value === state.focusedDate.year);
37
+ if (focusedYearIndex < 0) return null;
38
+ return focusedYearIndex;
39
+ };
40
+ const selectedYearIndex = getSelectedYearIndex();
41
+ const lastPressedYearIndexRef = useRef(null);
42
+ const listBoxSelectionProps = {};
43
+ if (selectedYearIndex !== null) listBoxSelectionProps.selectedKeys = [selectedYearIndex];
44
+ const parseYearIndexFromKey = (key) => {
45
+ if (typeof key === "number") return key;
46
+ if (typeof key === "string") {
47
+ const parsedIndex = Number.parseInt(key, 10);
48
+ if (!Number.isNaN(parsedIndex)) return parsedIndex;
49
+ }
50
+ return null;
51
+ };
32
52
  const { ref } = useScrollableListBox();
33
53
  return /* @__PURE__ */ jsx(ListBox, {
34
54
  ref,
35
55
  autoFocus: true,
36
56
  "aria-label": "Year",
37
57
  selectionMode: "single",
38
- selectedKeys: [selectedYear],
58
+ ...listBoxSelectionProps,
39
59
  onSelectionChange: (key) => {
40
60
  if (key === "all") return;
41
- const index = [...key][0];
42
- if (typeof index === "number") {
43
- const selectedDate = state.focusedDate.set({ year: years[index].value });
44
- state.setFocusedDate(selectedDate);
45
- onDateChange?.(selectedDate);
61
+ const selectedKey = [...key][0];
62
+ const selectedYearIndexValue = parseYearIndexFromKey(selectedKey);
63
+ if (selectedYearIndexValue !== null && years[selectedYearIndexValue]) {
64
+ const nextSelectedDate = state.focusedDate.set({ year: years[selectedYearIndexValue].value });
65
+ state.setFocusedDate(nextSelectedDate);
66
+ onDateChange?.(nextSelectedDate);
46
67
  }
47
68
  onSelectionChange(key);
48
69
  },
@@ -53,6 +74,18 @@ var YearPicker = ({ state, onSelectionChange, onDateChange }) => {
53
74
  id: index,
54
75
  textValue: year.formatted,
55
76
  isDisabled,
77
+ onPress: () => {
78
+ const isSecondConsecutivePress = lastPressedYearIndexRef.current === index;
79
+ lastPressedYearIndexRef.current = index;
80
+ const nextSelectedDate = state.focusedDate.set({ year: year.value });
81
+ state.setFocusedDate(nextSelectedDate);
82
+ onDateChange?.(nextSelectedDate);
83
+ if (!isSecondConsecutivePress) return;
84
+ if (!selectedDate) return;
85
+ if (selectedYearIndex === null) return;
86
+ if (index !== selectedYearIndex) return;
87
+ onCommit?.();
88
+ },
56
89
  className: clsx("flex px-4 py-2 text-interactive-text-secondary-idle outline-none", "border-elevation-outline-default-1 border-b border-solid bg-elevation-fill-default-1 last:border-b-0", "hover:text-interactive-text-secondary-hover", "selected:bg-interactive-contained-primary-idle selected:text-interactive-text-secondary-idle-inverted", "focus-visible:bg-interactive-contained-primary-focus focus-visible:text-interactive-text-secondary-idle-inverted", isDisabled ? "cursor-default opacity-50" : "cursor-pointer"),
57
90
  children: /* @__PURE__ */ jsx(Typography, {
58
91
  as: "span",
@@ -188,7 +188,7 @@ var BottomSheetBase = ({ isOpen, onOpenChange, onStateChange, isDismissable = tr
188
188
  initial: { opacity: 0 },
189
189
  exit: { opacity: 0 },
190
190
  ref: setFooterElement,
191
- className: clsx("pointer-events-auto absolute z-50 w-full bg-elevation-fill-default-2", "top-(--visual-viewport-height) left-0 translate-y-[calc(-100%-var(--scroll-position,0px))]"),
191
+ className: clsx("pointer-events-auto absolute z-50 w-full bg-elevation-fill-default-2", "top-[100dvh] left-0 translate-y-[calc(-100%-var(--scroll-position,0px))]"),
192
192
  children: footer
193
193
  })
194
194
  ] })
@@ -11,7 +11,7 @@ import { mergeRefs } from "@react-aria/utils";
11
11
  import { Controller } from "react-hook-form";
12
12
  import { useToggleGroupState } from "react-stately";
13
13
  //#region src/components/segment/Segment.tsx
14
- var SegmentBase = ({ className, items, error, onChange, value, defaultValue, ...rest }) => {
14
+ var SegmentBase = ({ className, items, error, onChange, value, defaultValue, segmentItemClassName, ...rest }) => {
15
15
  const props = {
16
16
  ...rest,
17
17
  defaultSelectedKeys: defaultValue ? Array.isArray(defaultValue) ? defaultValue : [defaultValue] : void 0,
@@ -49,6 +49,7 @@ var SegmentBase = ({ className, items, error, onChange, value, defaultValue, ...
49
49
  style: { gridTemplateColumns },
50
50
  children: items.map((item, index) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(SegmentItem, {
51
51
  ...item,
52
+ className: clsx(item.className, segmentItemClassName),
52
53
  isDisabled: item.isDisabled || props.isDisabled,
53
54
  isSelected: state.selectedKeys.has(item.id),
54
55
  onPress: () => {
@@ -1,2 +1,2 @@
1
1
  import { SegmentItem as SegmentItemProps } from './segment.types';
2
- export declare const SegmentItem: ({ label, icon, id, isDisabled, ...props }: SegmentItemProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const SegmentItem: ({ label, icon, id, isDisabled, className, ...props }: SegmentItemProps) => import("react/jsx-runtime").JSX.Element;
@@ -4,14 +4,17 @@ import { segmentItemCva } from "./segment.cva.js";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { ToggleButton } from "react-aria-components";
6
6
  //#region src/components/segment/SegmentItem.tsx
7
- var SegmentItem = ({ label, icon, id, isDisabled, ...props }) => {
7
+ var SegmentItem = ({ label, icon, id, isDisabled, className, ...props }) => {
8
8
  const IconComponent = icon;
9
9
  const iconOnly = !!(icon && !label);
10
10
  const segmentItemCva$1 = UIStyle.useCva("segment.itemCva", segmentItemCva);
11
11
  return /* @__PURE__ */ jsxs(ToggleButton, {
12
12
  ...props,
13
13
  id,
14
- className: segmentItemCva$1({ iconOnly }),
14
+ className: segmentItemCva$1({
15
+ iconOnly,
16
+ className
17
+ }),
15
18
  isDisabled,
16
19
  children: [icon && /* @__PURE__ */ jsx(IconComponent, { className: "size-6" }), label && /* @__PURE__ */ jsx(Typography, {
17
20
  size: "label-2",
@@ -30,6 +30,7 @@ export type SegmentProps<TKey extends Key = Key> = Omit<ToggleButtonGroupProps &
30
30
  ref?: Ref<HTMLElement>;
31
31
  items: SegmentItem<TKey>[];
32
32
  className?: string;
33
+ segmentItemClassName?: string;
33
34
  error?: string;
34
35
  } & GroupedSegmentProps<TKey>;
35
36
  export type ControlledSegmentProps<TFieldValues extends FieldValues, TKey extends Key = Key> = SegmentProps<TKey> & GroupedSegmentControlProps<TFieldValues, TKey>;
@@ -3,6 +3,6 @@ type UseIntersectionObserverProps = {
3
3
  onIntersectionChange?: (entry: IntersectionObserverEntry) => void;
4
4
  } & IntersectionObserverInit;
5
5
  export declare const useIntersectionObserver: <T extends HTMLElement>({ onIntersection, onIntersectionChange, root, rootMargin, threshold, }: UseIntersectionObserverProps) => {
6
- ref: import('react').RefObject<T | null>;
6
+ ref: (node: T | null) => void;
7
7
  };
8
8
  export {};
@@ -1,8 +1,12 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useCallback, useEffect, useState } from "react";
2
2
  //#region src/hooks/useIntersectionObserver.ts
3
3
  var useIntersectionObserver = ({ onIntersection, onIntersectionChange, root, rootMargin, threshold }) => {
4
- const ref = useRef(null);
4
+ const [element, setElement] = useState(null);
5
+ const ref = useCallback((node) => {
6
+ setElement(node);
7
+ }, []);
5
8
  useEffect(() => {
9
+ if (!element) return;
6
10
  const observer = new IntersectionObserver((entries) => {
7
11
  if (entries.some((entry) => entry.isIntersecting)) onIntersection?.();
8
12
  onIntersectionChange?.(entries[0]);
@@ -11,13 +15,15 @@ var useIntersectionObserver = ({ onIntersection, onIntersectionChange, root, roo
11
15
  rootMargin,
12
16
  threshold
13
17
  });
14
- if (ref.current) observer.observe(ref.current);
18
+ observer.observe(element);
15
19
  return () => observer.disconnect();
16
20
  }, [
17
- ref,
21
+ element,
18
22
  root,
19
23
  onIntersection,
20
- onIntersectionChange
24
+ onIntersectionChange,
25
+ rootMargin,
26
+ threshold
21
27
  ]);
22
28
  return { ref };
23
29
  };
package/dist/index.js CHANGED
@@ -67,8 +67,8 @@ import { ToggleButton } from "./components/buttons/ToggleButton/ToggleButton.js"
67
67
  import { Checkbox } from "./components/inputs/Checkbox/Checkbox.js";
68
68
  import { useLongPressRepeat } from "./hooks/useLongPressRepeat.js";
69
69
  import { useScrollableListBox } from "./hooks/useScrollableListBox.js";
70
- import { FormField } from "./components/inputs/FormField/FormField.js";
71
70
  import { DateTimeUtils } from "./utils/date-time.utils.js";
71
+ import { FormField } from "./components/inputs/FormField/FormField.js";
72
72
  import { DatePicker } from "./components/inputs/DateTime/DatePicker/DatePicker.js";
73
73
  import { Tag } from "./components/text/Tag/Tag.js";
74
74
  import { useDebounceCallback } from "./hooks/useDebounceCallback.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@povio/ui",
3
- "version": "2.2.10",
3
+ "version": "2.2.12",
4
4
  "type": "module",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -47,9 +47,9 @@
47
47
  "react-responsive": "^10.0.1",
48
48
  "react-stately": "^3.44.0",
49
49
  "react-toastify": "^11.0.5",
50
- "tailwindcss": "^4.2.1",
50
+ "tailwindcss": "^4.2.4",
51
51
  "tailwindcss-react-aria-components": "^2.0.1",
52
- "zod": "^4.3.5"
52
+ "zod": "^4.4.3"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@casl/ability": "^6.7.3",