@bsol-oss/react-datatable5 13.0.1-beta.8 → 13.0.1-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -29,9 +29,9 @@ var reactHookForm = require('react-hook-form');
29
29
  var Ajv = require('ajv');
30
30
  var addFormats = require('ajv-formats');
31
31
  var dayjs = require('dayjs');
32
- var utc = require('dayjs/plugin/utc');
33
- var timezone = require('dayjs/plugin/timezone');
34
32
  var customParseFormat = require('dayjs/plugin/customParseFormat');
33
+ var timezone = require('dayjs/plugin/timezone');
34
+ var utc = require('dayjs/plugin/utc');
35
35
  var ti = require('react-icons/ti');
36
36
  var matchSorterUtils = require('@tanstack/match-sorter-utils');
37
37
 
@@ -4680,7 +4680,7 @@ function DatePickerInput({ value, onChange, placeholder = 'Select a date', dateF
4680
4680
  }, timezone = 'Asia/Hong_Kong', minDate, maxDate, firstDayOfWeek, showOutsideDays, monthsToDisplay = 1, insideDialog = false, readOnly = false, showHelperButtons = true, }) {
4681
4681
  const [open, setOpen] = React.useState(false);
4682
4682
  const [inputValue, setInputValue] = React.useState('');
4683
- // Update input value when prop value changes
4683
+ // Sync inputValue with value prop changes
4684
4684
  React.useEffect(() => {
4685
4685
  if (value) {
4686
4686
  const formatted = typeof value === 'string'
@@ -4693,7 +4693,7 @@ function DatePickerInput({ value, onChange, placeholder = 'Select a date', dateF
4693
4693
  else {
4694
4694
  setInputValue('');
4695
4695
  }
4696
- }, [value, displayFormat, timezone]);
4696
+ }, [value, timezone, displayFormat]);
4697
4697
  // Convert value to Date object for DatePicker
4698
4698
  const selectedDate = value
4699
4699
  ? typeof value === 'string'
@@ -4775,7 +4775,14 @@ function DatePickerInput({ value, onChange, placeholder = 'Select a date', dateF
4775
4775
  }
4776
4776
  };
4777
4777
  const handleDateSelected = ({ date }) => {
4778
+ console.debug('[DatePickerInput] handleDateSelected called:', {
4779
+ date: date.toISOString(),
4780
+ timezone,
4781
+ dateFormat,
4782
+ formattedDate: dayjs(date).tz(timezone).format(dateFormat),
4783
+ });
4778
4784
  const formattedDate = dayjs(date).tz(timezone).format(dateFormat);
4785
+ console.debug('[DatePickerInput] Calling onChange with formatted date:', formattedDate);
4779
4786
  onChange?.(formattedDate);
4780
4787
  setOpen(false);
4781
4788
  };
@@ -6762,11 +6769,193 @@ const TextAreaInput = ({ column, schema, prefix, }) => {
6762
6769
 
6763
6770
  dayjs.extend(utc);
6764
6771
  dayjs.extend(timezone);
6765
- const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem, onChange = () => { }, startTime, selectedDate, timezone = 'Asia/Hong_Kong', portalled = true, labels = {
6766
- placeholder: 'hh:mm AM/PM',
6767
- emptyMessage: 'No time found',
6768
- }, }) => {
6769
- // Generate time options (every 15 minutes in 12-hour format)
6772
+ const TimePicker$1 = (props) => {
6773
+ const { format = '12h', value: controlledValue, onChange: controlledOnChange, hour: uncontrolledHour, setHour: uncontrolledSetHour, minute: uncontrolledMinute, setMinute: uncontrolledSetMinute, startTime, selectedDate, timezone = 'Asia/Hong_Kong', portalled = true, labels = {
6774
+ placeholder: format === '24h' ? 'HH:mm:ss' : 'hh:mm AM/PM',
6775
+ emptyMessage: 'No time found',
6776
+ }, onTimeChange, } = props;
6777
+ const is24Hour = format === '24h';
6778
+ const uncontrolledMeridiem = is24Hour ? undefined : props.meridiem;
6779
+ const uncontrolledSetMeridiem = is24Hour ? undefined : props.setMeridiem;
6780
+ const uncontrolledSecond = is24Hour ? props.second : undefined;
6781
+ const uncontrolledSetSecond = is24Hour ? props.setSecond : undefined;
6782
+ // Determine if we're in controlled mode
6783
+ const isControlled = controlledValue !== undefined;
6784
+ // Parse time string to extract hour, minute, second, meridiem
6785
+ const parseTimeString = (timeStr) => {
6786
+ if (!timeStr || !timeStr.trim()) {
6787
+ return { hour: null, minute: null, second: null, meridiem: null };
6788
+ }
6789
+ // Remove timezone suffix if present (e.g., "14:30:00Z" -> "14:30:00")
6790
+ const timeWithoutTz = timeStr.replace(/[Z+-]\d{2}:?\d{2}$/, '').trim();
6791
+ // Try parsing 24-hour format: "HH:mm:ss" or "HH:mm"
6792
+ const time24Pattern = /^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/;
6793
+ const match24 = timeWithoutTz.match(time24Pattern);
6794
+ if (match24) {
6795
+ const hour24 = parseInt(match24[1], 10);
6796
+ const minute = parseInt(match24[2], 10);
6797
+ const second = match24[3] ? parseInt(match24[3], 10) : 0;
6798
+ if (hour24 >= 0 &&
6799
+ hour24 <= 23 &&
6800
+ minute >= 0 &&
6801
+ minute <= 59 &&
6802
+ second >= 0 &&
6803
+ second <= 59) {
6804
+ if (is24Hour) {
6805
+ return { hour: hour24, minute, second, meridiem: null };
6806
+ }
6807
+ else {
6808
+ // Convert to 12-hour format
6809
+ let hour12 = hour24;
6810
+ let meridiem;
6811
+ if (hour24 === 0) {
6812
+ hour12 = 12;
6813
+ meridiem = 'am';
6814
+ }
6815
+ else if (hour24 === 12) {
6816
+ hour12 = 12;
6817
+ meridiem = 'pm';
6818
+ }
6819
+ else if (hour24 > 12) {
6820
+ hour12 = hour24 - 12;
6821
+ meridiem = 'pm';
6822
+ }
6823
+ else {
6824
+ hour12 = hour24;
6825
+ meridiem = 'am';
6826
+ }
6827
+ return { hour: hour12, minute, second: null, meridiem };
6828
+ }
6829
+ }
6830
+ }
6831
+ // Try parsing 12-hour format: "hh:mm AM/PM" or "hh:mm:ss AM/PM"
6832
+ const time12Pattern = /^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?\s*(am|pm|AM|PM)$/i;
6833
+ const match12 = timeWithoutTz.match(time12Pattern);
6834
+ if (match12 && !is24Hour) {
6835
+ const hour12 = parseInt(match12[1], 10);
6836
+ const minute = parseInt(match12[2], 10);
6837
+ const second = match12[3] ? parseInt(match12[3], 10) : null;
6838
+ const meridiem = match12[4].toLowerCase();
6839
+ if (hour12 >= 1 &&
6840
+ hour12 <= 12 &&
6841
+ minute >= 0 &&
6842
+ minute <= 59 &&
6843
+ (second === null || (second >= 0 && second <= 59))) {
6844
+ return { hour: hour12, minute, second, meridiem };
6845
+ }
6846
+ }
6847
+ return { hour: null, minute: null, second: null, meridiem: null };
6848
+ };
6849
+ // Format time values to time string
6850
+ const formatTimeString = (hour, minute, second, meridiem) => {
6851
+ if (hour === null || minute === null) {
6852
+ return undefined;
6853
+ }
6854
+ if (is24Hour) {
6855
+ const h = hour.toString().padStart(2, '0');
6856
+ const m = minute.toString().padStart(2, '0');
6857
+ const s = (second ?? 0).toString().padStart(2, '0');
6858
+ return `${h}:${m}:${s}`;
6859
+ }
6860
+ else {
6861
+ if (meridiem === null) {
6862
+ return undefined;
6863
+ }
6864
+ const h = hour.toString();
6865
+ const m = minute.toString().padStart(2, '0');
6866
+ return `${h}:${m} ${meridiem.toUpperCase()}`;
6867
+ }
6868
+ };
6869
+ // Internal state for controlled mode
6870
+ const [internalHour, setInternalHour] = React.useState(null);
6871
+ const [internalMinute, setInternalMinute] = React.useState(null);
6872
+ const [internalSecond, setInternalSecond] = React.useState(null);
6873
+ const [internalMeridiem, setInternalMeridiem] = React.useState(null);
6874
+ // Use controlled or uncontrolled values
6875
+ const hour = isControlled ? internalHour : uncontrolledHour ?? null;
6876
+ const minute = isControlled ? internalMinute : uncontrolledMinute ?? null;
6877
+ const second = isControlled ? internalSecond : uncontrolledSecond ?? null;
6878
+ const meridiem = isControlled
6879
+ ? internalMeridiem
6880
+ : uncontrolledMeridiem ?? null;
6881
+ // Setters that work for both modes
6882
+ const setHour = isControlled
6883
+ ? setInternalHour
6884
+ : uncontrolledSetHour || (() => { });
6885
+ const setMinute = isControlled
6886
+ ? setInternalMinute
6887
+ : uncontrolledSetMinute || (() => { });
6888
+ const setSecond = isControlled
6889
+ ? setInternalSecond
6890
+ : uncontrolledSetSecond || (() => { });
6891
+ const setMeridiem = isControlled
6892
+ ? setInternalMeridiem
6893
+ : uncontrolledSetMeridiem || (() => { });
6894
+ // Sync internal state with controlled value prop
6895
+ const prevValueRef = React.useRef(controlledValue);
6896
+ React.useEffect(() => {
6897
+ if (!isControlled)
6898
+ return;
6899
+ if (prevValueRef.current === controlledValue) {
6900
+ return;
6901
+ }
6902
+ prevValueRef.current = controlledValue;
6903
+ const parsed = parseTimeString(controlledValue);
6904
+ setInternalHour(parsed.hour);
6905
+ setInternalMinute(parsed.minute);
6906
+ if (is24Hour) {
6907
+ setInternalSecond(parsed.second);
6908
+ }
6909
+ else {
6910
+ setInternalMeridiem(parsed.meridiem);
6911
+ }
6912
+ }, [controlledValue, isControlled, is24Hour]);
6913
+ // Wrapper onChange that calls both controlled and uncontrolled onChange
6914
+ const handleTimeChange = (newHour, newMinute, newSecond, newMeridiem) => {
6915
+ if (isControlled) {
6916
+ const timeString = formatTimeString(newHour, newMinute, newSecond, newMeridiem);
6917
+ controlledOnChange?.(timeString);
6918
+ }
6919
+ else {
6920
+ // Call legacy onTimeChange if provided
6921
+ if (onTimeChange) {
6922
+ if (is24Hour) {
6923
+ const timeChange24h = onTimeChange;
6924
+ timeChange24h({
6925
+ hour: newHour,
6926
+ minute: newMinute,
6927
+ second: newSecond,
6928
+ });
6929
+ }
6930
+ else {
6931
+ const timeChange12h = onTimeChange;
6932
+ timeChange12h({
6933
+ hour: newHour,
6934
+ minute: newMinute,
6935
+ meridiem: newMeridiem,
6936
+ });
6937
+ }
6938
+ }
6939
+ }
6940
+ };
6941
+ const [inputValue, setInputValue] = React.useState('');
6942
+ // Sync inputValue with current time
6943
+ React.useEffect(() => {
6944
+ if (is24Hour && second !== undefined) {
6945
+ if (hour !== null && minute !== null && second !== null) {
6946
+ const formatted = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second.toString().padStart(2, '0')}`;
6947
+ setInputValue(formatted);
6948
+ }
6949
+ else {
6950
+ setInputValue('');
6951
+ }
6952
+ }
6953
+ else {
6954
+ // 12-hour format - input is managed by combobox
6955
+ setInputValue('');
6956
+ }
6957
+ }, [hour, minute, second, is24Hour]);
6958
+ // Generate time options based on format
6770
6959
  const timeOptions = React.useMemo(() => {
6771
6960
  const options = [];
6772
6961
  // Get start time for comparison if provided
@@ -6777,32 +6966,25 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
6777
6966
  const selectedDateObj = dayjs(selectedDate).tz(timezone);
6778
6967
  if (startDateObj.isValid() && selectedDateObj.isValid()) {
6779
6968
  startDateTime = startDateObj;
6780
- // Only filter if dates are the same
6781
6969
  shouldFilterByDate =
6782
6970
  startDateObj.format('YYYY-MM-DD') ===
6783
6971
  selectedDateObj.format('YYYY-MM-DD');
6784
6972
  }
6785
6973
  }
6786
- // Generate 12-hour format options (1-12 for hours, AM/PM)
6787
- for (let h = 1; h <= 12; h++) {
6788
- for (let m = 0; m < 60; m += 15) {
6789
- for (const mer of ['am', 'pm']) {
6790
- // Convert 12-hour to 24-hour for comparison
6791
- let hour24 = h;
6792
- if (mer === 'am' && h === 12)
6793
- hour24 = 0;
6794
- else if (mer === 'pm' && h < 12)
6795
- hour24 = h + 12;
6796
- // Filter out times that would result in negative duration (only when dates are the same)
6974
+ if (is24Hour) {
6975
+ // Generate 24-hour format options (0-23 for hours)
6976
+ for (let h = 0; h < 24; h++) {
6977
+ for (let m = 0; m < 60; m += 15) {
6978
+ // Filter out times that would result in negative duration
6797
6979
  if (startDateTime && selectedDate && shouldFilterByDate) {
6798
6980
  const selectedDateObj = dayjs(selectedDate).tz(timezone);
6799
6981
  const optionDateTime = selectedDateObj
6800
- .hour(hour24)
6982
+ .hour(h)
6801
6983
  .minute(m)
6802
6984
  .second(0)
6803
6985
  .millisecond(0);
6804
6986
  if (optionDateTime.isBefore(startDateTime)) {
6805
- continue; // Skip this option as it would result in negative duration
6987
+ continue;
6806
6988
  }
6807
6989
  }
6808
6990
  // Calculate duration if startTime is provided
@@ -6810,7 +6992,7 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
6810
6992
  if (startDateTime && selectedDate) {
6811
6993
  const selectedDateObj = dayjs(selectedDate).tz(timezone);
6812
6994
  const optionDateTime = selectedDateObj
6813
- .hour(hour24)
6995
+ .hour(h)
6814
6996
  .minute(m)
6815
6997
  .second(0)
6816
6998
  .millisecond(0);
@@ -6835,62 +7017,133 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
6835
7017
  }
6836
7018
  }
6837
7019
  }
6838
- const hourDisplay = h.toString();
6839
- const minuteDisplay = m.toString().padStart(2, '0');
6840
- const timeDisplay = `${hourDisplay}:${minuteDisplay} ${mer.toUpperCase()}`;
7020
+ const timeDisplay = `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:00`;
6841
7021
  options.push({
6842
7022
  label: timeDisplay,
6843
- value: `${h}:${m}:${mer}`,
7023
+ value: `${h}:${m}:0`,
6844
7024
  hour: h,
6845
7025
  minute: m,
6846
- meridiem: mer,
6847
- searchText: timeDisplay, // Use base time without duration for searching
7026
+ second: 0,
7027
+ searchText: timeDisplay,
6848
7028
  durationText,
6849
7029
  });
6850
7030
  }
6851
7031
  }
6852
7032
  }
6853
- // Sort options by time (convert to 24-hour for proper chronological sorting)
6854
- return options.sort((a, b) => {
6855
- // Convert 12-hour to 24-hour for comparison
6856
- let hour24A = a.hour;
6857
- if (a.meridiem === 'am' && a.hour === 12)
6858
- hour24A = 0;
6859
- else if (a.meridiem === 'pm' && a.hour < 12)
6860
- hour24A = a.hour + 12;
6861
- let hour24B = b.hour;
6862
- if (b.meridiem === 'am' && b.hour === 12)
6863
- hour24B = 0;
6864
- else if (b.meridiem === 'pm' && b.hour < 12)
6865
- hour24B = b.hour + 12;
6866
- // Compare by hour first, then minute
6867
- if (hour24A !== hour24B) {
6868
- return hour24A - hour24B;
6869
- }
6870
- return a.minute - b.minute;
6871
- });
6872
- }, [startTime, selectedDate, timezone]);
7033
+ else {
7034
+ // Generate 12-hour format options (1-12 for hours, AM/PM)
7035
+ for (let h = 1; h <= 12; h++) {
7036
+ for (let m = 0; m < 60; m += 15) {
7037
+ for (const mer of ['am', 'pm']) {
7038
+ // Convert 12-hour to 24-hour for comparison
7039
+ let hour24 = h;
7040
+ if (mer === 'am' && h === 12)
7041
+ hour24 = 0;
7042
+ else if (mer === 'pm' && h < 12)
7043
+ hour24 = h + 12;
7044
+ // Filter out times that would result in negative duration
7045
+ if (startDateTime && selectedDate && shouldFilterByDate) {
7046
+ const selectedDateObj = dayjs(selectedDate).tz(timezone);
7047
+ const optionDateTime = selectedDateObj
7048
+ .hour(hour24)
7049
+ .minute(m)
7050
+ .second(0)
7051
+ .millisecond(0);
7052
+ if (optionDateTime.isBefore(startDateTime)) {
7053
+ continue;
7054
+ }
7055
+ }
7056
+ // Calculate duration if startTime is provided
7057
+ let durationText;
7058
+ if (startDateTime && selectedDate) {
7059
+ const selectedDateObj = dayjs(selectedDate).tz(timezone);
7060
+ const optionDateTime = selectedDateObj
7061
+ .hour(hour24)
7062
+ .minute(m)
7063
+ .second(0)
7064
+ .millisecond(0);
7065
+ if (optionDateTime.isValid() &&
7066
+ optionDateTime.isAfter(startDateTime)) {
7067
+ const diffMs = optionDateTime.diff(startDateTime);
7068
+ const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
7069
+ const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
7070
+ const diffSeconds = Math.floor((diffMs % (1000 * 60)) / 1000);
7071
+ if (diffHours > 0 || diffMinutes > 0 || diffSeconds > 0) {
7072
+ let diffText = '';
7073
+ if (diffHours > 0) {
7074
+ diffText = `${diffHours}h ${diffMinutes}m`;
7075
+ }
7076
+ else if (diffMinutes > 0) {
7077
+ diffText = `${diffMinutes}m ${diffSeconds}s`;
7078
+ }
7079
+ else {
7080
+ diffText = `${diffSeconds}s`;
7081
+ }
7082
+ durationText = `+${diffText}`;
7083
+ }
7084
+ }
7085
+ }
7086
+ const hourDisplay = h.toString();
7087
+ const minuteDisplay = m.toString().padStart(2, '0');
7088
+ const timeDisplay = `${hourDisplay}:${minuteDisplay} ${mer.toUpperCase()}`;
7089
+ options.push({
7090
+ label: timeDisplay,
7091
+ value: `${h}:${m}:${mer}`,
7092
+ hour: h,
7093
+ minute: m,
7094
+ meridiem: mer,
7095
+ searchText: timeDisplay,
7096
+ durationText,
7097
+ });
7098
+ }
7099
+ }
7100
+ }
7101
+ // Sort 12-hour options by time (convert to 24-hour for proper chronological sorting)
7102
+ return options.sort((a, b) => {
7103
+ const a12 = a;
7104
+ const b12 = b;
7105
+ let hour24A = a12.hour;
7106
+ if (a12.meridiem === 'am' && a12.hour === 12)
7107
+ hour24A = 0;
7108
+ else if (a12.meridiem === 'pm' && a12.hour < 12)
7109
+ hour24A = a12.hour + 12;
7110
+ let hour24B = b12.hour;
7111
+ if (b12.meridiem === 'am' && b12.hour === 12)
7112
+ hour24B = 0;
7113
+ else if (b12.meridiem === 'pm' && b12.hour < 12)
7114
+ hour24B = b12.hour + 12;
7115
+ if (hour24A !== hour24B) {
7116
+ return hour24A - hour24B;
7117
+ }
7118
+ return a12.minute - b12.minute;
7119
+ });
7120
+ }
7121
+ return options;
7122
+ }, [startTime, selectedDate, timezone, is24Hour]);
6873
7123
  // itemToString returns only the clean display text (no metadata)
6874
7124
  const itemToString = React.useMemo(() => {
6875
7125
  return (item) => {
6876
- return item.searchText; // Clean display text only
7126
+ return item.searchText;
6877
7127
  };
6878
7128
  }, []);
6879
- // Custom filter function that filters by time and supports 24-hour format input
7129
+ // Custom filter function
7130
+ const { contains } = react.useFilter({ sensitivity: 'base' });
6880
7131
  const customTimeFilter = React.useMemo(() => {
7132
+ if (is24Hour) {
7133
+ return contains; // Simple contains filter for 24-hour format
7134
+ }
7135
+ // For 12-hour format, support both 12-hour and 24-hour input
6881
7136
  return (itemText, filterText) => {
6882
7137
  if (!filterText) {
6883
- return true; // Show all items when no filter
7138
+ return true;
6884
7139
  }
6885
7140
  const lowerItemText = itemText.toLowerCase();
6886
7141
  const lowerFilterText = filterText.toLowerCase();
6887
- // First, try matching against the display text (12-hour format)
6888
7142
  if (lowerItemText.includes(lowerFilterText)) {
6889
7143
  return true;
6890
7144
  }
6891
- // Find the corresponding item to check 24-hour format matches
6892
7145
  const item = timeOptions.find((opt) => opt.searchText.toLowerCase() === lowerItemText);
6893
- if (!item) {
7146
+ if (!item || !('meridiem' in item)) {
6894
7147
  return false;
6895
7148
  }
6896
7149
  // Convert item to 24-hour format for matching
@@ -6901,18 +7154,17 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
6901
7154
  hour24 = item.hour + 12;
6902
7155
  const hour24Str = hour24.toString().padStart(2, '0');
6903
7156
  const minuteStr = item.minute.toString().padStart(2, '0');
6904
- // Check if filterText matches 24-hour format variations
6905
7157
  const formats = [
6906
- `${hour24Str}:${minuteStr}`, // "13:30"
6907
- `${hour24Str}${minuteStr}`, // "1330"
6908
- hour24Str, // "13"
6909
- `${hour24}:${minuteStr}`, // "13:30" (without padding)
6910
- hour24.toString(), // "13" (without padding)
7158
+ `${hour24Str}:${minuteStr}`,
7159
+ `${hour24Str}${minuteStr}`,
7160
+ hour24Str,
7161
+ `${hour24}:${minuteStr}`,
7162
+ hour24.toString(),
6911
7163
  ];
6912
7164
  return formats.some((format) => format.toLowerCase().includes(lowerFilterText) ||
6913
7165
  lowerFilterText.includes(format.toLowerCase()));
6914
7166
  };
6915
- }, [timeOptions]);
7167
+ }, [timeOptions, is24Hour, contains]);
6916
7168
  const { collection, filter } = react.useListCollection({
6917
7169
  initialItems: timeOptions,
6918
7170
  itemToString: itemToString,
@@ -6921,32 +7173,48 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
6921
7173
  });
6922
7174
  // Get current value string for combobox
6923
7175
  const currentValue = React.useMemo(() => {
6924
- if (hour === null || minute === null || meridiem === null) {
6925
- return '';
7176
+ if (is24Hour) {
7177
+ if (hour === null || minute === null || second === null) {
7178
+ return '';
7179
+ }
7180
+ return `${hour}:${minute}:${second}`;
7181
+ }
7182
+ else {
7183
+ if (hour === null || minute === null || meridiem === null) {
7184
+ return '';
7185
+ }
7186
+ return `${hour}:${minute}:${meridiem}`;
6926
7187
  }
6927
- return `${hour}:${minute}:${meridiem}`;
6928
- }, [hour, minute, meridiem]);
7188
+ }, [hour, minute, second, meridiem, is24Hour]);
6929
7189
  // Calculate duration difference
6930
7190
  const durationDiff = React.useMemo(() => {
6931
- if (!startTime ||
6932
- !selectedDate ||
6933
- hour === null ||
6934
- minute === null ||
6935
- meridiem === null) {
7191
+ if (!startTime || !selectedDate || hour === null || minute === null) {
6936
7192
  return null;
6937
7193
  }
7194
+ if (is24Hour) {
7195
+ if (second === null)
7196
+ return null;
7197
+ }
7198
+ else {
7199
+ if (meridiem === null)
7200
+ return null;
7201
+ }
6938
7202
  const startDateObj = dayjs(startTime).tz(timezone);
6939
7203
  const selectedDateObj = dayjs(selectedDate).tz(timezone);
6940
- // Convert 12-hour to 24-hour format
7204
+ // Convert to 24-hour format
6941
7205
  let hour24 = hour;
6942
- if (meridiem === 'am' && hour === 12)
6943
- hour24 = 0;
6944
- else if (meridiem === 'pm' && hour < 12)
6945
- hour24 = hour + 12;
7206
+ if (!is24Hour && meridiem) {
7207
+ if (meridiem === 'am' && hour === 12)
7208
+ hour24 = 0;
7209
+ else if (meridiem === 'pm' && hour < 12)
7210
+ hour24 = hour + 12;
7211
+ }
6946
7212
  const currentDateTime = selectedDateObj
6947
7213
  .hour(hour24)
6948
7214
  .minute(minute)
6949
- .second(0)
7215
+ .second(is24Hour && second !== null && second !== undefined
7216
+ ? second
7217
+ : 0)
6950
7218
  .millisecond(0);
6951
7219
  if (!startDateObj.isValid() || !currentDateTime.isValid()) {
6952
7220
  return null;
@@ -6972,13 +7240,28 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
6972
7240
  return `+${diffText}`;
6973
7241
  }
6974
7242
  return null;
6975
- }, [hour, minute, meridiem, startTime, selectedDate, timezone]);
7243
+ }, [
7244
+ hour,
7245
+ minute,
7246
+ second,
7247
+ meridiem,
7248
+ startTime,
7249
+ selectedDate,
7250
+ timezone,
7251
+ is24Hour,
7252
+ ]);
6976
7253
  const handleClear = () => {
6977
7254
  setHour(null);
6978
7255
  setMinute(null);
6979
- setMeridiem(null);
6980
- filter(''); // Reset filter to show all options
6981
- onChange({ hour: null, minute: null, meridiem: null });
7256
+ if (is24Hour && setSecond) {
7257
+ setSecond(null);
7258
+ handleTimeChange(null, null, null, null);
7259
+ }
7260
+ else if (!is24Hour && setMeridiem) {
7261
+ setMeridiem(null);
7262
+ handleTimeChange(null, null, null, null);
7263
+ }
7264
+ filter('');
6982
7265
  };
6983
7266
  const handleValueChange = (details) => {
6984
7267
  if (details.value.length === 0) {
@@ -6990,112 +7273,165 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
6990
7273
  if (selectedOption) {
6991
7274
  setHour(selectedOption.hour);
6992
7275
  setMinute(selectedOption.minute);
6993
- setMeridiem(selectedOption.meridiem);
6994
- filter(''); // Reset filter after selection
6995
- onChange({
6996
- hour: selectedOption.hour,
6997
- minute: selectedOption.minute,
6998
- meridiem: selectedOption.meridiem,
6999
- });
7276
+ filter('');
7277
+ if (is24Hour) {
7278
+ const opt24 = selectedOption;
7279
+ if (setSecond)
7280
+ setSecond(opt24.second);
7281
+ handleTimeChange(opt24.hour, opt24.minute, opt24.second, null);
7282
+ }
7283
+ else {
7284
+ const opt12 = selectedOption;
7285
+ if (setMeridiem)
7286
+ setMeridiem(opt12.meridiem);
7287
+ handleTimeChange(opt12.hour, opt12.minute, null, opt12.meridiem);
7288
+ }
7000
7289
  }
7001
7290
  };
7002
7291
  // Parse input value and update state
7003
7292
  const parseAndCommitInput = (value) => {
7004
7293
  const trimmedValue = value.trim();
7005
- // Filter the collection based on input
7006
7294
  filter(trimmedValue);
7007
7295
  if (!trimmedValue) {
7008
7296
  return;
7009
7297
  }
7010
- // Parse 24-hour format first (e.g., "13:30", "14:00", "1330", "1400", "9:05", "905")
7011
- const timePattern24Hour = /^(\d{1,2}):?(\d{2})$/;
7012
- const match24Hour = trimmedValue.match(timePattern24Hour);
7013
- if (match24Hour) {
7014
- const parsedHour24 = parseInt(match24Hour[1], 10);
7015
- const parsedMinute = parseInt(match24Hour[2], 10);
7016
- // Validate 24-hour format ranges
7017
- if (parsedHour24 >= 0 &&
7018
- parsedHour24 <= 23 &&
7019
- parsedMinute >= 0 &&
7020
- parsedMinute <= 59) {
7021
- // Convert 24-hour to 12-hour format
7022
- let hour12;
7023
- let meridiem;
7024
- if (parsedHour24 === 0) {
7025
- hour12 = 12;
7026
- meridiem = 'am';
7027
- }
7028
- else if (parsedHour24 === 12) {
7029
- hour12 = 12;
7030
- meridiem = 'pm';
7031
- }
7032
- else if (parsedHour24 > 12) {
7033
- hour12 = parsedHour24 - 12;
7034
- meridiem = 'pm';
7298
+ if (is24Hour) {
7299
+ // Parse 24-hour format: "HH:mm:ss" or "HH:mm" or "HHmmss" or "HHmm"
7300
+ const timePattern = /^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/;
7301
+ const match = trimmedValue.match(timePattern);
7302
+ if (match) {
7303
+ const parsedHour = parseInt(match[1], 10);
7304
+ const parsedMinute = parseInt(match[2], 10);
7305
+ const parsedSecond = match[3] ? parseInt(match[3], 10) : 0;
7306
+ if (parsedHour >= 0 &&
7307
+ parsedHour <= 23 &&
7308
+ parsedMinute >= 0 &&
7309
+ parsedMinute <= 59 &&
7310
+ parsedSecond >= 0 &&
7311
+ parsedSecond <= 59) {
7312
+ setHour(parsedHour);
7313
+ setMinute(parsedMinute);
7314
+ if (setSecond)
7315
+ setSecond(parsedSecond);
7316
+ handleTimeChange(parsedHour, parsedMinute, parsedSecond, null);
7317
+ return;
7035
7318
  }
7036
- else {
7037
- hour12 = parsedHour24;
7038
- meridiem = 'am';
7319
+ }
7320
+ // Try numbers only format: "123045" or "1230"
7321
+ const numbersOnly = trimmedValue.replace(/[^0-9]/g, '');
7322
+ if (numbersOnly.length >= 4) {
7323
+ const parsedHour = parseInt(numbersOnly.slice(0, 2), 10);
7324
+ const parsedMinute = parseInt(numbersOnly.slice(2, 4), 10);
7325
+ const parsedSecond = numbersOnly.length >= 6 ? parseInt(numbersOnly.slice(4, 6), 10) : 0;
7326
+ if (parsedHour >= 0 &&
7327
+ parsedHour <= 23 &&
7328
+ parsedMinute >= 0 &&
7329
+ parsedMinute <= 59 &&
7330
+ parsedSecond >= 0 &&
7331
+ parsedSecond <= 59) {
7332
+ setHour(parsedHour);
7333
+ setMinute(parsedMinute);
7334
+ if (setSecond)
7335
+ setSecond(parsedSecond);
7336
+ handleTimeChange(parsedHour, parsedMinute, parsedSecond, null);
7337
+ return;
7039
7338
  }
7040
- setHour(hour12);
7041
- setMinute(parsedMinute);
7042
- setMeridiem(meridiem);
7043
- onChange({
7044
- hour: hour12,
7045
- minute: parsedMinute,
7046
- meridiem: meridiem,
7047
- });
7048
- return;
7049
7339
  }
7050
7340
  }
7051
- // Parse formats like "1:30 PM", "1:30PM", "1:30 pm", "1:30pm"
7052
- const timePattern12Hour = /^(\d{1,2}):(\d{1,2})\s*(am|pm|AM|PM)$/i;
7053
- const match12Hour = trimmedValue.match(timePattern12Hour);
7054
- if (match12Hour) {
7055
- const parsedHour = parseInt(match12Hour[1], 10);
7056
- const parsedMinute = parseInt(match12Hour[2], 10);
7057
- const parsedMeridiem = match12Hour[3].toLowerCase();
7058
- // Validate ranges
7059
- if (parsedHour >= 1 &&
7060
- parsedHour <= 12 &&
7061
- parsedMinute >= 0 &&
7062
- parsedMinute <= 59) {
7063
- setHour(parsedHour);
7064
- setMinute(parsedMinute);
7065
- setMeridiem(parsedMeridiem);
7066
- onChange({
7067
- hour: parsedHour,
7068
- minute: parsedMinute,
7069
- meridiem: parsedMeridiem,
7070
- });
7071
- return;
7341
+ else {
7342
+ // Parse 24-hour format first (e.g., "13:30", "14:00", "1330", "1400")
7343
+ const timePattern24Hour = /^(\d{1,2}):?(\d{2})$/;
7344
+ const match24Hour = trimmedValue.match(timePattern24Hour);
7345
+ if (match24Hour) {
7346
+ const parsedHour24 = parseInt(match24Hour[1], 10);
7347
+ const parsedMinute = parseInt(match24Hour[2], 10);
7348
+ if (parsedHour24 >= 0 &&
7349
+ parsedHour24 <= 23 &&
7350
+ parsedMinute >= 0 &&
7351
+ parsedMinute <= 59) {
7352
+ // Convert 24-hour to 12-hour format
7353
+ let hour12;
7354
+ let meridiem;
7355
+ if (parsedHour24 === 0) {
7356
+ hour12 = 12;
7357
+ meridiem = 'am';
7358
+ }
7359
+ else if (parsedHour24 === 12) {
7360
+ hour12 = 12;
7361
+ meridiem = 'pm';
7362
+ }
7363
+ else if (parsedHour24 > 12) {
7364
+ hour12 = parsedHour24 - 12;
7365
+ meridiem = 'pm';
7366
+ }
7367
+ else {
7368
+ hour12 = parsedHour24;
7369
+ meridiem = 'am';
7370
+ }
7371
+ setHour(hour12);
7372
+ setMinute(parsedMinute);
7373
+ if (setMeridiem)
7374
+ setMeridiem(meridiem);
7375
+ handleTimeChange(hour12, parsedMinute, null, meridiem);
7376
+ return;
7377
+ }
7072
7378
  }
7073
- }
7074
- // Try to parse formats like "130pm" or "130 pm" (without colon)
7075
- const timePatternNoColon = /^(\d{1,4})\s*(am|pm|AM|PM)$/i;
7076
- const matchNoColon = trimmedValue.match(timePatternNoColon);
7077
- if (matchNoColon) {
7078
- const numbersOnly = matchNoColon[1];
7079
- const parsedMeridiem = matchNoColon[2].toLowerCase();
7080
- if (numbersOnly.length >= 3) {
7081
- const parsedHour = parseInt(numbersOnly.slice(0, -2), 10);
7082
- const parsedMinute = parseInt(numbersOnly.slice(-2), 10);
7083
- // Validate ranges
7379
+ // Parse formats like "1:30 PM", "1:30PM", "1:30 pm", "1:30pm"
7380
+ const timePattern12Hour = /^(\d{1,2}):(\d{1,2})\s*(am|pm|AM|PM)$/i;
7381
+ const match12Hour = trimmedValue.match(timePattern12Hour);
7382
+ if (match12Hour) {
7383
+ const parsedHour = parseInt(match12Hour[1], 10);
7384
+ const parsedMinute = parseInt(match12Hour[2], 10);
7385
+ const parsedMeridiem = match12Hour[3].toLowerCase();
7084
7386
  if (parsedHour >= 1 &&
7085
7387
  parsedHour <= 12 &&
7086
7388
  parsedMinute >= 0 &&
7087
7389
  parsedMinute <= 59) {
7088
7390
  setHour(parsedHour);
7089
7391
  setMinute(parsedMinute);
7090
- setMeridiem(parsedMeridiem);
7091
- onChange({
7092
- hour: parsedHour,
7093
- minute: parsedMinute,
7094
- meridiem: parsedMeridiem,
7095
- });
7392
+ if (setMeridiem)
7393
+ setMeridiem(parsedMeridiem);
7394
+ handleTimeChange(parsedHour, parsedMinute, null, parsedMeridiem);
7395
+ return;
7396
+ }
7397
+ }
7398
+ // Parse formats like "12am" or "1pm" (hour only with meridiem, no minutes)
7399
+ const timePatternHourOnly = /^(\d{1,2})\s*(am|pm|AM|PM)$/i;
7400
+ const matchHourOnly = trimmedValue.match(timePatternHourOnly);
7401
+ if (matchHourOnly) {
7402
+ const parsedHour = parseInt(matchHourOnly[1], 10);
7403
+ const parsedMeridiem = matchHourOnly[2].toLowerCase();
7404
+ if (parsedHour >= 1 && parsedHour <= 12) {
7405
+ setHour(parsedHour);
7406
+ setMinute(0); // Default to 0 minutes when only hour is provided
7407
+ if (setMeridiem)
7408
+ setMeridiem(parsedMeridiem);
7409
+ handleTimeChange(parsedHour, 0, null, parsedMeridiem);
7096
7410
  return;
7097
7411
  }
7098
7412
  }
7413
+ // Try to parse formats like "130pm" or "130 pm" (without colon, with minutes)
7414
+ const timePatternNoColon = /^(\d{1,4})\s*(am|pm|AM|PM)$/i;
7415
+ const matchNoColon = trimmedValue.match(timePatternNoColon);
7416
+ if (matchNoColon) {
7417
+ const numbersOnly = matchNoColon[1];
7418
+ const parsedMeridiem = matchNoColon[2].toLowerCase();
7419
+ if (numbersOnly.length >= 3) {
7420
+ const parsedHour = parseInt(numbersOnly.slice(0, -2), 10);
7421
+ const parsedMinute = parseInt(numbersOnly.slice(-2), 10);
7422
+ if (parsedHour >= 1 &&
7423
+ parsedHour <= 12 &&
7424
+ parsedMinute >= 0 &&
7425
+ parsedMinute <= 59) {
7426
+ setHour(parsedHour);
7427
+ setMinute(parsedMinute);
7428
+ if (setMeridiem)
7429
+ setMeridiem(parsedMeridiem);
7430
+ handleTimeChange(parsedHour, parsedMinute, null, parsedMeridiem);
7431
+ return;
7432
+ }
7433
+ }
7434
+ }
7099
7435
  }
7100
7436
  // Parse failed, select first result
7101
7437
  selectFirstResult();
@@ -7106,55 +7442,84 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
7106
7442
  const firstItem = collection.items[0];
7107
7443
  setHour(firstItem.hour);
7108
7444
  setMinute(firstItem.minute);
7109
- setMeridiem(firstItem.meridiem);
7110
- filter(''); // Reset filter after selection
7111
- onChange({
7112
- hour: firstItem.hour,
7113
- minute: firstItem.minute,
7114
- meridiem: firstItem.meridiem,
7115
- });
7445
+ filter('');
7446
+ if (is24Hour) {
7447
+ const opt24 = firstItem;
7448
+ if (setSecond)
7449
+ setSecond(opt24.second);
7450
+ handleTimeChange(opt24.hour, opt24.minute, opt24.second, null);
7451
+ }
7452
+ else {
7453
+ const opt12 = firstItem;
7454
+ if (setMeridiem)
7455
+ setMeridiem(opt12.meridiem);
7456
+ handleTimeChange(opt12.hour, opt12.minute, null, opt12.meridiem);
7457
+ }
7116
7458
  }
7117
7459
  };
7118
7460
  const handleInputValueChange = (details) => {
7119
- // Filter the collection based on input, but don't parse yet
7461
+ if (is24Hour) {
7462
+ setInputValue(details.inputValue);
7463
+ }
7120
7464
  filter(details.inputValue);
7121
7465
  };
7122
7466
  const handleFocus = (e) => {
7123
- // Select all text when focusing
7124
7467
  e.target.select();
7125
7468
  };
7126
7469
  const handleBlur = (e) => {
7127
- // Parse and commit the input value when losing focus
7128
- const inputValue = e.target.value;
7129
- if (inputValue) {
7130
- parseAndCommitInput(inputValue);
7470
+ const inputVal = e.target.value;
7471
+ if (is24Hour) {
7472
+ setInputValue(inputVal);
7473
+ }
7474
+ if (inputVal) {
7475
+ parseAndCommitInput(inputVal);
7131
7476
  }
7132
7477
  };
7133
7478
  const handleKeyDown = (e) => {
7134
- // Commit input on Enter key
7135
7479
  if (e.key === 'Enter') {
7136
7480
  e.preventDefault();
7137
- const inputValue = e.currentTarget.value;
7138
- if (inputValue) {
7139
- parseAndCommitInput(inputValue);
7481
+ const inputVal = e.currentTarget.value;
7482
+ if (is24Hour) {
7483
+ setInputValue(inputVal);
7484
+ }
7485
+ if (inputVal) {
7486
+ parseAndCommitInput(inputVal);
7140
7487
  }
7141
- // Blur the input
7142
7488
  e.currentTarget?.blur();
7143
7489
  }
7144
7490
  };
7145
- return (jsxRuntime.jsx(react.Flex, { direction: "column", gap: 3, children: jsxRuntime.jsxs(react.Flex, { alignItems: "center", gap: "2", width: "auto", minWidth: "300px", children: [jsxRuntime.jsxs(react.Combobox.Root, { collection: collection, value: currentValue ? [currentValue] : [], onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, allowCustomValue: true, selectionBehavior: "replace", openOnClick: true, flex: 1, children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.InputGroup, { startElement: jsxRuntime.jsx(bs.BsClock, {}), children: jsxRuntime.jsx(react.Combobox.Input, { placeholder: labels?.placeholder ?? 'hh:mm AM/PM', onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown }) }), jsxRuntime.jsx(react.Combobox.IndicatorGroup, { children: jsxRuntime.jsx(react.Combobox.Trigger, {}) })] }), jsxRuntime.jsx(react.Portal, { disabled: !portalled, children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [jsxRuntime.jsx(react.Combobox.Empty, { children: labels?.emptyMessage ?? 'No time found' }), collection.items.map((item) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsxs(react.Flex, { alignItems: "center", gap: 2, width: "100%", children: [jsxRuntime.jsx(react.Text, { flex: 1, children: item.label }), item.durationText && (jsxRuntime.jsx(react.Tag.Root, { size: "sm", children: jsxRuntime.jsx(react.Tag.Label, { children: item.durationText }) }))] }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value)))] }) }) })] }), durationDiff && (jsxRuntime.jsx(react.Tag.Root, { size: "sm", children: jsxRuntime.jsx(react.Tag.Label, { children: durationDiff }) })), jsxRuntime.jsx(react.Button, { onClick: handleClear, size: "sm", variant: "ghost", children: jsxRuntime.jsx(react.Icon, { children: jsxRuntime.jsx(md.MdCancel, {}) }) })] }) }));
7491
+ return (jsxRuntime.jsx(react.Flex, { direction: "column", gap: 3, children: jsxRuntime.jsxs(react.Flex, { alignItems: "center", gap: "2", width: "auto", minWidth: "300px", children: [jsxRuntime.jsxs(react.Combobox.Root, { collection: collection, value: currentValue ? [currentValue] : [], onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, allowCustomValue: true, selectionBehavior: "replace", flex: 1, children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.InputGroup, { startElement: jsxRuntime.jsx(bs.BsClock, {}), children: jsxRuntime.jsx(react.Combobox.Input, { value: is24Hour ? inputValue : undefined, placeholder: labels?.placeholder ?? (is24Hour ? 'HH:mm:ss' : 'hh:mm AM/PM'), onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown }) }), jsxRuntime.jsx(react.Combobox.IndicatorGroup, { children: jsxRuntime.jsx(react.Combobox.Trigger, {}) })] }), jsxRuntime.jsx(react.Portal, { disabled: !portalled, children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [jsxRuntime.jsx(react.Combobox.Empty, { children: labels?.emptyMessage ?? 'No time found' }), collection.items.map((item) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsxs(react.Flex, { alignItems: "center", gap: 2, width: "100%", children: [jsxRuntime.jsx(react.Text, { flex: 1, children: item.label }), item.durationText && (jsxRuntime.jsx(react.Tag.Root, { size: "sm", children: jsxRuntime.jsx(react.Tag.Label, { children: item.durationText }) }))] }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value)))] }) }) })] }), durationDiff && (jsxRuntime.jsx(react.Tag.Root, { size: "sm", children: jsxRuntime.jsx(react.Tag.Label, { children: durationDiff }) }))] }) }));
7146
7492
  };
7147
7493
 
7148
7494
  dayjs.extend(timezone);
7149
7495
  const TimePicker = ({ column, schema, prefix }) => {
7150
7496
  const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
7151
7497
  const { timezone, insideDialog, timePickerLabels } = useSchemaContext();
7152
- const { required, gridColumn = 'span 12', gridRow = 'span 1', timeFormat = 'HH:mm:ssZ', displayTimeFormat = 'hh:mm A', } = schema;
7498
+ const { required, gridColumn = 'span 12', gridRow = 'span 1', timeFormat = 'HH:mm:ssZ', displayTimeFormat = 'hh:mm A', startTimeField, selectedDateField, } = schema;
7153
7499
  const isRequired = required?.some((columnId) => columnId === column);
7154
7500
  const colLabel = `${prefix}${column}`;
7155
7501
  const formI18n = useFormI18n(column, prefix, schema);
7156
7502
  const [open, setOpen] = React.useState(false);
7157
7503
  const value = watch(colLabel);
7504
+ // Watch startTime and selectedDate fields for offset calculation
7505
+ const startTimeValue = startTimeField
7506
+ ? watch(`${prefix}${startTimeField}`)
7507
+ : undefined;
7508
+ const selectedDateValue = selectedDateField
7509
+ ? watch(`${prefix}${selectedDateField}`)
7510
+ : undefined;
7511
+ // Convert to ISO string format for startTime if it's a date-time string
7512
+ const startTime = startTimeValue
7513
+ ? dayjs(startTimeValue).tz(timezone).isValid()
7514
+ ? dayjs(startTimeValue).tz(timezone).toISOString()
7515
+ : undefined
7516
+ : undefined;
7517
+ // Convert selectedDate to YYYY-MM-DD format
7518
+ const selectedDate = selectedDateValue
7519
+ ? dayjs(selectedDateValue).tz(timezone).isValid()
7520
+ ? dayjs(selectedDateValue).tz(timezone).format('YYYY-MM-DD')
7521
+ : undefined
7522
+ : undefined;
7158
7523
  const displayedTime = dayjs(`1970-01-01T${value}`).tz(timezone).isValid()
7159
7524
  ? dayjs(`1970-01-01T${value}`).tz(timezone).format(displayTimeFormat)
7160
7525
  : '';
@@ -7210,941 +7575,860 @@ const TimePicker = ({ column, schema, prefix }) => {
7210
7575
  return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
7211
7576
  gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsxs(react.Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
7212
7577
  setOpen(true);
7213
- }, justifyContent: 'start', children: [jsxRuntime.jsx(io.IoMdClock, {}), !!value ? `${displayedTime}` : ''] }) }), insideDialog ? (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { maxH: "70vh", overflowY: "auto", children: jsxRuntime.jsx(react.Popover.Body, { overflow: "visible", children: jsxRuntime.jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, labels: timePickerLabels }) }) }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { children: jsxRuntime.jsx(react.Popover.Body, { children: jsxRuntime.jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, labels: timePickerLabels }) }) }) }) }))] }) }));
7578
+ }, justifyContent: 'start', children: [jsxRuntime.jsx(io.IoMdClock, {}), !!value ? `${displayedTime}` : ''] }) }), insideDialog ? (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { maxH: "70vh", overflowY: "auto", children: jsxRuntime.jsx(react.Popover.Body, { overflow: "visible", children: jsxRuntime.jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, startTime: startTime, selectedDate: selectedDate, timezone: timezone, portalled: false, labels: timePickerLabels }) }) }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { children: jsxRuntime.jsx(react.Popover.Body, { children: jsxRuntime.jsx(TimePicker$1, { format: "12h", hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, startTime: startTime, selectedDate: selectedDate, timezone: timezone, portalled: false, labels: timePickerLabels }) }) }) }) }))] }) }));
7214
7579
  };
7215
7580
 
7216
7581
  dayjs.extend(utc);
7217
7582
  dayjs.extend(timezone);
7218
- function IsoTimePicker({ hour, setHour, minute, setMinute, second, setSecond,
7219
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
7220
- onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Kong', portalled = true, labels = {
7221
- placeholder: 'HH:mm:ss',
7222
- emptyMessage: 'No time found',
7223
- }, }) {
7224
- // Generate time options (every 15 minutes, seconds always 0)
7225
- const timeOptions = React.useMemo(() => {
7226
- const options = [];
7227
- // Get start time for comparison if provided
7228
- let startDateTime = null;
7229
- let shouldFilterByDate = false;
7230
- if (startTime && selectedDate) {
7231
- const startDateObj = dayjs(startTime).tz(timezone);
7232
- const selectedDateObj = dayjs(selectedDate).tz(timezone);
7233
- if (startDateObj.isValid() && selectedDateObj.isValid()) {
7234
- startDateTime = startDateObj;
7235
- // Only filter if dates are the same
7236
- shouldFilterByDate =
7237
- startDateObj.format('YYYY-MM-DD') ===
7238
- selectedDateObj.format('YYYY-MM-DD');
7239
- }
7583
+ dayjs.extend(customParseFormat);
7584
+ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds = false, labels = {
7585
+ monthNamesShort: [
7586
+ 'January',
7587
+ 'February',
7588
+ 'March',
7589
+ 'April',
7590
+ 'May',
7591
+ 'June',
7592
+ 'July',
7593
+ 'August',
7594
+ 'September',
7595
+ 'October',
7596
+ 'November',
7597
+ 'December',
7598
+ ],
7599
+ weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
7600
+ backButtonLabel: 'Back',
7601
+ forwardButtonLabel: 'Forward',
7602
+ }, timePickerLabels, timezone: tz = 'Asia/Hong_Kong', startTime, minDate, maxDate, portalled = false, defaultDate, defaultTime, showQuickActions = false, quickActionLabels = {
7603
+ yesterday: 'Yesterday',
7604
+ today: 'Today',
7605
+ tomorrow: 'Tomorrow',
7606
+ plus7Days: '+7 Days',
7607
+ }, showTimezoneSelector = false, }) {
7608
+ const is24Hour = format === 'iso-date-time' || showSeconds;
7609
+ const { monthNamesShort = [
7610
+ 'January',
7611
+ 'February',
7612
+ 'March',
7613
+ 'April',
7614
+ 'May',
7615
+ 'June',
7616
+ 'July',
7617
+ 'August',
7618
+ 'September',
7619
+ 'October',
7620
+ 'November',
7621
+ 'December',
7622
+ ], weekdayNamesShort = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], backButtonLabel = 'Back', forwardButtonLabel = 'Forward', } = labels;
7623
+ // Parse value to get date and time
7624
+ const parsedValue = React.useMemo(() => {
7625
+ if (!value)
7626
+ return null;
7627
+ const dateObj = dayjs(value).tz(tz);
7628
+ if (!dateObj.isValid())
7629
+ return null;
7630
+ return dateObj;
7631
+ }, [value, tz]);
7632
+ // Initialize date state
7633
+ const [selectedDate, setSelectedDate] = React.useState(() => {
7634
+ if (parsedValue) {
7635
+ return parsedValue.toDate();
7240
7636
  }
7241
- for (let h = 0; h < 24; h++) {
7242
- for (let m = 0; m < 60; m += 15) {
7243
- const timeDisplay = `${h.toString().padStart(2, '0')}:${m
7244
- .toString()
7245
- .padStart(2, '0')}:00`;
7246
- // Filter out times that would result in negative duration (only when dates are the same)
7247
- if (startDateTime && selectedDate && shouldFilterByDate) {
7248
- const selectedDateObj = dayjs(selectedDate).tz(timezone);
7249
- const optionDateTime = selectedDateObj
7250
- .hour(h)
7251
- .minute(m)
7252
- .second(0)
7253
- .millisecond(0);
7254
- if (optionDateTime.isBefore(startDateTime)) {
7255
- continue; // Skip this option as it would result in negative duration
7256
- }
7257
- }
7258
- // Calculate duration if startTime is provided
7259
- let durationText;
7260
- if (startDateTime && selectedDate) {
7261
- const selectedDateObj = dayjs(selectedDate).tz(timezone);
7262
- const optionDateTime = selectedDateObj
7263
- .hour(h)
7264
- .minute(m)
7265
- .second(0)
7266
- .millisecond(0);
7267
- if (optionDateTime.isValid() &&
7268
- optionDateTime.isAfter(startDateTime)) {
7269
- const diffMs = optionDateTime.diff(startDateTime);
7270
- const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
7271
- const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
7272
- const diffSeconds = Math.floor((diffMs % (1000 * 60)) / 1000);
7273
- if (diffHours > 0 || diffMinutes > 0 || diffSeconds > 0) {
7274
- let diffText = '';
7275
- if (diffHours > 0) {
7276
- diffText = `${diffHours}h ${diffMinutes}m`;
7277
- }
7278
- else if (diffMinutes > 0) {
7279
- diffText = `${diffMinutes}m ${diffSeconds}s`;
7280
- }
7281
- else {
7282
- diffText = `${diffSeconds}s`;
7283
- }
7284
- durationText = `+${diffText}`;
7285
- }
7286
- }
7287
- }
7288
- options.push({
7289
- label: timeDisplay,
7290
- value: `${h}:${m}:0`,
7291
- hour: h,
7292
- minute: m,
7293
- second: 0,
7294
- searchText: timeDisplay, // Use base time without duration for searching
7295
- durationText,
7296
- });
7297
- }
7637
+ if (defaultDate) {
7638
+ const defaultDateObj = dayjs(defaultDate).tz(tz);
7639
+ return defaultDateObj.isValid() ? defaultDateObj.toDate() : new Date();
7298
7640
  }
7299
- return options;
7300
- }, [startTime, selectedDate, timezone]);
7301
- const { contains } = react.useFilter({ sensitivity: 'base' });
7302
- const { collection, filter } = react.useListCollection({
7303
- initialItems: timeOptions,
7304
- itemToString: (item) => item.searchText, // Use searchText (without duration) for filtering
7305
- itemToValue: (item) => item.value,
7306
- filter: contains,
7641
+ return new Date();
7307
7642
  });
7308
- // Get current value string for combobox
7309
- const currentValue = React.useMemo(() => {
7310
- if (hour === null || minute === null || second === null) {
7311
- return '';
7312
- }
7313
- return `${hour}:${minute}:${second}`;
7314
- }, [hour, minute, second]);
7315
- // Calculate duration difference
7316
- const durationDiff = React.useMemo(() => {
7317
- if (!startTime ||
7318
- !selectedDate ||
7319
- hour === null ||
7320
- minute === null ||
7321
- second === null) {
7322
- return null;
7643
+ // Initialize time state
7644
+ const [hour, setHour] = React.useState(() => {
7645
+ if (parsedValue) {
7646
+ return parsedValue.hour();
7323
7647
  }
7324
- const startDateObj = dayjs(startTime).tz(timezone);
7325
- const selectedDateObj = dayjs(selectedDate).tz(timezone);
7326
- const currentDateTime = selectedDateObj
7327
- .hour(hour)
7328
- .minute(minute)
7329
- .second(second ?? 0)
7330
- .millisecond(0);
7331
- if (!startDateObj.isValid() || !currentDateTime.isValid()) {
7332
- return null;
7648
+ if (defaultTime?.hour !== null && defaultTime?.hour !== undefined) {
7649
+ return defaultTime.hour;
7333
7650
  }
7334
- const diffMs = currentDateTime.diff(startDateObj);
7335
- if (diffMs < 0) {
7336
- return null;
7651
+ return null;
7652
+ });
7653
+ const [minute, setMinute] = React.useState(() => {
7654
+ if (parsedValue) {
7655
+ return parsedValue.minute();
7337
7656
  }
7338
- const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
7339
- const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
7340
- const diffSeconds = Math.floor((diffMs % (1000 * 60)) / 1000);
7341
- if (diffHours > 0 || diffMinutes > 0 || diffSeconds > 0) {
7342
- let diffText = '';
7343
- if (diffHours > 0) {
7344
- diffText = `${diffHours}h ${diffMinutes}m`;
7345
- }
7346
- else if (diffMinutes > 0) {
7347
- diffText = `${diffMinutes}m ${diffSeconds}s`;
7348
- }
7349
- else {
7350
- diffText = `${diffSeconds}s`;
7351
- }
7352
- return `+${diffText}`;
7657
+ if (defaultTime?.minute !== null && defaultTime?.minute !== undefined) {
7658
+ return defaultTime.minute;
7353
7659
  }
7354
7660
  return null;
7355
- }, [hour, minute, second, startTime, selectedDate, timezone]);
7356
- const handleClear = () => {
7357
- setHour(null);
7358
- setMinute(null);
7359
- setSecond(null);
7360
- filter(''); // Reset filter to show all options
7361
- onChange({ hour: null, minute: null, second: null });
7362
- };
7363
- const handleValueChange = (details) => {
7364
- if (details.value.length === 0) {
7365
- handleClear();
7366
- return;
7661
+ });
7662
+ const [second, setSecond] = React.useState(() => {
7663
+ if (parsedValue) {
7664
+ return parsedValue.second();
7367
7665
  }
7368
- const selectedValue = details.value[0];
7369
- const selectedOption = timeOptions.find((opt) => opt.value === selectedValue);
7370
- if (selectedOption) {
7371
- setHour(selectedOption.hour);
7372
- setMinute(selectedOption.minute);
7373
- setSecond(selectedOption.second);
7374
- filter(''); // Reset filter after selection
7375
- onChange({
7376
- hour: selectedOption.hour,
7377
- minute: selectedOption.minute,
7378
- second: selectedOption.second,
7379
- });
7666
+ if (defaultTime?.second !== null && defaultTime?.second !== undefined) {
7667
+ return defaultTime.second;
7380
7668
  }
7381
- };
7382
- // Parse input value and update state
7383
- const parseAndCommitInput = (value) => {
7384
- const trimmedValue = value.trim();
7385
- // Filter the collection based on input
7386
- filter(trimmedValue);
7387
- if (!trimmedValue) {
7669
+ return showSeconds ? 0 : null;
7670
+ });
7671
+ const [meridiem, setMeridiem] = React.useState(() => {
7672
+ if (parsedValue) {
7673
+ const h = parsedValue.hour();
7674
+ return h < 12 ? 'am' : 'pm';
7675
+ }
7676
+ if (defaultTime?.meridiem !== null && defaultTime?.meridiem !== undefined) {
7677
+ return defaultTime.meridiem;
7678
+ }
7679
+ return is24Hour ? null : 'am';
7680
+ });
7681
+ // Popover state - separate for date, time, and timezone
7682
+ const [datePopoverOpen, setDatePopoverOpen] = React.useState(false);
7683
+ const [timePopoverOpen, setTimePopoverOpen] = React.useState(false);
7684
+ const [timezonePopoverOpen, setTimezonePopoverOpen] = React.useState(false);
7685
+ const [calendarPopoverOpen, setCalendarPopoverOpen] = React.useState(false);
7686
+ // Timezone offset state
7687
+ const [timezoneOffset, setTimezoneOffset] = React.useState(() => {
7688
+ if (parsedValue) {
7689
+ return parsedValue.format('Z');
7690
+ }
7691
+ // Default to +08:00
7692
+ return '+08:00';
7693
+ });
7694
+ // Sync timezone offset when value changes
7695
+ // Generate timezone offset options (UTC-12 to UTC+14)
7696
+ const timezoneOffsetOptions = React.useMemo(() => {
7697
+ const options = [];
7698
+ for (let offset = -12; offset <= 14; offset++) {
7699
+ const sign = offset >= 0 ? '+' : '-';
7700
+ const hours = Math.abs(offset).toString().padStart(2, '0');
7701
+ const value = `${sign}${hours}:00`;
7702
+ const label = `UTC${sign}${hours}:00`;
7703
+ options.push({ value, label });
7704
+ }
7705
+ return options;
7706
+ }, []);
7707
+ // Create collection for Select
7708
+ const { collection: timezoneCollection } = react.useListCollection({
7709
+ initialItems: timezoneOffsetOptions,
7710
+ itemToString: (item) => item.label,
7711
+ itemToValue: (item) => item.value,
7712
+ });
7713
+ // Date input state
7714
+ const [dateInputValue, setDateInputValue] = React.useState('');
7715
+ // Sync date input value with selected date
7716
+ React.useEffect(() => {
7717
+ if (selectedDate) {
7718
+ const formatted = dayjs(selectedDate).tz(tz).format('YYYY-MM-DD');
7719
+ setDateInputValue(formatted);
7720
+ }
7721
+ else {
7722
+ setDateInputValue('');
7723
+ }
7724
+ }, [selectedDate, tz]);
7725
+ // Parse and validate date input
7726
+ const parseAndValidateDateInput = (inputVal) => {
7727
+ // If empty, clear the value
7728
+ if (!inputVal.trim()) {
7729
+ setSelectedDate(null);
7730
+ updateDateTime(null, hour, minute, second, meridiem);
7388
7731
  return;
7389
7732
  }
7390
- // Parse HH:mm:ss or HH:mm format
7391
- const timePattern = /^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/;
7392
- const match = trimmedValue.match(timePattern);
7393
- if (match) {
7394
- const parsedHour = parseInt(match[1], 10);
7395
- const parsedMinute = parseInt(match[2], 10);
7396
- const parsedSecond = match[3] ? parseInt(match[3], 10) : 0;
7397
- // Validate ranges
7398
- if (parsedHour >= 0 &&
7399
- parsedHour <= 23 &&
7400
- parsedMinute >= 0 &&
7401
- parsedMinute <= 59 &&
7402
- parsedSecond >= 0 &&
7403
- parsedSecond <= 59) {
7404
- setHour(parsedHour);
7405
- setMinute(parsedMinute);
7406
- setSecond(parsedSecond);
7407
- onChange({
7408
- hour: parsedHour,
7409
- minute: parsedMinute,
7410
- second: parsedSecond,
7411
- });
7733
+ // Try parsing with common date formats
7734
+ let parsedDate = dayjs(inputVal, 'YYYY-MM-DD', true);
7735
+ // If that fails, try other common formats
7736
+ if (!parsedDate.isValid()) {
7737
+ parsedDate = dayjs(inputVal);
7738
+ }
7739
+ // If valid, check constraints and update
7740
+ if (parsedDate.isValid()) {
7741
+ const dateObj = parsedDate.tz(tz).toDate();
7742
+ // Check min/max constraints
7743
+ if (minDate && dateObj < minDate) {
7744
+ // Invalid: before minDate, reset to current selected date
7745
+ if (selectedDate) {
7746
+ const formatted = dayjs(selectedDate).tz(tz).format('YYYY-MM-DD');
7747
+ setDateInputValue(formatted);
7748
+ }
7749
+ else {
7750
+ setDateInputValue('');
7751
+ }
7412
7752
  return;
7413
7753
  }
7414
- }
7415
- else {
7416
- // Try to parse formats like "123045" (HHmmss) or "1230" (HHmm)
7417
- const numbersOnly = trimmedValue.replace(/[^0-9]/g, '');
7418
- if (numbersOnly.length >= 4) {
7419
- const parsedHour = parseInt(numbersOnly.slice(0, 2), 10);
7420
- const parsedMinute = parseInt(numbersOnly.slice(2, 4), 10);
7421
- const parsedSecond = numbersOnly.length >= 6 ? parseInt(numbersOnly.slice(4, 6), 10) : 0;
7422
- // Validate ranges
7423
- if (parsedHour >= 0 &&
7424
- parsedHour <= 23 &&
7425
- parsedMinute >= 0 &&
7426
- parsedMinute <= 59 &&
7427
- parsedSecond >= 0 &&
7428
- parsedSecond <= 59) {
7429
- setHour(parsedHour);
7430
- setMinute(parsedMinute);
7431
- setSecond(parsedSecond);
7432
- onChange({
7433
- hour: parsedHour,
7434
- minute: parsedMinute,
7435
- second: parsedSecond,
7436
- });
7437
- return;
7754
+ if (maxDate && dateObj > maxDate) {
7755
+ // Invalid: after maxDate, reset to current selected date
7756
+ if (selectedDate) {
7757
+ const formatted = dayjs(selectedDate).tz(tz).format('YYYY-MM-DD');
7758
+ setDateInputValue(formatted);
7438
7759
  }
7760
+ else {
7761
+ setDateInputValue('');
7762
+ }
7763
+ return;
7439
7764
  }
7440
- }
7441
- // Parse failed, select first result
7442
- selectFirstResult();
7443
- };
7444
- // Select first result from filtered collection
7445
- const selectFirstResult = () => {
7446
- if (collection.items.length > 0) {
7447
- const firstItem = collection.items[0];
7448
- setHour(firstItem.hour);
7449
- setMinute(firstItem.minute);
7450
- setSecond(firstItem.second);
7451
- filter(''); // Reset filter after selection
7452
- onChange({
7453
- hour: firstItem.hour,
7454
- minute: firstItem.minute,
7455
- second: firstItem.second,
7456
- });
7457
- }
7458
- };
7459
- const [inputValue, setInputValue] = React.useState('');
7460
- // Sync inputValue with currentValue when time changes externally
7461
- React.useEffect(() => {
7462
- if (hour !== null && minute !== null && second !== null) {
7463
- const formattedValue = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second.toString().padStart(2, '0')}`;
7464
- setInputValue(formattedValue);
7765
+ // Valid date - update selected date
7766
+ setSelectedDate(dateObj);
7767
+ updateDateTime(dateObj, hour, minute, second, meridiem);
7768
+ // Format and update input value
7769
+ const formatted = parsedDate.tz(tz).format('YYYY-MM-DD');
7770
+ setDateInputValue(formatted);
7465
7771
  }
7466
7772
  else {
7467
- setInputValue('');
7773
+ // Invalid date - reset to current selected date
7774
+ if (selectedDate) {
7775
+ const formatted = dayjs(selectedDate).tz(tz).format('YYYY-MM-DD');
7776
+ setDateInputValue(formatted);
7777
+ }
7778
+ else {
7779
+ setDateInputValue('');
7780
+ }
7468
7781
  }
7469
- }, [hour, minute, second]);
7470
- const handleInputValueChange = (details) => {
7471
- // Update local input value state
7472
- setInputValue(details.inputValue);
7473
- // Filter the collection based on input, but don't parse yet
7474
- filter(details.inputValue);
7475
7782
  };
7476
- const handleFocus = (e) => {
7477
- // Select all text when focusing
7478
- e.target.select();
7783
+ const handleDateInputChange = (e) => {
7784
+ setDateInputValue(e.target.value);
7479
7785
  };
7480
- const handleBlur = (e) => {
7481
- // Parse and commit the input value when losing focus
7482
- const inputVal = e.target.value;
7483
- setInputValue(inputVal);
7484
- if (inputVal) {
7485
- parseAndCommitInput(inputVal);
7486
- }
7786
+ const handleDateInputBlur = () => {
7787
+ parseAndValidateDateInput(dateInputValue);
7487
7788
  };
7488
- const handleKeyDown = (e) => {
7489
- // Commit input on Enter key
7789
+ const handleDateInputKeyDown = (e) => {
7490
7790
  if (e.key === 'Enter') {
7491
7791
  e.preventDefault();
7492
- const inputVal = e.currentTarget.value;
7493
- setInputValue(inputVal);
7494
- if (inputVal) {
7495
- parseAndCommitInput(inputVal);
7496
- }
7497
- // Blur the input
7498
- e.currentTarget?.blur();
7792
+ parseAndValidateDateInput(dateInputValue);
7499
7793
  }
7500
7794
  };
7501
- return (jsxRuntime.jsx(react.Flex, { direction: "column", gap: 3, children: jsxRuntime.jsxs(react.Flex, { alignItems: "center", gap: "2", width: "auto", minWidth: "300px", children: [jsxRuntime.jsxs(react.Combobox.Root, { collection: collection, value: currentValue ? [currentValue] : [], onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, allowCustomValue: true, selectionBehavior: "replace", openOnClick: true, flex: 1, children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.InputGroup, { startElement: jsxRuntime.jsx(bs.BsClock, {}), children: jsxRuntime.jsx(react.Combobox.Input, { value: inputValue, placeholder: labels.placeholder, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown }) }), jsxRuntime.jsx(react.Combobox.IndicatorGroup, { children: jsxRuntime.jsx(react.Combobox.Trigger, {}) })] }), jsxRuntime.jsx(react.Portal, { disabled: !portalled, children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [jsxRuntime.jsx(react.Combobox.Empty, { children: labels.emptyMessage }), collection.items.map((item) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsxs(react.Flex, { alignItems: "center", gap: 2, width: "100%", children: [jsxRuntime.jsx(react.Text, { flex: 1, children: item.label }), item.durationText && (jsxRuntime.jsx(react.Tag.Root, { size: "sm", children: jsxRuntime.jsx(react.Tag.Label, { children: item.durationText }) }))] }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value)))] }) }) })] }), durationDiff && (jsxRuntime.jsx(react.Tag.Root, { size: "sm", children: jsxRuntime.jsx(react.Tag.Label, { children: durationDiff }) })), jsxRuntime.jsx(react.Button, { onClick: handleClear, size: "sm", variant: "ghost", children: jsxRuntime.jsx(react.Icon, { children: jsxRuntime.jsx(md.MdCancel, {}) }) })] }) }));
7502
- }
7503
-
7504
- dayjs.extend(utc);
7505
- dayjs.extend(timezone);
7506
- function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds = false, labels = {
7507
- monthNamesShort: [
7508
- 'Jan',
7509
- 'Feb',
7510
- 'Mar',
7511
- 'Apr',
7512
- 'May',
7513
- 'Jun',
7514
- 'Jul',
7515
- 'Aug',
7516
- 'Sep',
7517
- 'Oct',
7518
- 'Nov',
7519
- 'Dec',
7520
- ],
7521
- weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
7522
- backButtonLabel: 'Back',
7523
- forwardButtonLabel: 'Next',
7524
- }, timePickerLabels, timezone = 'Asia/Hong_Kong', startTime, minDate, maxDate, portalled = false, defaultDate, defaultTime, }) {
7525
- console.debug('[DateTimePicker] Component initialized with props:', {
7526
- value,
7527
- format,
7528
- showSeconds,
7529
- timezone,
7530
- startTime,
7531
- minDate,
7532
- maxDate,
7533
- });
7534
- // Initialize selectedDate from value prop, converting ISO to YYYY-MM-DD format
7535
- const getDateString = React.useCallback((val) => {
7536
- if (!val)
7537
- return '';
7538
- const dateObj = dayjs(val).tz(timezone);
7539
- return dateObj.isValid() ? dateObj.format('YYYY-MM-DD') : '';
7540
- }, [timezone]);
7541
- const [selectedDate, setSelectedDate] = React.useState(getDateString(value));
7542
- // Helper to get time values from value prop with timezone
7543
- const getTimeFromValue = React.useCallback((val) => {
7544
- console.debug('[DateTimePicker] getTimeFromValue called:', {
7545
- val,
7546
- timezone,
7547
- showSeconds,
7548
- });
7549
- if (!val) {
7550
- console.debug('[DateTimePicker] No value provided, returning nulls');
7551
- return {
7552
- hour12: null,
7553
- minute: null,
7554
- meridiem: null,
7555
- hour24: null,
7556
- second: null,
7557
- };
7795
+ // Helper functions to get dates in the correct timezone
7796
+ const getToday = () => dayjs().tz(tz).startOf('day').toDate();
7797
+ const getYesterday = () => dayjs().tz(tz).subtract(1, 'day').startOf('day').toDate();
7798
+ const getTomorrow = () => dayjs().tz(tz).add(1, 'day').startOf('day').toDate();
7799
+ const getPlus7Days = () => dayjs().tz(tz).add(7, 'day').startOf('day').toDate();
7800
+ // Check if a date is within min/max constraints
7801
+ const isDateValid = (date) => {
7802
+ if (minDate) {
7803
+ const minDateStart = dayjs(minDate).tz(tz).startOf('day').toDate();
7804
+ const dateStart = dayjs(date).tz(tz).startOf('day').toDate();
7805
+ if (dateStart < minDateStart)
7806
+ return false;
7558
7807
  }
7559
- const dateObj = dayjs(val).tz(timezone);
7560
- console.debug('[DateTimePicker] Parsed date object:', {
7561
- original: val,
7562
- timezone,
7563
- isValid: dateObj.isValid(),
7564
- formatted: dateObj.format('YYYY-MM-DD HH:mm:ss Z'),
7565
- hour24: dateObj.hour(),
7566
- minute: dateObj.minute(),
7567
- second: dateObj.second(),
7568
- });
7569
- if (!dateObj.isValid()) {
7570
- console.debug('[DateTimePicker] Invalid date object, returning nulls');
7571
- return {
7572
- hour12: null,
7573
- minute: null,
7574
- meridiem: null,
7575
- hour24: null,
7576
- second: null,
7577
- };
7808
+ if (maxDate) {
7809
+ const maxDateStart = dayjs(maxDate).tz(tz).startOf('day').toDate();
7810
+ const dateStart = dayjs(date).tz(tz).startOf('day').toDate();
7811
+ if (dateStart > maxDateStart)
7812
+ return false;
7578
7813
  }
7579
- const hour24Value = dateObj.hour();
7580
- const hour12Value = hour24Value % 12 || 12;
7581
- const minuteValue = dateObj.minute();
7582
- const meridiemValue = hour24Value >= 12 ? 'pm' : 'am';
7583
- const secondValue = showSeconds ? dateObj.second() : null;
7584
- const result = {
7585
- hour12: hour12Value,
7586
- minute: minuteValue,
7587
- meridiem: meridiemValue,
7588
- hour24: hour24Value,
7589
- second: secondValue,
7590
- };
7591
- console.debug('[DateTimePicker] Extracted time values:', result);
7592
- return result;
7593
- }, [timezone, showSeconds]);
7594
- const initialTime = getTimeFromValue(value);
7595
- console.debug('[DateTimePicker] Initial time from value:', {
7596
- value,
7597
- initialTime,
7598
- });
7599
- // Normalize startTime to ignore milliseconds (needed for effectiveDefaultDate calculation)
7600
- const normalizedStartTime = startTime
7601
- ? dayjs(startTime).tz(timezone).millisecond(0).toISOString()
7602
- : undefined;
7603
- // Calculate effective defaultDate: use prop if provided, otherwise use startTime date, otherwise use today
7604
- const effectiveDefaultDate = React.useMemo(() => {
7605
- if (defaultDate) {
7606
- return defaultDate;
7607
- }
7608
- if (normalizedStartTime &&
7609
- dayjs(normalizedStartTime).tz(timezone).isValid()) {
7610
- return dayjs(normalizedStartTime).tz(timezone).format('YYYY-MM-DD');
7611
- }
7612
- return dayjs().tz(timezone).format('YYYY-MM-DD');
7613
- }, [defaultDate, normalizedStartTime, timezone]);
7614
- // Initialize time with default values if no value is provided
7615
- const getInitialTimeValues = () => {
7616
- if (value && initialTime.hour12 !== null) {
7617
- return initialTime;
7618
- }
7619
- // If no value or no time in value, use defaultTime or 00:00
7620
- if (defaultTime) {
7621
- if (format === 'iso-date-time') {
7622
- const defaultTime24 = defaultTime;
7623
- return {
7624
- hour12: null,
7625
- minute: defaultTime24.minute ?? 0,
7626
- meridiem: null,
7627
- hour24: defaultTime24.hour ?? 0,
7628
- second: showSeconds ? defaultTime24.second ?? 0 : null,
7629
- };
7630
- }
7631
- else {
7632
- const defaultTime12 = defaultTime;
7633
- return {
7634
- hour12: defaultTime12.hour ?? 12,
7635
- minute: defaultTime12.minute ?? 0,
7636
- meridiem: defaultTime12.meridiem ?? 'am',
7637
- hour24: null,
7638
- second: null,
7639
- };
7640
- }
7814
+ return true;
7815
+ };
7816
+ // Handle quick action button clicks
7817
+ const handleQuickActionClick = (date) => {
7818
+ if (isDateValid(date)) {
7819
+ setSelectedDate(date);
7820
+ updateDateTime(date, hour, minute, second, meridiem);
7821
+ // Close the calendar popover if open
7822
+ setCalendarPopoverOpen(false);
7641
7823
  }
7642
- // Default to 00:00
7643
- if (format === 'iso-date-time') {
7644
- return {
7645
- hour12: null,
7646
- minute: 0,
7647
- meridiem: null,
7648
- hour24: 0,
7649
- second: showSeconds ? 0 : null,
7650
- };
7824
+ };
7825
+ // Display text for buttons
7826
+ const dateDisplayText = React.useMemo(() => {
7827
+ if (!selectedDate)
7828
+ return 'Select date';
7829
+ return dayjs(selectedDate).tz(tz).format('YYYY-MM-DD');
7830
+ }, [selectedDate, tz]);
7831
+ const timeDisplayText = React.useMemo(() => {
7832
+ if (hour === null || minute === null)
7833
+ return 'Select time';
7834
+ if (is24Hour) {
7835
+ // 24-hour format: never show meridiem, always use 24-hour format (0-23)
7836
+ const hour24 = hour >= 0 && hour <= 23 ? hour : hour % 24;
7837
+ const s = second ?? 0;
7838
+ if (showSeconds) {
7839
+ return `${hour24.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
7840
+ }
7841
+ return `${hour24.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
7651
7842
  }
7652
7843
  else {
7653
- return {
7654
- hour12: 12,
7655
- minute: 0,
7656
- meridiem: 'am',
7657
- hour24: null,
7658
- second: null,
7659
- };
7660
- }
7661
- };
7662
- const initialTimeValues = getInitialTimeValues();
7663
- // Time state for 12-hour format
7664
- const [hour12, setHour12] = React.useState(initialTimeValues.hour12);
7665
- const [minute, setMinute] = React.useState(initialTimeValues.minute);
7666
- const [meridiem, setMeridiem] = React.useState(initialTimeValues.meridiem);
7667
- // Time state for 24-hour format
7668
- const [hour24, setHour24] = React.useState(initialTimeValues.hour24);
7669
- const [second, setSecond] = React.useState(initialTimeValues.second);
7670
- // Sync selectedDate and time states when value prop changes
7844
+ // 12-hour format: always show meridiem (AM/PM)
7845
+ const hour12 = hour >= 1 && hour <= 12 ? hour : hour % 12;
7846
+ if (meridiem === null)
7847
+ return 'Select time';
7848
+ const hourDisplay = hour12.toString();
7849
+ const minuteDisplay = minute.toString().padStart(2, '0');
7850
+ return `${hourDisplay}:${minuteDisplay} ${meridiem.toUpperCase()}`;
7851
+ }
7852
+ }, [hour, minute, second, meridiem, is24Hour, showSeconds]);
7853
+ const timezoneDisplayText = React.useMemo(() => {
7854
+ if (!showTimezoneSelector)
7855
+ return '';
7856
+ // Show offset as is (e.g., "+08:00")
7857
+ return timezoneOffset;
7858
+ }, [timezoneOffset, showTimezoneSelector]);
7859
+ // Update selectedDate when value changes externally
7671
7860
  React.useEffect(() => {
7672
- console.debug('[DateTimePicker] useEffect triggered - value changed:', {
7673
- value,
7674
- timezone,
7675
- format,
7676
- });
7677
- // If value is null, undefined, or invalid, clear date but keep default time values
7678
- if (!value || value === null || value === undefined) {
7679
- console.debug('[DateTimePicker] Value is null/undefined, clearing date but keeping default time');
7680
- setSelectedDate('');
7681
- // Keep default time values instead of clearing them
7682
- if (format === 'iso-date-time') {
7683
- setHour24(defaultTime ? defaultTime.hour ?? 0 : 0);
7684
- setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
7685
- setSecond(showSeconds
7686
- ? defaultTime
7687
- ? defaultTime.second ?? 0
7688
- : 0
7689
- : null);
7690
- }
7691
- else {
7692
- setHour12(defaultTime ? defaultTime.hour ?? 12 : 12);
7693
- setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
7694
- setMeridiem(defaultTime ? defaultTime.meridiem ?? 'am' : 'am');
7861
+ if (parsedValue) {
7862
+ setSelectedDate(parsedValue.toDate());
7863
+ setHour(parsedValue.hour());
7864
+ setMinute(parsedValue.minute());
7865
+ setSecond(parsedValue.second());
7866
+ if (!is24Hour) {
7867
+ const h = parsedValue.hour();
7868
+ setMeridiem(h < 12 ? 'am' : 'pm');
7695
7869
  }
7870
+ }
7871
+ }, [parsedValue, is24Hour]);
7872
+ // Combine date and time and call onChange
7873
+ const updateDateTime = (newDate, newHour, newMinute, newSecond, newMeridiem, timezoneOffsetOverride) => {
7874
+ if (!newDate || newHour === null || newMinute === null) {
7875
+ onChange?.(undefined);
7696
7876
  return;
7697
7877
  }
7698
- // Check if value is valid
7699
- const dateObj = dayjs(value).tz(timezone);
7700
- if (!dateObj.isValid()) {
7701
- console.debug('[DateTimePicker] Invalid value, clearing date but keeping default time');
7702
- setSelectedDate('');
7703
- // Keep default time values instead of clearing them
7704
- if (format === 'iso-date-time') {
7705
- setHour24(defaultTime ? defaultTime.hour ?? 0 : 0);
7706
- setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
7707
- setSecond(showSeconds
7708
- ? defaultTime
7709
- ? defaultTime.second ?? 0
7710
- : 0
7711
- : null);
7878
+ // Convert 12-hour to 24-hour if needed
7879
+ let hour24 = newHour;
7880
+ if (!is24Hour && newMeridiem) {
7881
+ // In 12-hour format, hour should be 1-12
7882
+ // If hour is > 12, it might already be in 24-hour format, convert it first
7883
+ let hour12 = newHour;
7884
+ if (newHour > 12) {
7885
+ // Hour is in 24-hour format, convert to 12-hour first
7886
+ if (newHour === 12) {
7887
+ hour12 = 12;
7888
+ }
7889
+ else {
7890
+ hour12 = newHour - 12;
7891
+ }
7892
+ }
7893
+ // Now convert 12-hour to 24-hour format (0-23)
7894
+ if (newMeridiem === 'am') {
7895
+ if (hour12 === 12) {
7896
+ hour24 = 0; // 12 AM = 0:00
7897
+ }
7898
+ else {
7899
+ hour24 = hour12; // 1-11 AM = 1-11
7900
+ }
7712
7901
  }
7713
7902
  else {
7714
- setHour12(defaultTime ? defaultTime.hour ?? 12 : 12);
7715
- setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
7716
- setMeridiem(defaultTime ? defaultTime.meridiem ?? 'am' : 'am');
7903
+ // PM
7904
+ if (hour12 === 12) {
7905
+ hour24 = 12; // 12 PM = 12:00
7906
+ }
7907
+ else {
7908
+ hour24 = hour12 + 12; // 1-11 PM = 13-23
7909
+ }
7717
7910
  }
7911
+ }
7912
+ else if (!is24Hour && !newMeridiem) {
7913
+ // If in 12-hour mode but no meridiem, assume the hour is already in 12-hour format
7914
+ // and default to AM (or keep as is if it's a valid 12-hour value)
7915
+ // This shouldn't happen in normal flow, but handle it gracefully
7916
+ hour24 = newHour;
7917
+ }
7918
+ // If timezone selector is enabled, create date-time without timezone conversion
7919
+ // to ensure the selected timestamp matches the picker values exactly
7920
+ if (showTimezoneSelector) {
7921
+ // Use override if provided, otherwise use state value
7922
+ const offsetToUse = timezoneOffsetOverride ?? timezoneOffset;
7923
+ // Create date-time from the Date object without timezone conversion
7924
+ // Extract year, month, day from the date
7925
+ const year = newDate.getFullYear();
7926
+ const month = newDate.getMonth();
7927
+ const day = newDate.getDate();
7928
+ // Create a date-time string with the exact values from the picker
7929
+ const formattedDateTime = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}T${String(hour24).padStart(2, '0')}:${String(newMinute).padStart(2, '0')}:${String(newSecond ?? 0).padStart(2, '0')}`;
7930
+ onChange?.(`${formattedDateTime}${offsetToUse}`);
7718
7931
  return;
7719
7932
  }
7720
- const dateString = getDateString(value);
7721
- console.debug('[DateTimePicker] Setting selectedDate:', dateString);
7722
- setSelectedDate(dateString);
7723
- const timeData = getTimeFromValue(value);
7724
- console.debug('[DateTimePicker] Updating time states:', {
7725
- timeData,
7726
- });
7727
- setHour12(timeData.hour12);
7728
- setMinute(timeData.minute);
7729
- setMeridiem(timeData.meridiem);
7730
- setHour24(timeData.hour24);
7731
- setSecond(timeData.second);
7732
- }, [value, getTimeFromValue, getDateString, timezone]);
7733
- const handleDateChange = (date) => {
7734
- console.debug('[DateTimePicker] handleDateChange called:', {
7735
- date,
7736
- timezone,
7737
- showSeconds,
7738
- currentTimeStates: { hour12, minute, meridiem, hour24, second },
7739
- });
7740
- // If date is empty or invalid, clear all fields
7741
- if (!date || date === '') {
7742
- console.debug('[DateTimePicker] Empty date, clearing all fields');
7743
- setSelectedDate('');
7744
- setHour12(null);
7745
- setMinute(null);
7746
- setMeridiem(null);
7747
- setHour24(null);
7748
- setSecond(null);
7933
+ // Normal mode: use timezone conversion
7934
+ let dateTime = dayjs(newDate)
7935
+ .tz(tz)
7936
+ .hour(hour24)
7937
+ .minute(newMinute)
7938
+ .second(newSecond ?? 0)
7939
+ .millisecond(0);
7940
+ if (!dateTime.isValid()) {
7749
7941
  onChange?.(undefined);
7750
7942
  return;
7751
7943
  }
7944
+ // Format based on format prop
7945
+ if (format === 'iso-date-time') {
7946
+ onChange?.(dateTime.format('YYYY-MM-DDTHH:mm:ss'));
7947
+ }
7948
+ else {
7949
+ // date-time format with timezone
7950
+ onChange?.(dateTime.format('YYYY-MM-DDTHH:mm:ssZ'));
7951
+ }
7952
+ };
7953
+ // Handle date selection
7954
+ const handleDateSelected = ({ date, }) => {
7752
7955
  setSelectedDate(date);
7753
- // Parse the date string (YYYY-MM-DD) in the specified timezone
7754
- const dateObj = dayjs.tz(date, timezone);
7755
- console.debug('[DateTimePicker] Parsed date object:', {
7756
- date,
7757
- timezone,
7758
- isValid: dateObj.isValid(),
7759
- isoString: dateObj.toISOString(),
7760
- formatted: dateObj.format('YYYY-MM-DD HH:mm:ss Z'),
7761
- });
7762
- if (!dateObj.isValid()) {
7763
- console.warn('[DateTimePicker] Invalid date object in handleDateChange, clearing fields');
7764
- setSelectedDate('');
7765
- setHour12(null);
7766
- setMinute(null);
7767
- setMeridiem(null);
7768
- setHour24(null);
7769
- setSecond(null);
7770
- onChange?.(undefined);
7771
- return;
7956
+ updateDateTime(date, hour, minute, second, meridiem);
7957
+ };
7958
+ // Handle time change
7959
+ const handleTimeChange = (newHour, newMinute, newSecond, newMeridiem) => {
7960
+ setHour(newHour);
7961
+ setMinute(newMinute);
7962
+ if (is24Hour) {
7963
+ setSecond(newSecond);
7964
+ }
7965
+ else {
7966
+ setMeridiem(newMeridiem);
7967
+ }
7968
+ if (selectedDate) {
7969
+ updateDateTime(selectedDate, newHour, newMinute, newSecond, newMeridiem);
7970
+ }
7971
+ };
7972
+ // Calendar hook
7973
+ const calendarProps = useCalendar({
7974
+ selected: selectedDate || undefined,
7975
+ date: selectedDate || undefined,
7976
+ minDate,
7977
+ maxDate,
7978
+ monthsToDisplay: 1,
7979
+ onDateSelected: handleDateSelected,
7980
+ });
7981
+ // Generate time options
7982
+ const timeOptions = React.useMemo(() => {
7983
+ const options = [];
7984
+ // Get start time for comparison if provided
7985
+ let startDateTime = null;
7986
+ let shouldFilterByDate = false;
7987
+ if (startTime && selectedDate) {
7988
+ const startDateObj = dayjs(startTime).tz(tz);
7989
+ const selectedDateObj = dayjs(selectedDate).tz(tz);
7990
+ if (startDateObj.isValid() && selectedDateObj.isValid()) {
7991
+ startDateTime = startDateObj;
7992
+ shouldFilterByDate =
7993
+ startDateObj.format('YYYY-MM-DD') ===
7994
+ selectedDateObj.format('YYYY-MM-DD');
7995
+ }
7772
7996
  }
7773
- // Check if time values are null - if so, use defaultTime or set to 00:00
7774
- const hasTimeValues = format === 'iso-date-time'
7775
- ? hour24 !== null || minute !== null
7776
- : hour12 !== null || minute !== null || meridiem !== null;
7777
- let timeDataToUse = undefined;
7778
- if (!hasTimeValues) {
7779
- // Use defaultTime if provided, otherwise default to 00:00
7780
- if (defaultTime) {
7781
- console.debug('[DateTimePicker] No time values set, using defaultTime');
7782
- if (format === 'iso-date-time') {
7783
- const defaultTime24 = defaultTime;
7784
- setHour24(defaultTime24.hour ?? 0);
7785
- setMinute(defaultTime24.minute ?? 0);
7786
- if (showSeconds) {
7787
- setSecond(defaultTime24.second ?? 0);
7997
+ if (is24Hour) {
7998
+ // Generate 24-hour format options
7999
+ for (let h = 0; h < 24; h++) {
8000
+ for (let m = 0; m < 60; m += 15) {
8001
+ // Filter out times that would result in negative duration
8002
+ if (startDateTime && selectedDate && shouldFilterByDate) {
8003
+ const selectedDateObj = dayjs(selectedDate).tz(tz);
8004
+ const optionDateTime = selectedDateObj
8005
+ .hour(h)
8006
+ .minute(m)
8007
+ .second(0)
8008
+ .millisecond(0);
8009
+ if (optionDateTime.isBefore(startDateTime)) {
8010
+ continue;
8011
+ }
7788
8012
  }
7789
- timeDataToUse = {
7790
- hour: defaultTime24.hour ?? 0,
7791
- minute: defaultTime24.minute ?? 0,
7792
- second: showSeconds ? defaultTime24.second ?? 0 : undefined,
7793
- };
7794
- }
7795
- else {
7796
- const defaultTime12 = defaultTime;
7797
- setHour12(defaultTime12.hour ?? 12);
7798
- setMinute(defaultTime12.minute ?? 0);
7799
- setMeridiem(defaultTime12.meridiem ?? 'am');
7800
- timeDataToUse = {
7801
- hour: defaultTime12.hour ?? 12,
7802
- minute: defaultTime12.minute ?? 0,
7803
- meridiem: defaultTime12.meridiem ?? 'am',
7804
- };
8013
+ // Calculate duration if startTime is provided
8014
+ let durationText;
8015
+ if (startDateTime && selectedDate) {
8016
+ const selectedDateObj = dayjs(selectedDate).tz(tz);
8017
+ const optionDateTime = selectedDateObj
8018
+ .hour(h)
8019
+ .minute(m)
8020
+ .second(0)
8021
+ .millisecond(0);
8022
+ if (optionDateTime.isValid() &&
8023
+ optionDateTime.isAfter(startDateTime)) {
8024
+ const diffMs = optionDateTime.diff(startDateTime);
8025
+ const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
8026
+ const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
8027
+ const diffSeconds = Math.floor((diffMs % (1000 * 60)) / 1000);
8028
+ if (diffHours > 0 || diffMinutes > 0 || diffSeconds > 0) {
8029
+ let diffText = '';
8030
+ if (diffHours > 0) {
8031
+ diffText = `${diffHours}h ${diffMinutes}m`;
8032
+ }
8033
+ else if (diffMinutes > 0) {
8034
+ diffText = `${diffMinutes}m ${diffSeconds}s`;
8035
+ }
8036
+ else {
8037
+ diffText = `${diffSeconds}s`;
8038
+ }
8039
+ durationText = `+${diffText}`;
8040
+ }
8041
+ }
8042
+ }
8043
+ const s = showSeconds ? 0 : 0;
8044
+ const timeDisplay = showSeconds
8045
+ ? `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:00`
8046
+ : `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`;
8047
+ options.push({
8048
+ label: timeDisplay,
8049
+ value: `${h}:${m}:${s}`,
8050
+ hour: h,
8051
+ minute: m,
8052
+ second: s,
8053
+ searchText: timeDisplay,
8054
+ durationText,
8055
+ });
7805
8056
  }
7806
8057
  }
7807
- else {
7808
- console.debug('[DateTimePicker] No time values set, defaulting to 00:00');
7809
- if (format === 'iso-date-time') {
7810
- setHour24(0);
7811
- setMinute(0);
7812
- if (showSeconds) {
7813
- setSecond(0);
8058
+ }
8059
+ else {
8060
+ // Generate 12-hour format options
8061
+ for (let h = 1; h <= 12; h++) {
8062
+ for (let m = 0; m < 60; m += 15) {
8063
+ for (const mer of ['am', 'pm']) {
8064
+ // Convert 12-hour to 24-hour for comparison
8065
+ let hour24 = h;
8066
+ if (mer === 'am' && h === 12)
8067
+ hour24 = 0;
8068
+ else if (mer === 'pm' && h < 12)
8069
+ hour24 = h + 12;
8070
+ // Filter out times that would result in negative duration
8071
+ if (startDateTime && selectedDate && shouldFilterByDate) {
8072
+ const selectedDateObj = dayjs(selectedDate).tz(tz);
8073
+ const optionDateTime = selectedDateObj
8074
+ .hour(hour24)
8075
+ .minute(m)
8076
+ .second(0)
8077
+ .millisecond(0);
8078
+ if (optionDateTime.isBefore(startDateTime)) {
8079
+ continue;
8080
+ }
8081
+ }
8082
+ // Calculate duration if startTime is provided
8083
+ let durationText;
8084
+ if (startDateTime && selectedDate) {
8085
+ const selectedDateObj = dayjs(selectedDate).tz(tz);
8086
+ const optionDateTime = selectedDateObj
8087
+ .hour(hour24)
8088
+ .minute(m)
8089
+ .second(0)
8090
+ .millisecond(0);
8091
+ if (optionDateTime.isValid() &&
8092
+ optionDateTime.isAfter(startDateTime)) {
8093
+ const diffMs = optionDateTime.diff(startDateTime);
8094
+ const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
8095
+ const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
8096
+ const diffSeconds = Math.floor((diffMs % (1000 * 60)) / 1000);
8097
+ if (diffHours > 0 || diffMinutes > 0 || diffSeconds > 0) {
8098
+ let diffText = '';
8099
+ if (diffHours > 0) {
8100
+ diffText = `${diffHours}h ${diffMinutes}m`;
8101
+ }
8102
+ else if (diffMinutes > 0) {
8103
+ diffText = `${diffMinutes}m ${diffSeconds}s`;
8104
+ }
8105
+ else {
8106
+ diffText = `${diffSeconds}s`;
8107
+ }
8108
+ durationText = `+${diffText}`;
8109
+ }
8110
+ }
8111
+ }
8112
+ const hourDisplay = h.toString();
8113
+ const minuteDisplay = m.toString().padStart(2, '0');
8114
+ const timeDisplay = `${hourDisplay}:${minuteDisplay} ${mer.toUpperCase()}`;
8115
+ options.push({
8116
+ label: timeDisplay,
8117
+ value: `${h}:${m}:${mer}`,
8118
+ hour: h,
8119
+ minute: m,
8120
+ meridiem: mer,
8121
+ searchText: timeDisplay,
8122
+ durationText,
8123
+ });
7814
8124
  }
7815
- timeDataToUse = {
7816
- hour: 0,
7817
- minute: 0,
7818
- second: showSeconds ? 0 : undefined,
7819
- };
7820
- }
7821
- else {
7822
- setHour12(12);
7823
- setMinute(0);
7824
- setMeridiem('am');
7825
- timeDataToUse = {
7826
- hour: 12,
7827
- minute: 0,
7828
- meridiem: 'am',
7829
- };
7830
8125
  }
7831
8126
  }
8127
+ // Sort 12-hour options by time
8128
+ return options.sort((a, b) => {
8129
+ const a12 = a;
8130
+ const b12 = b;
8131
+ let hour24A = a12.hour;
8132
+ if (a12.meridiem === 'am' && a12.hour === 12)
8133
+ hour24A = 0;
8134
+ else if (a12.meridiem === 'pm' && a12.hour < 12)
8135
+ hour24A = a12.hour + 12;
8136
+ let hour24B = b12.hour;
8137
+ if (b12.meridiem === 'am' && b12.hour === 12)
8138
+ hour24B = 0;
8139
+ else if (b12.meridiem === 'pm' && b12.hour < 12)
8140
+ hour24B = b12.hour + 12;
8141
+ if (hour24A !== hour24B) {
8142
+ return hour24A - hour24B;
8143
+ }
8144
+ return a12.minute - b12.minute;
8145
+ });
7832
8146
  }
7833
- // When showSeconds is false, ignore seconds from the date
7834
- if (!showSeconds) {
7835
- const dateWithoutSeconds = dateObj.second(0).millisecond(0).toISOString();
7836
- console.debug('[DateTimePicker] Updating date without seconds:', dateWithoutSeconds);
7837
- updateDateTime(dateWithoutSeconds, timeDataToUse);
7838
- }
7839
- else {
7840
- const dateWithSeconds = dateObj.toISOString();
7841
- console.debug('[DateTimePicker] Updating date with seconds:', dateWithSeconds);
7842
- updateDateTime(dateWithSeconds, timeDataToUse);
8147
+ return options;
8148
+ }, [startTime, selectedDate, tz, is24Hour, showSeconds]);
8149
+ // Time picker combobox setup
8150
+ const itemToString = React.useMemo(() => {
8151
+ return (item) => {
8152
+ return item.searchText;
8153
+ };
8154
+ }, []);
8155
+ const { contains } = react.useFilter({ sensitivity: 'base' });
8156
+ const customTimeFilter = React.useMemo(() => {
8157
+ if (is24Hour) {
8158
+ return contains;
7843
8159
  }
7844
- };
7845
- const handleTimeChange = (timeData) => {
7846
- console.debug('[DateTimePicker] handleTimeChange called:', {
7847
- timeData,
7848
- format,
7849
- selectedDate,
7850
- timezone,
7851
- });
7852
- if (format === 'iso-date-time') {
7853
- const data = timeData;
7854
- console.debug('[DateTimePicker] ISO format - setting 24-hour time:', data);
7855
- setHour24(data.hour);
7856
- setMinute(data.minute);
7857
- if (showSeconds) {
7858
- setSecond(data.second ?? null);
8160
+ return (itemText, filterText) => {
8161
+ if (!filterText) {
8162
+ return true;
7859
8163
  }
7860
- else {
7861
- // Ignore seconds - always set to null when showSeconds is false
7862
- setSecond(null);
8164
+ const lowerItemText = itemText.toLowerCase();
8165
+ const lowerFilterText = filterText.toLowerCase();
8166
+ if (lowerItemText.includes(lowerFilterText)) {
8167
+ return true;
8168
+ }
8169
+ const item = timeOptions.find((opt) => opt.searchText.toLowerCase() === lowerItemText);
8170
+ if (!item || !('meridiem' in item)) {
8171
+ return false;
7863
8172
  }
8173
+ let hour24 = item.hour;
8174
+ if (item.meridiem === 'am' && item.hour === 12)
8175
+ hour24 = 0;
8176
+ else if (item.meridiem === 'pm' && item.hour < 12)
8177
+ hour24 = item.hour + 12;
8178
+ const hour24Str = hour24.toString().padStart(2, '0');
8179
+ const minuteStr = item.minute.toString().padStart(2, '0');
8180
+ const formats = [
8181
+ `${hour24Str}:${minuteStr}`,
8182
+ `${hour24Str}${minuteStr}`,
8183
+ hour24Str,
8184
+ `${hour24}:${minuteStr}`,
8185
+ hour24.toString(),
8186
+ ];
8187
+ return formats.some((format) => format.toLowerCase().includes(lowerFilterText) ||
8188
+ lowerFilterText.includes(format.toLowerCase()));
8189
+ };
8190
+ }, [timeOptions, is24Hour, contains]);
8191
+ const { collection, filter } = react.useListCollection({
8192
+ initialItems: timeOptions,
8193
+ itemToString: itemToString,
8194
+ itemToValue: (item) => item.value,
8195
+ filter: customTimeFilter,
8196
+ });
8197
+ // Get current value string for combobox (must match option.value format)
8198
+ const currentTimeValue = React.useMemo(() => {
8199
+ if (is24Hour) {
8200
+ if (hour === null || minute === null) {
8201
+ return '';
8202
+ }
8203
+ const s = second ?? 0;
8204
+ return `${hour}:${minute}:${s}`;
7864
8205
  }
7865
8206
  else {
7866
- const data = timeData;
7867
- console.debug('[DateTimePicker] 12-hour format - setting time:', data);
7868
- setHour12(data.hour);
7869
- setMinute(data.minute);
7870
- setMeridiem(data.meridiem);
7871
- }
7872
- // Use selectedDate if valid, otherwise use effectiveDefaultDate or clear all fields
7873
- if (!selectedDate || !dayjs(selectedDate).isValid()) {
7874
- // If effectiveDefaultDate is available, use it instead of clearing
7875
- if (effectiveDefaultDate && dayjs(effectiveDefaultDate).isValid()) {
7876
- console.debug('[DateTimePicker] No valid selectedDate, using effectiveDefaultDate:', effectiveDefaultDate);
7877
- setSelectedDate(effectiveDefaultDate);
7878
- const dateObj = dayjs(effectiveDefaultDate).tz(timezone);
7879
- if (dateObj.isValid()) {
7880
- updateDateTime(dateObj.toISOString(), timeData);
8207
+ if (hour === null || minute === null || meridiem === null) {
8208
+ return '';
8209
+ }
8210
+ return `${hour}:${minute}:${meridiem}`;
8211
+ }
8212
+ }, [hour, minute, second, meridiem, is24Hour]);
8213
+ // Parse custom time input formats like "1400", "2pm", "14:00", "2:00 PM"
8214
+ const parseCustomTimeInput = (input) => {
8215
+ if (!input || !input.trim()) {
8216
+ return { hour: null, minute: null, second: null, meridiem: null };
8217
+ }
8218
+ const trimmed = input.trim().toLowerCase();
8219
+ // Try parsing 4-digit format without colon: "1400" -> 14:00
8220
+ const fourDigitMatch = trimmed.match(/^(\d{4})$/);
8221
+ if (fourDigitMatch) {
8222
+ const digits = fourDigitMatch[1];
8223
+ const hour = parseInt(digits.substring(0, 2), 10);
8224
+ const minute = parseInt(digits.substring(2, 4), 10);
8225
+ if (hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59) {
8226
+ if (is24Hour) {
8227
+ return { hour, minute, second: 0, meridiem: null };
7881
8228
  }
7882
8229
  else {
7883
- console.warn('[DateTimePicker] Invalid effectiveDefaultDate, clearing fields');
7884
- setSelectedDate('');
7885
- setHour12(null);
7886
- setMinute(null);
7887
- setMeridiem(null);
7888
- setHour24(null);
7889
- setSecond(null);
7890
- onChange?.(undefined);
8230
+ // Convert to 12-hour format
8231
+ let hour12 = hour;
8232
+ let meridiem;
8233
+ if (hour === 0) {
8234
+ hour12 = 12;
8235
+ meridiem = 'am';
8236
+ }
8237
+ else if (hour === 12) {
8238
+ hour12 = 12;
8239
+ meridiem = 'pm';
8240
+ }
8241
+ else if (hour > 12) {
8242
+ hour12 = hour - 12;
8243
+ meridiem = 'pm';
8244
+ }
8245
+ else {
8246
+ hour12 = hour;
8247
+ meridiem = 'am';
8248
+ }
8249
+ return { hour: hour12, minute, second: null, meridiem };
7891
8250
  }
7892
- return;
7893
8251
  }
7894
- else {
7895
- console.debug('[DateTimePicker] No valid selectedDate and no effectiveDefaultDate, keeping time values but no date');
7896
- // Keep the time values that were just set, but don't set a date
7897
- // This should rarely happen as effectiveDefaultDate always defaults to today
7898
- setSelectedDate('');
7899
- onChange?.(undefined);
7900
- return;
8252
+ }
8253
+ // Try parsing hour with meridiem: "2pm", "14pm", "2am"
8254
+ const hourMeridiemMatch = trimmed.match(/^(\d{1,2})\s*(am|pm)$/);
8255
+ if (hourMeridiemMatch && !is24Hour) {
8256
+ const hour12 = parseInt(hourMeridiemMatch[1], 10);
8257
+ const meridiem = hourMeridiemMatch[2];
8258
+ if (hour12 >= 1 && hour12 <= 12) {
8259
+ return { hour: hour12, minute: 0, second: null, meridiem };
7901
8260
  }
7902
8261
  }
7903
- const dateObj = dayjs(selectedDate).tz(timezone);
7904
- if (dateObj.isValid()) {
7905
- updateDateTime(dateObj.toISOString(), timeData);
8262
+ // Try parsing 24-hour format with hour only: "14" -> 14:00
8263
+ const hourOnlyMatch = trimmed.match(/^(\d{1,2})$/);
8264
+ if (hourOnlyMatch && is24Hour) {
8265
+ const hour = parseInt(hourOnlyMatch[1], 10);
8266
+ if (hour >= 0 && hour <= 23) {
8267
+ return { hour, minute: 0, second: 0, meridiem: null };
8268
+ }
7906
8269
  }
7907
- else {
7908
- console.warn('[DateTimePicker] Invalid date object in handleTimeChange, clearing fields');
7909
- setSelectedDate('');
7910
- setHour12(null);
7911
- setMinute(null);
7912
- setMeridiem(null);
7913
- setHour24(null);
7914
- setSecond(null);
7915
- onChange?.(undefined);
8270
+ // Try parsing standard formats: "14:00", "2:00 PM"
8271
+ const time24Pattern = /^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/;
8272
+ const match24 = trimmed.match(time24Pattern);
8273
+ if (match24) {
8274
+ const hour24 = parseInt(match24[1], 10);
8275
+ const minute = parseInt(match24[2], 10);
8276
+ const second = match24[3] ? parseInt(match24[3], 10) : 0;
8277
+ if (hour24 >= 0 &&
8278
+ hour24 <= 23 &&
8279
+ minute >= 0 &&
8280
+ minute <= 59 &&
8281
+ second >= 0 &&
8282
+ second <= 59) {
8283
+ if (is24Hour) {
8284
+ return { hour: hour24, minute, second, meridiem: null };
8285
+ }
8286
+ else {
8287
+ // Convert to 12-hour format
8288
+ let hour12 = hour24;
8289
+ let meridiem;
8290
+ if (hour24 === 0) {
8291
+ hour12 = 12;
8292
+ meridiem = 'am';
8293
+ }
8294
+ else if (hour24 === 12) {
8295
+ hour12 = 12;
8296
+ meridiem = 'pm';
8297
+ }
8298
+ else if (hour24 > 12) {
8299
+ hour12 = hour24 - 12;
8300
+ meridiem = 'pm';
8301
+ }
8302
+ else {
8303
+ hour12 = hour24;
8304
+ meridiem = 'am';
8305
+ }
8306
+ return { hour: hour12, minute, second: null, meridiem };
8307
+ }
8308
+ }
7916
8309
  }
7917
- };
7918
- const updateDateTime = (date, timeData) => {
7919
- console.debug('[DateTimePicker] updateDateTime called:', {
7920
- date,
7921
- timeData,
7922
- format,
7923
- currentStates: { hour12, minute, meridiem, hour24, second },
7924
- });
7925
- if (!date || date === null || date === undefined) {
7926
- console.debug('[DateTimePicker] No date provided, clearing all fields and calling onChange(undefined)');
7927
- setSelectedDate('');
7928
- setHour12(null);
7929
- setMinute(null);
7930
- setMeridiem(null);
7931
- setHour24(null);
7932
- setSecond(null);
7933
- onChange?.(undefined);
7934
- return;
8310
+ // Try parsing 12-hour format: "2:00 PM", "2:00PM"
8311
+ const time12Pattern = /^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?\s*(am|pm)$/;
8312
+ const match12 = trimmed.match(time12Pattern);
8313
+ if (match12 && !is24Hour) {
8314
+ const hour12 = parseInt(match12[1], 10);
8315
+ const minute = parseInt(match12[2], 10);
8316
+ const second = match12[3] ? parseInt(match12[3], 10) : null;
8317
+ const meridiem = match12[4];
8318
+ if (hour12 >= 1 &&
8319
+ hour12 <= 12 &&
8320
+ minute >= 0 &&
8321
+ minute <= 59 &&
8322
+ (second === null || (second >= 0 && second <= 59))) {
8323
+ return { hour: hour12, minute, second, meridiem };
8324
+ }
7935
8325
  }
7936
- // use dayjs to convert the date to the timezone
7937
- const dateObj = dayjs(date).tz(timezone);
7938
- if (!dateObj.isValid()) {
7939
- console.warn('[DateTimePicker] Invalid date object in updateDateTime, clearing fields:', date);
7940
- setSelectedDate('');
7941
- setHour12(null);
7942
- setMinute(null);
7943
- setMeridiem(null);
7944
- setHour24(null);
7945
- setSecond(null);
7946
- onChange?.(undefined);
8326
+ return { hour: null, minute: null, second: null, meridiem: null };
8327
+ };
8328
+ const handleTimeValueChange = (details) => {
8329
+ if (details.value.length === 0) {
8330
+ handleTimeChange(null, null, null, null);
8331
+ filter('');
7947
8332
  return;
7948
8333
  }
7949
- const newDate = dateObj.toDate();
7950
- if (format === 'iso-date-time') {
7951
- const data = timeData;
7952
- // Use timeData values if provided, otherwise fall back to current state
7953
- // But if timeData is explicitly provided with nulls, we need to check if all are null
7954
- const h = data !== undefined ? data.hour : hour24;
7955
- const m = data !== undefined ? data.minute : minute;
7956
- // Always ignore seconds when showSeconds is false - set to 0
7957
- const s = showSeconds
7958
- ? data !== undefined
7959
- ? data.second ?? null
7960
- : second ?? 0
7961
- : 0;
7962
- // If all time values are null, clear the value
7963
- if (h === null && m === null && (showSeconds ? s === null : true)) {
7964
- console.debug('[DateTimePicker] All time values are null, clearing value');
7965
- onChange?.(undefined);
7966
- return;
7967
- }
7968
- console.debug('[DateTimePicker] ISO format - setting time on date:', {
7969
- h,
7970
- m,
7971
- s,
7972
- showSeconds,
7973
- });
7974
- if (h !== null)
7975
- newDate.setHours(h);
7976
- if (m !== null)
7977
- newDate.setMinutes(m);
7978
- newDate.setSeconds(s ?? 0);
7979
- }
7980
- else {
7981
- const data = timeData;
7982
- console.debug('[DateTimePicker] Processing 12-hour format:', {
7983
- 'data !== undefined': data !== undefined,
7984
- 'data?.hour': data?.hour,
7985
- 'data?.minute': data?.minute,
7986
- 'data?.meridiem': data?.meridiem,
7987
- 'current hour12': hour12,
7988
- 'current minute': minute,
7989
- 'current meridiem': meridiem,
7990
- });
7991
- // Use timeData values if provided, otherwise fall back to current state
7992
- const h = data !== undefined ? data.hour : hour12;
7993
- const m = data !== undefined ? data.minute : minute;
7994
- const mer = data !== undefined ? data.meridiem : meridiem;
7995
- console.debug('[DateTimePicker] Resolved time values:', { h, m, mer });
7996
- // If all time values are null, clear the value
7997
- if (h === null && m === null && mer === null) {
7998
- console.debug('[DateTimePicker] All time values are null, clearing value');
7999
- onChange?.(undefined);
8000
- return;
8001
- }
8002
- console.debug('[DateTimePicker] 12-hour format - converting time:', {
8003
- h,
8004
- m,
8005
- mer,
8006
- });
8007
- if (h !== null && mer !== null) {
8008
- let hour24 = h;
8009
- if (mer === 'am' && h === 12)
8010
- hour24 = 0;
8011
- else if (mer === 'pm' && h < 12)
8012
- hour24 = h + 12;
8013
- console.debug('[DateTimePicker] Converted to 24-hour:', {
8014
- h,
8015
- mer,
8016
- hour24,
8017
- });
8018
- newDate.setHours(hour24);
8019
- }
8020
- else {
8021
- console.debug('[DateTimePicker] Skipping hour update - h or mer is null:', {
8022
- h,
8023
- mer,
8024
- });
8025
- }
8026
- if (m !== null) {
8027
- newDate.setMinutes(m);
8334
+ const selectedValue = details.value[0];
8335
+ const selectedOption = timeOptions.find((opt) => opt.value === selectedValue);
8336
+ if (selectedOption) {
8337
+ filter('');
8338
+ if (is24Hour) {
8339
+ const opt24 = selectedOption;
8340
+ handleTimeChange(opt24.hour, opt24.minute, opt24.second, null);
8028
8341
  }
8029
8342
  else {
8030
- console.debug('[DateTimePicker] Skipping minute update - m is null');
8343
+ const opt12 = selectedOption;
8344
+ handleTimeChange(opt12.hour, opt12.minute, null, opt12.meridiem);
8031
8345
  }
8032
- newDate.setSeconds(0);
8033
8346
  }
8034
- const finalISO = dayjs(newDate).tz(timezone).toISOString();
8035
- console.debug('[DateTimePicker] Final ISO string to emit:', {
8036
- newDate: newDate.toISOString(),
8037
- timezone,
8038
- finalISO,
8039
- });
8040
- onChange?.(finalISO);
8041
8347
  };
8042
- const handleClear = () => {
8043
- setSelectedDate('');
8044
- // Reset to default time values instead of clearing them
8045
- if (format === 'iso-date-time') {
8046
- setHour24(defaultTime ? defaultTime.hour ?? 0 : 0);
8047
- setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
8048
- setSecond(showSeconds
8049
- ? defaultTime
8050
- ? defaultTime.second ?? 0
8051
- : 0
8052
- : null);
8053
- }
8054
- else {
8055
- setHour12(defaultTime ? defaultTime.hour ?? 12 : 12);
8056
- setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
8057
- setMeridiem(defaultTime ? defaultTime.meridiem ?? 'am' : 'am');
8058
- }
8059
- onChange?.(undefined);
8060
- };
8061
- const isISO = format === 'iso-date-time';
8062
- // Determine minDate: prioritize explicit minDate prop, then fall back to startTime
8063
- const effectiveMinDate = minDate
8064
- ? minDate
8065
- : normalizedStartTime && dayjs(normalizedStartTime).tz(timezone).isValid()
8066
- ? dayjs(normalizedStartTime).tz(timezone).startOf('day').toDate()
8067
- : undefined;
8068
- // Log current state before render
8069
- React.useEffect(() => {
8070
- console.debug('[DateTimePicker] Current state before render:', {
8071
- isISO,
8072
- hour12,
8073
- minute,
8074
- meridiem,
8075
- hour24,
8076
- second,
8077
- selectedDate,
8078
- normalizedStartTime,
8079
- timezone,
8080
- });
8081
- }, [
8082
- isISO,
8083
- hour12,
8084
- minute,
8085
- meridiem,
8086
- hour24,
8087
- second,
8088
- selectedDate,
8089
- normalizedStartTime,
8090
- timezone,
8091
- ]);
8092
- // Compute display text from current state
8093
- const displayText = React.useMemo(() => {
8094
- if (!selectedDate)
8095
- return null;
8096
- const dateObj = dayjs.tz(selectedDate, timezone);
8097
- if (!dateObj.isValid())
8098
- return null;
8099
- if (isISO) {
8100
- // For ISO format, use hour24, minute, second
8101
- if (hour24 === null || minute === null)
8102
- return null;
8103
- const dateTimeObj = dateObj
8104
- .hour(hour24)
8105
- .minute(minute)
8106
- .second(second ?? 0);
8107
- return dateTimeObj.format(showSeconds ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm');
8108
- }
8109
- else {
8110
- // For 12-hour format, use hour12, minute, meridiem
8111
- if (hour12 === null || minute === null || meridiem === null)
8112
- return null;
8113
- // Convert to 24-hour format for dayjs
8114
- let hour24Value = hour12;
8115
- if (meridiem === 'am' && hour12 === 12)
8116
- hour24Value = 0;
8117
- else if (meridiem === 'pm' && hour12 < 12)
8118
- hour24Value = hour12 + 12;
8119
- const dateTimeObj = dateObj.hour(hour24Value).minute(minute).second(0);
8120
- return dateTimeObj.format('YYYY-MM-DD hh:mm A');
8348
+ // Track the current input value for Enter key handling
8349
+ const [timeInputValue, setTimeInputValue] = React.useState('');
8350
+ const handleTimeInputChange = (details) => {
8351
+ // Store the input value and filter
8352
+ setTimeInputValue(details.inputValue);
8353
+ filter(details.inputValue);
8354
+ };
8355
+ const handleTimeInputKeyDown = (e) => {
8356
+ if (e.key === 'Enter') {
8357
+ e.preventDefault();
8358
+ // Use the stored input value
8359
+ const parsed = parseCustomTimeInput(timeInputValue);
8360
+ if (parsed.hour !== null && parsed.minute !== null) {
8361
+ if (is24Hour) {
8362
+ handleTimeChange(parsed.hour, parsed.minute, parsed.second, null);
8363
+ }
8364
+ else {
8365
+ if (parsed.meridiem !== null) {
8366
+ handleTimeChange(parsed.hour, parsed.minute, null, parsed.meridiem);
8367
+ }
8368
+ }
8369
+ // Clear the filter and input value after applying
8370
+ filter('');
8371
+ setTimeInputValue('');
8372
+ // Close the popover if value is valid
8373
+ setTimePopoverOpen(false);
8374
+ }
8121
8375
  }
8122
- }, [
8123
- selectedDate,
8124
- isISO,
8125
- hour12,
8126
- minute,
8127
- meridiem,
8128
- hour24,
8129
- second,
8130
- showSeconds,
8131
- timezone,
8132
- ]);
8133
- const timezoneOffset = React.useMemo(() => {
8134
- if (!selectedDate)
8376
+ };
8377
+ // Calendar rendering
8378
+ const renderCalendar = () => {
8379
+ const { calendars, getBackProps, getForwardProps, getDateProps } = calendarProps;
8380
+ if (calendars.length === 0)
8135
8381
  return null;
8136
- const dateObj = dayjs.tz(selectedDate, timezone);
8137
- return dateObj.isValid() ? dateObj.format('Z') : null;
8138
- }, [selectedDate, timezone]);
8139
- return (jsxRuntime.jsxs(react.Flex, { direction: "column", gap: 2, children: [jsxRuntime.jsx(DatePickerInput, { value: selectedDate || undefined, onChange: (date) => {
8140
- if (date) {
8141
- handleDateChange(date);
8142
- }
8143
- else {
8144
- setSelectedDate('');
8145
- onChange?.(undefined);
8146
- }
8147
- }, placeholder: "Select a date", dateFormat: "YYYY-MM-DD", displayFormat: "YYYY-MM-DD", labels: labels, timezone: timezone, minDate: effectiveMinDate, maxDate: maxDate, monthsToDisplay: 1, readOnly: false }), jsxRuntime.jsxs(react.Grid, { templateColumns: "1fr auto", alignItems: "center", gap: 2, children: [isISO ? (jsxRuntime.jsx(IsoTimePicker, { hour: hour24, setHour: setHour24, minute: minute, setMinute: setMinute, second: showSeconds ? second : null, setSecond: showSeconds ? setSecond : () => { }, onChange: handleTimeChange, startTime: normalizedStartTime, selectedDate: selectedDate, timezone: timezone, portalled: portalled, labels: timePickerLabels })) : (jsxRuntime.jsx(TimePicker$1, { hour: hour12, setHour: setHour12, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, startTime: normalizedStartTime, selectedDate: selectedDate, timezone: timezone, portalled: portalled, labels: timePickerLabels })), jsxRuntime.jsx(react.Button, { onClick: handleClear, size: "sm", variant: "outline", colorScheme: "red", children: jsxRuntime.jsx(react.Icon, { as: fa6.FaTrash }) })] }), displayText && (jsxRuntime.jsxs(react.Flex, { gap: 2, children: [jsxRuntime.jsx(react.Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: displayText }), timezoneOffset && (jsxRuntime.jsx(react.Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: timezoneOffset })), jsxRuntime.jsx(react.Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: timezone })] }))] }));
8382
+ const calendar = calendars[0];
8383
+ return (jsxRuntime.jsxs(react.Grid, { gap: 4, children: [jsxRuntime.jsxs(react.Grid, { templateColumns: 'repeat(4, auto)', justifyContent: 'center', children: [jsxRuntime.jsx(react.Button, { variant: 'ghost', ...getBackProps({ offset: 12 }), children: '<<' }), jsxRuntime.jsx(react.Button, { variant: 'ghost', ...getBackProps(), children: backButtonLabel }), jsxRuntime.jsx(react.Button, { variant: 'ghost', ...getForwardProps(), children: forwardButtonLabel }), jsxRuntime.jsx(react.Button, { variant: 'ghost', ...getForwardProps({ offset: 12 }), children: '>>' })] }), jsxRuntime.jsx(react.Grid, { justifyContent: 'center', children: jsxRuntime.jsxs(react.Text, { children: [monthNamesShort[calendar.month], " ", calendar.year] }) }), jsxRuntime.jsx(react.Grid, { templateColumns: 'repeat(7, auto)', justifyContent: 'center', children: [0, 1, 2, 3, 4, 5, 6].map((weekdayNum) => {
8384
+ return (jsxRuntime.jsx(react.Text, { textAlign: 'center', fontWeight: "semibold", minW: "40px", children: weekdayNamesShort[weekdayNum] }, `header-${weekdayNum}`));
8385
+ }) }), calendar.weeks.map((week, weekIndex) => (jsxRuntime.jsx(react.Grid, { templateColumns: 'repeat(7, auto)', justifyContent: 'center', children: week.map((dateObj, dayIndex) => {
8386
+ if (!dateObj) {
8387
+ return (jsxRuntime.jsx("div", { style: { minWidth: '40px' } }, `empty-${dayIndex}`));
8388
+ }
8389
+ const { date, selected, selectable, isCurrentMonth } = dateObj;
8390
+ const dateProps = getDateProps({
8391
+ dateObj,
8392
+ });
8393
+ return (jsxRuntime.jsx(react.Button, { variant: selected ? 'solid' : 'ghost', colorPalette: selected ? 'blue' : undefined, size: "sm", minW: "40px", disabled: !selectable, opacity: isCurrentMonth ? 1 : 0.4, ...dateProps, children: date.getDate() }, `${date.getTime()}`));
8394
+ }) }, `week-${weekIndex}`)))] }));
8395
+ };
8396
+ return (jsxRuntime.jsxs(react.Flex, { direction: "row", gap: 2, align: "center", children: [jsxRuntime.jsxs(react.Popover.Root, { open: datePopoverOpen, onOpenChange: (e) => setDatePopoverOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsxs(react.Button, { size: "sm", variant: "outline", onClick: () => setDatePopoverOpen(true), justifyContent: "start", children: [jsxRuntime.jsx(md.MdDateRange, {}), dateDisplayText] }) }), portalled ? (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "350px", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { p: 4, children: jsxRuntime.jsxs(react.Grid, { gap: 4, children: [jsxRuntime.jsx(react.InputGroup, { endElement: jsxRuntime.jsxs(react.Popover.Root, { open: calendarPopoverOpen, onOpenChange: (e) => setCalendarPopoverOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsx(react.Button, { variant: "ghost", size: "xs", "aria-label": "Open calendar", onClick: () => setCalendarPopoverOpen(true), children: jsxRuntime.jsx(md.MdDateRange, {}) }) }), jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "350px", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { p: 4, children: renderCalendar() }) }) })] }), children: jsxRuntime.jsx(react.Input, { value: dateInputValue, onChange: handleDateInputChange, onBlur: handleDateInputBlur, onKeyDown: handleDateInputKeyDown, placeholder: "YYYY-MM-DD" }) }), showQuickActions && (jsxRuntime.jsxs(react.Grid, { templateColumns: "repeat(4, 1fr)", gap: 2, children: [jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleQuickActionClick(getYesterday()), disabled: !isDateValid(getYesterday()), children: quickActionLabels.yesterday }), jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleQuickActionClick(getToday()), disabled: !isDateValid(getToday()), children: quickActionLabels.today }), jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleQuickActionClick(getTomorrow()), disabled: !isDateValid(getTomorrow()), children: quickActionLabels.tomorrow }), jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleQuickActionClick(getPlus7Days()), disabled: !isDateValid(getPlus7Days()), children: quickActionLabels.plus7Days })] }))] }) }) }) }) })) : (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", children: jsxRuntime.jsx(react.Popover.Body, { p: 4, children: jsxRuntime.jsxs(react.Grid, { gap: 4, children: [jsxRuntime.jsx(react.InputGroup, { endElement: jsxRuntime.jsxs(react.Popover.Root, { open: calendarPopoverOpen, onOpenChange: (e) => setCalendarPopoverOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsx(react.Button, { variant: "ghost", size: "xs", "aria-label": "Open calendar", onClick: () => setCalendarPopoverOpen(true), children: jsxRuntime.jsx(md.MdDateRange, {}) }) }), jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "350px", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { p: 4, children: renderCalendar() }) }) })] }), children: jsxRuntime.jsx(react.Input, { value: dateInputValue, onChange: handleDateInputChange, onBlur: handleDateInputBlur, onKeyDown: handleDateInputKeyDown, placeholder: "YYYY-MM-DD" }) }), showQuickActions && (jsxRuntime.jsxs(react.Grid, { templateColumns: "repeat(4, 1fr)", gap: 2, children: [jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleQuickActionClick(getYesterday()), disabled: !isDateValid(getYesterday()), children: quickActionLabels.yesterday }), jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleQuickActionClick(getToday()), disabled: !isDateValid(getToday()), children: quickActionLabels.today }), jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleQuickActionClick(getTomorrow()), disabled: !isDateValid(getTomorrow()), children: quickActionLabels.tomorrow }), jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleQuickActionClick(getPlus7Days()), disabled: !isDateValid(getPlus7Days()), children: quickActionLabels.plus7Days })] }))] }) }) }) }))] }), jsxRuntime.jsxs(react.Popover.Root, { open: timePopoverOpen, onOpenChange: (e) => setTimePopoverOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsxs(react.Button, { size: "sm", variant: "outline", onClick: () => setTimePopoverOpen(true), justifyContent: "start", children: [jsxRuntime.jsx(bs.BsClock, {}), timeDisplayText] }) }), portalled ? (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "300px", children: jsxRuntime.jsx(react.Popover.Body, { p: 4, children: jsxRuntime.jsx(react.Grid, { gap: 2, children: jsxRuntime.jsxs(react.Combobox.Root, { value: currentTimeValue ? [currentTimeValue] : [], onValueChange: handleTimeValueChange, onInputValueChange: handleTimeInputChange, collection: collection, allowCustomValue: true, children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.InputGroup, { startElement: jsxRuntime.jsx(bs.BsClock, {}), children: jsxRuntime.jsx(react.Combobox.Input, { placeholder: timePickerLabels?.placeholder ??
8397
+ (is24Hour ? 'HH:mm' : 'hh:mm AM/PM'), onKeyDown: handleTimeInputKeyDown }) }), jsxRuntime.jsx(react.Combobox.IndicatorGroup, { children: jsxRuntime.jsx(react.Combobox.Trigger, {}) })] }), jsxRuntime.jsx(react.Portal, { disabled: true, children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [jsxRuntime.jsx(react.Combobox.Empty, { children: timePickerLabels?.emptyMessage ??
8398
+ 'No time found' }), collection.items.map((item) => {
8399
+ const option = item;
8400
+ return (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsxs(react.Flex, { justify: "space-between", align: "center", w: "100%", children: [jsxRuntime.jsx(react.Text, { children: option.label }), option.durationText && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "gray.500", children: option.durationText }))] }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, option.value));
8401
+ })] }) }) })] }) }) }) }) }) })) : (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "300px", children: jsxRuntime.jsx(react.Popover.Body, { p: 4, children: jsxRuntime.jsx(react.Grid, { gap: 2, children: jsxRuntime.jsxs(react.Combobox.Root, { value: currentTimeValue ? [currentTimeValue] : [], onValueChange: handleTimeValueChange, onInputValueChange: handleTimeInputChange, collection: collection, allowCustomValue: true, children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.InputGroup, { startElement: jsxRuntime.jsx(bs.BsClock, {}), children: jsxRuntime.jsx(react.Combobox.Input, { placeholder: timePickerLabels?.placeholder ??
8402
+ (is24Hour ? 'HH:mm' : 'hh:mm AM/PM'), onKeyDown: handleTimeInputKeyDown }) }), jsxRuntime.jsx(react.Combobox.IndicatorGroup, { children: jsxRuntime.jsx(react.Combobox.Trigger, {}) })] }), jsxRuntime.jsx(react.Portal, { disabled: true, children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [jsxRuntime.jsx(react.Combobox.Empty, { children: timePickerLabels?.emptyMessage ?? 'No time found' }), collection.items.map((item) => {
8403
+ const option = item;
8404
+ return (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsxs(react.Flex, { justify: "space-between", align: "center", w: "100%", children: [jsxRuntime.jsx(react.Text, { children: option.label }), option.durationText && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "gray.500", children: option.durationText }))] }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, option.value));
8405
+ })] }) }) })] }) }) }) }) }))] }), showTimezoneSelector && (jsxRuntime.jsxs(react.Popover.Root, { open: timezonePopoverOpen, onOpenChange: (e) => setTimezonePopoverOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => setTimezonePopoverOpen(true), justifyContent: "start", children: timezoneDisplayText || 'Select timezone' }) }), portalled ? (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "250px", children: jsxRuntime.jsx(react.Popover.Body, { p: 4, children: jsxRuntime.jsx(react.Grid, { gap: 2, children: jsxRuntime.jsxs(react.Select.Root, { size: "sm", collection: timezoneCollection, value: timezoneOffset ? [timezoneOffset] : [], onValueChange: (e) => {
8406
+ const newOffset = e.value[0];
8407
+ if (newOffset) {
8408
+ setTimezoneOffset(newOffset);
8409
+ // Update date-time with new offset
8410
+ if (selectedDate &&
8411
+ hour !== null &&
8412
+ minute !== null) {
8413
+ updateDateTime(selectedDate, hour, minute, second, meridiem);
8414
+ }
8415
+ // Close popover after selection
8416
+ setTimezonePopoverOpen(false);
8417
+ }
8418
+ }, children: [jsxRuntime.jsxs(react.Select.Control, { children: [jsxRuntime.jsx(react.Select.Trigger, {}), jsxRuntime.jsx(react.Select.IndicatorGroup, { children: jsxRuntime.jsx(react.Select.Indicator, {}) })] }), jsxRuntime.jsx(react.Select.Positioner, { children: jsxRuntime.jsx(react.Select.Content, { children: timezoneCollection.items.map((item) => (jsxRuntime.jsxs(react.Select.Item, { item: item, children: [jsxRuntime.jsx(react.Select.ItemText, { children: item.label }), jsxRuntime.jsx(react.Select.ItemIndicator, {})] }, item.value))) }) })] }) }) }) }) }) })) : (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "250px", children: jsxRuntime.jsx(react.Popover.Body, { p: 4, children: jsxRuntime.jsx(react.Grid, { gap: 2, children: jsxRuntime.jsxs(react.Select.Root, { size: "sm", collection: timezoneCollection, value: timezoneOffset ? [timezoneOffset] : [], onValueChange: (e) => {
8419
+ const newOffset = e.value[0];
8420
+ if (newOffset) {
8421
+ setTimezoneOffset(newOffset);
8422
+ // Update date-time with new offset (pass it directly to avoid stale state)
8423
+ if (selectedDate &&
8424
+ hour !== null &&
8425
+ minute !== null) {
8426
+ updateDateTime(selectedDate, hour, minute, second, meridiem, newOffset);
8427
+ }
8428
+ // Close popover after selection
8429
+ setTimezonePopoverOpen(false);
8430
+ }
8431
+ }, children: [jsxRuntime.jsxs(react.Select.Control, { children: [jsxRuntime.jsx(react.Select.Trigger, {}), jsxRuntime.jsx(react.Select.IndicatorGroup, { children: jsxRuntime.jsx(react.Select.Indicator, {}) })] }), jsxRuntime.jsx(react.Select.Positioner, { children: jsxRuntime.jsx(react.Select.Content, { children: timezoneCollection.items.map((item) => (jsxRuntime.jsxs(react.Select.Item, { item: item, children: [jsxRuntime.jsx(react.Select.ItemText, { children: item.label }), jsxRuntime.jsx(react.Select.ItemIndicator, {})] }, item.value))) }) })] }) }) }) }) }))] }))] }));
8148
8432
  }
8149
8433
 
8150
8434
  dayjs.extend(utc);
@@ -8158,11 +8442,12 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
8158
8442
  dateFormat = 'YYYY-MM-DD[T]HH:mm:ssZ', } = schema;
8159
8443
  const isRequired = required?.some((columnId) => columnId === column);
8160
8444
  const colLabel = formI18n.colLabel;
8161
- const [open, setOpen] = React.useState(false);
8445
+ React.useState(false);
8162
8446
  const selectedDate = watch(colLabel);
8163
- const displayDate = selectedDate && dayjs(selectedDate).tz(timezone).isValid()
8447
+ selectedDate && dayjs(selectedDate).tz(timezone).isValid()
8164
8448
  ? dayjs(selectedDate).tz(timezone).format(displayDateFormat)
8165
8449
  : '';
8450
+ // Set default date on mount if no value exists
8166
8451
  const dateTimePickerLabelsConfig = {
8167
8452
  monthNamesShort: dateTimePickerLabels?.monthNamesShort ?? [
8168
8453
  'January',
@@ -8204,9 +8489,7 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
8204
8489
  }
8205
8490
  }, timezone: timezone, labels: dateTimePickerLabelsConfig, timePickerLabels: timePickerLabels }));
8206
8491
  return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
8207
- gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsxs(react.Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
8208
- setOpen(true);
8209
- }, justifyContent: 'start', children: [jsxRuntime.jsx(md.MdDateRange, {}), displayDate || ''] }) }), insideDialog ? (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "350px", minH: "10rem", children: jsxRuntime.jsx(react.Popover.Body, { children: dateTimePickerContent }) }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "350px", minH: "10rem", children: jsxRuntime.jsx(react.Popover.Body, { children: dateTimePickerContent }) }) }) }))] }) }));
8492
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: dateTimePickerContent }));
8210
8493
  };
8211
8494
 
8212
8495
  const SchemaRenderer = ({ schema, prefix, column, }) => {