@bsol-oss/react-datatable5 13.0.1-beta.4 → 13.0.1-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +24 -18
- package/dist/index.js +608 -194
- package/dist/index.mjs +608 -194
- package/dist/types/components/DatePicker/DatePicker.d.ts +24 -0
- package/dist/types/components/DatePicker/DateTimePicker.d.ts +14 -1
- package/dist/types/components/DatePicker/index.d.ts +1 -1
- package/dist/types/index.d.ts +0 -1
- package/package.json +1 -1
- package/dist/types/components/DatePicker/DatePickerInput.d.ts +0 -18
package/dist/index.mjs
CHANGED
|
@@ -30,8 +30,8 @@ import addFormats from 'ajv-formats';
|
|
|
30
30
|
import dayjs from 'dayjs';
|
|
31
31
|
import utc from 'dayjs/plugin/utc';
|
|
32
32
|
import timezone from 'dayjs/plugin/timezone';
|
|
33
|
-
import { TiDeleteOutline } from 'react-icons/ti';
|
|
34
33
|
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
34
|
+
import { TiDeleteOutline } from 'react-icons/ti';
|
|
35
35
|
import { rankItem } from '@tanstack/match-sorter-utils';
|
|
36
36
|
|
|
37
37
|
const DataTableContext = createContext({
|
|
@@ -4525,6 +4525,9 @@ const CustomInput = ({ column, schema, prefix }) => {
|
|
|
4525
4525
|
}));
|
|
4526
4526
|
};
|
|
4527
4527
|
|
|
4528
|
+
dayjs.extend(utc);
|
|
4529
|
+
dayjs.extend(timezone);
|
|
4530
|
+
dayjs.extend(customParseFormat);
|
|
4528
4531
|
const Calendar = ({ calendars, getBackProps, getForwardProps, getDateProps, firstDayOfWeek = 0, }) => {
|
|
4529
4532
|
const { labels } = useContext(DatePickerContext);
|
|
4530
4533
|
const { monthNamesShort, weekdayNamesShort, backButtonLabel, forwardButtonLabel, } = labels;
|
|
@@ -4594,6 +4597,9 @@ const DatePickerContext = createContext({
|
|
|
4594
4597
|
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
4595
4598
|
backButtonLabel: 'Back',
|
|
4596
4599
|
forwardButtonLabel: 'Next',
|
|
4600
|
+
todayLabel: 'Today',
|
|
4601
|
+
yesterdayLabel: 'Yesterday',
|
|
4602
|
+
tomorrowLabel: 'Tomorrow',
|
|
4597
4603
|
},
|
|
4598
4604
|
});
|
|
4599
4605
|
const DatePicker$1 = ({ labels = {
|
|
@@ -4614,6 +4620,9 @@ const DatePicker$1 = ({ labels = {
|
|
|
4614
4620
|
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
4615
4621
|
backButtonLabel: 'Back',
|
|
4616
4622
|
forwardButtonLabel: 'Next',
|
|
4623
|
+
todayLabel: 'Today',
|
|
4624
|
+
yesterdayLabel: 'Yesterday',
|
|
4625
|
+
tomorrowLabel: 'Tomorrow',
|
|
4617
4626
|
}, onDateSelected, selected, firstDayOfWeek, showOutsideDays, date, minDate, maxDate, monthsToDisplay, render, }) => {
|
|
4618
4627
|
const calendarData = useCalendar({
|
|
4619
4628
|
onDateSelected,
|
|
@@ -4628,9 +4637,164 @@ const DatePicker$1 = ({ labels = {
|
|
|
4628
4637
|
return (jsx(DatePickerContext.Provider, { value: { labels }, children: render ? (render(calendarData)) : (jsx(Calendar, { ...calendarData,
|
|
4629
4638
|
firstDayOfWeek })) }));
|
|
4630
4639
|
};
|
|
4640
|
+
function DatePickerInput({ value, onChange, placeholder = 'Select a date', dateFormat = 'YYYY-MM-DD', displayFormat = 'YYYY-MM-DD', labels = {
|
|
4641
|
+
monthNamesShort: [
|
|
4642
|
+
'Jan',
|
|
4643
|
+
'Feb',
|
|
4644
|
+
'Mar',
|
|
4645
|
+
'Apr',
|
|
4646
|
+
'May',
|
|
4647
|
+
'Jun',
|
|
4648
|
+
'Jul',
|
|
4649
|
+
'Aug',
|
|
4650
|
+
'Sep',
|
|
4651
|
+
'Oct',
|
|
4652
|
+
'Nov',
|
|
4653
|
+
'Dec',
|
|
4654
|
+
],
|
|
4655
|
+
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
4656
|
+
backButtonLabel: 'Back',
|
|
4657
|
+
forwardButtonLabel: 'Next',
|
|
4658
|
+
todayLabel: 'Today',
|
|
4659
|
+
yesterdayLabel: 'Yesterday',
|
|
4660
|
+
tomorrowLabel: 'Tomorrow',
|
|
4661
|
+
}, timezone = 'Asia/Hong_Kong', minDate, maxDate, firstDayOfWeek, showOutsideDays, monthsToDisplay = 1, insideDialog = false, readOnly = false, showHelperButtons = true, }) {
|
|
4662
|
+
const [open, setOpen] = useState(false);
|
|
4663
|
+
const [inputValue, setInputValue] = useState('');
|
|
4664
|
+
// Update input value when prop value changes
|
|
4665
|
+
useEffect(() => {
|
|
4666
|
+
if (value) {
|
|
4667
|
+
const formatted = typeof value === 'string'
|
|
4668
|
+
? dayjs(value).tz(timezone).isValid()
|
|
4669
|
+
? dayjs(value).tz(timezone).format(displayFormat)
|
|
4670
|
+
: ''
|
|
4671
|
+
: dayjs(value).tz(timezone).format(displayFormat);
|
|
4672
|
+
setInputValue(formatted);
|
|
4673
|
+
}
|
|
4674
|
+
else {
|
|
4675
|
+
setInputValue('');
|
|
4676
|
+
}
|
|
4677
|
+
}, [value, displayFormat, timezone]);
|
|
4678
|
+
// Convert value to Date object for DatePicker
|
|
4679
|
+
const selectedDate = value
|
|
4680
|
+
? typeof value === 'string'
|
|
4681
|
+
? dayjs(value).tz(timezone).isValid()
|
|
4682
|
+
? dayjs(value).tz(timezone).toDate()
|
|
4683
|
+
: new Date()
|
|
4684
|
+
: value
|
|
4685
|
+
: new Date();
|
|
4686
|
+
// Shared function to parse and validate input value
|
|
4687
|
+
const parseAndValidateInput = (inputVal) => {
|
|
4688
|
+
// If empty, clear the value
|
|
4689
|
+
if (!inputVal.trim()) {
|
|
4690
|
+
onChange?.(undefined);
|
|
4691
|
+
setInputValue('');
|
|
4692
|
+
return;
|
|
4693
|
+
}
|
|
4694
|
+
// Try parsing with displayFormat first
|
|
4695
|
+
let parsedDate = dayjs(inputVal, displayFormat, true);
|
|
4696
|
+
// If that fails, try common date formats
|
|
4697
|
+
if (!parsedDate.isValid()) {
|
|
4698
|
+
parsedDate = dayjs(inputVal);
|
|
4699
|
+
}
|
|
4700
|
+
// If still invalid, try parsing with dateFormat
|
|
4701
|
+
if (!parsedDate.isValid()) {
|
|
4702
|
+
parsedDate = dayjs(inputVal, dateFormat, true);
|
|
4703
|
+
}
|
|
4704
|
+
// If valid, check constraints and update
|
|
4705
|
+
if (parsedDate.isValid()) {
|
|
4706
|
+
const dateObj = parsedDate.tz(timezone).toDate();
|
|
4707
|
+
// Check min/max constraints
|
|
4708
|
+
if (minDate && dateObj < minDate) {
|
|
4709
|
+
// Invalid: before minDate, reset to prop value
|
|
4710
|
+
resetToPropValue();
|
|
4711
|
+
return;
|
|
4712
|
+
}
|
|
4713
|
+
if (maxDate && dateObj > maxDate) {
|
|
4714
|
+
// Invalid: after maxDate, reset to prop value
|
|
4715
|
+
resetToPropValue();
|
|
4716
|
+
return;
|
|
4717
|
+
}
|
|
4718
|
+
// Valid date - format and update
|
|
4719
|
+
const formattedDate = parsedDate.tz(timezone).format(dateFormat);
|
|
4720
|
+
const formattedDisplay = parsedDate.tz(timezone).format(displayFormat);
|
|
4721
|
+
onChange?.(formattedDate);
|
|
4722
|
+
setInputValue(formattedDisplay);
|
|
4723
|
+
}
|
|
4724
|
+
else {
|
|
4725
|
+
// Invalid date - reset to prop value
|
|
4726
|
+
resetToPropValue();
|
|
4727
|
+
}
|
|
4728
|
+
};
|
|
4729
|
+
// Helper function to reset input to prop value
|
|
4730
|
+
const resetToPropValue = () => {
|
|
4731
|
+
if (value) {
|
|
4732
|
+
const formatted = typeof value === 'string'
|
|
4733
|
+
? dayjs(value).tz(timezone).isValid()
|
|
4734
|
+
? dayjs(value).tz(timezone).format(displayFormat)
|
|
4735
|
+
: ''
|
|
4736
|
+
: dayjs(value).tz(timezone).format(displayFormat);
|
|
4737
|
+
setInputValue(formatted);
|
|
4738
|
+
}
|
|
4739
|
+
else {
|
|
4740
|
+
setInputValue('');
|
|
4741
|
+
}
|
|
4742
|
+
};
|
|
4743
|
+
const handleInputChange = (e) => {
|
|
4744
|
+
// Only update the input value, don't parse yet
|
|
4745
|
+
setInputValue(e.target.value);
|
|
4746
|
+
};
|
|
4747
|
+
const handleInputBlur = () => {
|
|
4748
|
+
// Parse and validate when input loses focus
|
|
4749
|
+
parseAndValidateInput(inputValue);
|
|
4750
|
+
};
|
|
4751
|
+
const handleKeyDown = (e) => {
|
|
4752
|
+
// Parse and validate when Enter is pressed
|
|
4753
|
+
if (e.key === 'Enter') {
|
|
4754
|
+
e.preventDefault();
|
|
4755
|
+
parseAndValidateInput(inputValue);
|
|
4756
|
+
}
|
|
4757
|
+
};
|
|
4758
|
+
const handleDateSelected = ({ date }) => {
|
|
4759
|
+
const formattedDate = dayjs(date).tz(timezone).format(dateFormat);
|
|
4760
|
+
onChange?.(formattedDate);
|
|
4761
|
+
setOpen(false);
|
|
4762
|
+
};
|
|
4763
|
+
// Helper function to get dates in the correct timezone
|
|
4764
|
+
const getToday = () => dayjs().tz(timezone).startOf('day').toDate();
|
|
4765
|
+
const getYesterday = () => dayjs().tz(timezone).subtract(1, 'day').startOf('day').toDate();
|
|
4766
|
+
const getTomorrow = () => dayjs().tz(timezone).add(1, 'day').startOf('day').toDate();
|
|
4767
|
+
// Check if a date is within min/max constraints
|
|
4768
|
+
const isDateValid = (date) => {
|
|
4769
|
+
if (minDate) {
|
|
4770
|
+
const minDateStart = dayjs(minDate).tz(timezone).startOf('day').toDate();
|
|
4771
|
+
const dateStart = dayjs(date).tz(timezone).startOf('day').toDate();
|
|
4772
|
+
if (dateStart < minDateStart)
|
|
4773
|
+
return false;
|
|
4774
|
+
}
|
|
4775
|
+
if (maxDate) {
|
|
4776
|
+
const maxDateStart = dayjs(maxDate).tz(timezone).startOf('day').toDate();
|
|
4777
|
+
const dateStart = dayjs(date).tz(timezone).startOf('day').toDate();
|
|
4778
|
+
if (dateStart > maxDateStart)
|
|
4779
|
+
return false;
|
|
4780
|
+
}
|
|
4781
|
+
return true;
|
|
4782
|
+
};
|
|
4783
|
+
const handleHelperButtonClick = (date) => {
|
|
4784
|
+
if (isDateValid(date)) {
|
|
4785
|
+
handleDateSelected({ date });
|
|
4786
|
+
}
|
|
4787
|
+
};
|
|
4788
|
+
const today = getToday();
|
|
4789
|
+
const yesterday = getYesterday();
|
|
4790
|
+
const tomorrow = getTomorrow();
|
|
4791
|
+
const datePickerContent = (jsxs(Grid, { gap: 2, children: [showHelperButtons && (jsxs(Grid, { templateColumns: "repeat(3, 1fr)", gap: 2, children: [jsx(Button$1, { size: "sm", variant: "outline", onClick: () => handleHelperButtonClick(yesterday), disabled: !isDateValid(yesterday), children: labels.yesterdayLabel ?? 'Yesterday' }), jsx(Button$1, { size: "sm", variant: "outline", onClick: () => handleHelperButtonClick(today), disabled: !isDateValid(today), children: labels.todayLabel ?? 'Today' }), jsx(Button$1, { size: "sm", variant: "outline", onClick: () => handleHelperButtonClick(tomorrow), disabled: !isDateValid(tomorrow), children: labels.tomorrowLabel ?? 'Tomorrow' })] })), jsx(DatePicker$1, { selected: selectedDate, onDateSelected: handleDateSelected, labels: labels, minDate: minDate, maxDate: maxDate, firstDayOfWeek: firstDayOfWeek, showOutsideDays: showOutsideDays, monthsToDisplay: monthsToDisplay })] }));
|
|
4792
|
+
return (jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsx(InputGroup, { endElement: jsx(Popover.Trigger, { asChild: true, children: jsx(IconButton, { variant: "ghost", size: "2xs", "aria-label": "Open calendar", onClick: () => setOpen(true), children: jsx(Icon, { children: jsx(MdDateRange, {}) }) }) }), children: jsx(Input, { value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onKeyDown: handleKeyDown, placeholder: placeholder, readOnly: readOnly }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minH: "25rem", children: jsx(Popover.Body, { children: datePickerContent }) }) })) : (jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minH: "25rem", children: jsx(Popover.Body, { children: datePickerContent }) }) }) }))] }));
|
|
4793
|
+
}
|
|
4631
4794
|
|
|
4632
4795
|
dayjs.extend(utc);
|
|
4633
4796
|
dayjs.extend(timezone);
|
|
4797
|
+
dayjs.extend(customParseFormat);
|
|
4634
4798
|
const DatePicker = ({ column, schema, prefix }) => {
|
|
4635
4799
|
const { watch, formState: { errors }, setValue, } = useFormContext();
|
|
4636
4800
|
const { timezone, dateTimePickerLabels, insideDialog } = useSchemaContext();
|
|
@@ -4639,15 +4803,29 @@ const DatePicker = ({ column, schema, prefix }) => {
|
|
|
4639
4803
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4640
4804
|
const colLabel = formI18n.colLabel;
|
|
4641
4805
|
const [open, setOpen] = useState(false);
|
|
4806
|
+
const [inputValue, setInputValue] = useState('');
|
|
4642
4807
|
const selectedDate = watch(colLabel);
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4808
|
+
// Update input value when form value changes
|
|
4809
|
+
useEffect(() => {
|
|
4810
|
+
if (selectedDate) {
|
|
4811
|
+
const parsedDate = dayjs(selectedDate).tz(timezone);
|
|
4812
|
+
if (parsedDate.isValid()) {
|
|
4813
|
+
const formatted = parsedDate.format(displayDateFormat);
|
|
4814
|
+
setInputValue(formatted);
|
|
4815
|
+
}
|
|
4816
|
+
else {
|
|
4817
|
+
setInputValue('');
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
else {
|
|
4821
|
+
setInputValue('');
|
|
4822
|
+
}
|
|
4823
|
+
}, [selectedDate, displayDateFormat, timezone]);
|
|
4824
|
+
// Format and validate existing value
|
|
4646
4825
|
useEffect(() => {
|
|
4647
4826
|
try {
|
|
4648
4827
|
if (selectedDate) {
|
|
4649
4828
|
// Parse the selectedDate as UTC or in a specific timezone to avoid +8 hour shift
|
|
4650
|
-
// For example, parse as UTC:
|
|
4651
4829
|
const parsedDate = dayjs(selectedDate).tz(timezone);
|
|
4652
4830
|
if (!parsedDate.isValid())
|
|
4653
4831
|
return;
|
|
@@ -4665,7 +4843,7 @@ const DatePicker = ({ column, schema, prefix }) => {
|
|
|
4665
4843
|
catch (e) {
|
|
4666
4844
|
console.error(e);
|
|
4667
4845
|
}
|
|
4668
|
-
}, [selectedDate, dateFormat, colLabel, setValue]);
|
|
4846
|
+
}, [selectedDate, dateFormat, colLabel, setValue, timezone]);
|
|
4669
4847
|
const datePickerLabels = {
|
|
4670
4848
|
monthNamesShort: dateTimePickerLabels?.monthNamesShort ?? [
|
|
4671
4849
|
'January',
|
|
@@ -4693,14 +4871,92 @@ const DatePicker = ({ column, schema, prefix }) => {
|
|
|
4693
4871
|
backButtonLabel: dateTimePickerLabels?.backButtonLabel ?? 'Back',
|
|
4694
4872
|
forwardButtonLabel: dateTimePickerLabels?.forwardButtonLabel ?? 'Forward',
|
|
4695
4873
|
};
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4874
|
+
// Convert value to Date object for DatePicker
|
|
4875
|
+
const selectedDateObj = selectedDate
|
|
4876
|
+
? dayjs(selectedDate).tz(timezone).isValid()
|
|
4877
|
+
? dayjs(selectedDate).tz(timezone).toDate()
|
|
4878
|
+
: new Date()
|
|
4879
|
+
: new Date();
|
|
4880
|
+
// Shared function to parse and validate input value
|
|
4881
|
+
const parseAndValidateInput = (inputVal) => {
|
|
4882
|
+
// If empty, clear the value
|
|
4883
|
+
if (!inputVal.trim()) {
|
|
4884
|
+
setValue(colLabel, undefined, {
|
|
4885
|
+
shouldValidate: true,
|
|
4886
|
+
shouldDirty: true,
|
|
4887
|
+
});
|
|
4888
|
+
setInputValue('');
|
|
4889
|
+
return;
|
|
4890
|
+
}
|
|
4891
|
+
// Try parsing with displayDateFormat first
|
|
4892
|
+
let parsedDate = dayjs(inputVal, displayDateFormat, true);
|
|
4893
|
+
// If that fails, try common date formats
|
|
4894
|
+
if (!parsedDate.isValid()) {
|
|
4895
|
+
parsedDate = dayjs(inputVal);
|
|
4896
|
+
}
|
|
4897
|
+
// If still invalid, try parsing with dateFormat
|
|
4898
|
+
if (!parsedDate.isValid()) {
|
|
4899
|
+
parsedDate = dayjs(inputVal, dateFormat, true);
|
|
4900
|
+
}
|
|
4901
|
+
// If valid, format and update
|
|
4902
|
+
if (parsedDate.isValid()) {
|
|
4903
|
+
const formattedDate = parsedDate.tz(timezone).format(dateFormat);
|
|
4904
|
+
const formattedDisplay = parsedDate
|
|
4905
|
+
.tz(timezone)
|
|
4906
|
+
.format(displayDateFormat);
|
|
4907
|
+
setValue(colLabel, formattedDate, {
|
|
4908
|
+
shouldValidate: true,
|
|
4909
|
+
shouldDirty: true,
|
|
4910
|
+
});
|
|
4911
|
+
setInputValue(formattedDisplay);
|
|
4912
|
+
}
|
|
4913
|
+
else {
|
|
4914
|
+
// Invalid date - reset to prop value
|
|
4915
|
+
resetToPropValue();
|
|
4916
|
+
}
|
|
4917
|
+
};
|
|
4918
|
+
// Helper function to reset input to prop value
|
|
4919
|
+
const resetToPropValue = () => {
|
|
4920
|
+
if (selectedDate) {
|
|
4921
|
+
const parsedDate = dayjs(selectedDate).tz(timezone);
|
|
4922
|
+
if (parsedDate.isValid()) {
|
|
4923
|
+
const formatted = parsedDate.format(displayDateFormat);
|
|
4924
|
+
setInputValue(formatted);
|
|
4925
|
+
}
|
|
4926
|
+
else {
|
|
4927
|
+
setInputValue('');
|
|
4928
|
+
}
|
|
4929
|
+
}
|
|
4930
|
+
else {
|
|
4931
|
+
setInputValue('');
|
|
4932
|
+
}
|
|
4933
|
+
};
|
|
4934
|
+
const handleInputChange = (e) => {
|
|
4935
|
+
// Only update the input value, don't parse yet
|
|
4936
|
+
setInputValue(e.target.value);
|
|
4937
|
+
};
|
|
4938
|
+
const handleInputBlur = () => {
|
|
4939
|
+
// Parse and validate when input loses focus
|
|
4940
|
+
parseAndValidateInput(inputValue);
|
|
4941
|
+
};
|
|
4942
|
+
const handleKeyDown = (e) => {
|
|
4943
|
+
// Parse and validate when Enter is pressed
|
|
4944
|
+
if (e.key === 'Enter') {
|
|
4945
|
+
e.preventDefault();
|
|
4946
|
+
parseAndValidateInput(inputValue);
|
|
4947
|
+
}
|
|
4948
|
+
};
|
|
4949
|
+
const handleDateSelected = ({ date }) => {
|
|
4950
|
+
const formattedDate = dayjs(date).tz(timezone).format(dateFormat);
|
|
4951
|
+
setValue(colLabel, formattedDate, {
|
|
4952
|
+
shouldValidate: true,
|
|
4953
|
+
shouldDirty: true,
|
|
4954
|
+
});
|
|
4955
|
+
setOpen(false);
|
|
4956
|
+
};
|
|
4957
|
+
const datePickerContent = (jsx(DatePicker$1, { selected: selectedDateObj, onDateSelected: handleDateSelected, labels: datePickerLabels }));
|
|
4700
4958
|
return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4701
|
-
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsx(Popover.Trigger, { asChild: true, children:
|
|
4702
|
-
setOpen(true);
|
|
4703
|
-
}, justifyContent: 'start', children: [jsx(MdDateRange, {}), selectedDate !== undefined ? `${displayDate}` : ''] }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minH: "25rem", children: jsx(Popover.Body, { children: datePickerContent }) }) })) : (jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minH: "25rem", children: jsx(Popover.Body, { children: datePickerContent }) }) }) }))] }) }));
|
|
4959
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsx(InputGroup, { endElement: jsx(Popover.Trigger, { asChild: true, children: jsx(IconButton, { variant: "ghost", size: "2xs", "aria-label": "Open calendar", onClick: () => setOpen(true), children: jsx(Icon, { children: jsx(MdDateRange, {}) }) }) }), children: jsx(Input, { value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onKeyDown: handleKeyDown, placeholder: formI18n.label(), size: "sm" }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minH: "25rem", children: jsx(Popover.Body, { children: datePickerContent }) }) })) : (jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minH: "25rem", children: jsx(Popover.Body, { children: datePickerContent }) }) }) }))] }) }));
|
|
4704
4960
|
};
|
|
4705
4961
|
|
|
4706
4962
|
dayjs.extend(utc);
|
|
@@ -6432,14 +6688,74 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
|
|
|
6432
6688
|
}
|
|
6433
6689
|
}
|
|
6434
6690
|
}
|
|
6435
|
-
|
|
6691
|
+
// Sort options by time (convert to 24-hour for proper chronological sorting)
|
|
6692
|
+
return options.sort((a, b) => {
|
|
6693
|
+
// Convert 12-hour to 24-hour for comparison
|
|
6694
|
+
let hour24A = a.hour;
|
|
6695
|
+
if (a.meridiem === 'am' && a.hour === 12)
|
|
6696
|
+
hour24A = 0;
|
|
6697
|
+
else if (a.meridiem === 'pm' && a.hour < 12)
|
|
6698
|
+
hour24A = a.hour + 12;
|
|
6699
|
+
let hour24B = b.hour;
|
|
6700
|
+
if (b.meridiem === 'am' && b.hour === 12)
|
|
6701
|
+
hour24B = 0;
|
|
6702
|
+
else if (b.meridiem === 'pm' && b.hour < 12)
|
|
6703
|
+
hour24B = b.hour + 12;
|
|
6704
|
+
// Compare by hour first, then minute
|
|
6705
|
+
if (hour24A !== hour24B) {
|
|
6706
|
+
return hour24A - hour24B;
|
|
6707
|
+
}
|
|
6708
|
+
return a.minute - b.minute;
|
|
6709
|
+
});
|
|
6436
6710
|
}, [startTime, selectedDate, timezone]);
|
|
6437
|
-
|
|
6711
|
+
// itemToString returns only the clean display text (no metadata)
|
|
6712
|
+
const itemToString = useMemo(() => {
|
|
6713
|
+
return (item) => {
|
|
6714
|
+
return item.searchText; // Clean display text only
|
|
6715
|
+
};
|
|
6716
|
+
}, []);
|
|
6717
|
+
// Custom filter function that filters by time and supports 24-hour format input
|
|
6718
|
+
const customTimeFilter = useMemo(() => {
|
|
6719
|
+
return (itemText, filterText) => {
|
|
6720
|
+
if (!filterText) {
|
|
6721
|
+
return true; // Show all items when no filter
|
|
6722
|
+
}
|
|
6723
|
+
const lowerItemText = itemText.toLowerCase();
|
|
6724
|
+
const lowerFilterText = filterText.toLowerCase();
|
|
6725
|
+
// First, try matching against the display text (12-hour format)
|
|
6726
|
+
if (lowerItemText.includes(lowerFilterText)) {
|
|
6727
|
+
return true;
|
|
6728
|
+
}
|
|
6729
|
+
// Find the corresponding item to check 24-hour format matches
|
|
6730
|
+
const item = timeOptions.find((opt) => opt.searchText.toLowerCase() === lowerItemText);
|
|
6731
|
+
if (!item) {
|
|
6732
|
+
return false;
|
|
6733
|
+
}
|
|
6734
|
+
// Convert item to 24-hour format for matching
|
|
6735
|
+
let hour24 = item.hour;
|
|
6736
|
+
if (item.meridiem === 'am' && item.hour === 12)
|
|
6737
|
+
hour24 = 0;
|
|
6738
|
+
else if (item.meridiem === 'pm' && item.hour < 12)
|
|
6739
|
+
hour24 = item.hour + 12;
|
|
6740
|
+
const hour24Str = hour24.toString().padStart(2, '0');
|
|
6741
|
+
const minuteStr = item.minute.toString().padStart(2, '0');
|
|
6742
|
+
// Check if filterText matches 24-hour format variations
|
|
6743
|
+
const formats = [
|
|
6744
|
+
`${hour24Str}:${minuteStr}`, // "13:30"
|
|
6745
|
+
`${hour24Str}${minuteStr}`, // "1330"
|
|
6746
|
+
hour24Str, // "13"
|
|
6747
|
+
`${hour24}:${minuteStr}`, // "13:30" (without padding)
|
|
6748
|
+
hour24.toString(), // "13" (without padding)
|
|
6749
|
+
];
|
|
6750
|
+
return formats.some((format) => format.toLowerCase().includes(lowerFilterText) ||
|
|
6751
|
+
lowerFilterText.includes(format.toLowerCase()));
|
|
6752
|
+
};
|
|
6753
|
+
}, [timeOptions]);
|
|
6438
6754
|
const { collection, filter } = useListCollection({
|
|
6439
6755
|
initialItems: timeOptions,
|
|
6440
|
-
itemToString:
|
|
6756
|
+
itemToString: itemToString,
|
|
6441
6757
|
itemToValue: (item) => item.value,
|
|
6442
|
-
filter:
|
|
6758
|
+
filter: customTimeFilter,
|
|
6443
6759
|
});
|
|
6444
6760
|
// Get current value string for combobox
|
|
6445
6761
|
const currentValue = useMemo(() => {
|
|
@@ -6529,6 +6845,47 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
|
|
|
6529
6845
|
if (!trimmedValue) {
|
|
6530
6846
|
return;
|
|
6531
6847
|
}
|
|
6848
|
+
// Parse 24-hour format first (e.g., "13:30", "14:00", "1330", "1400", "9:05", "905")
|
|
6849
|
+
const timePattern24Hour = /^(\d{1,2}):?(\d{2})$/;
|
|
6850
|
+
const match24Hour = trimmedValue.match(timePattern24Hour);
|
|
6851
|
+
if (match24Hour) {
|
|
6852
|
+
const parsedHour24 = parseInt(match24Hour[1], 10);
|
|
6853
|
+
const parsedMinute = parseInt(match24Hour[2], 10);
|
|
6854
|
+
// Validate 24-hour format ranges
|
|
6855
|
+
if (parsedHour24 >= 0 &&
|
|
6856
|
+
parsedHour24 <= 23 &&
|
|
6857
|
+
parsedMinute >= 0 &&
|
|
6858
|
+
parsedMinute <= 59) {
|
|
6859
|
+
// Convert 24-hour to 12-hour format
|
|
6860
|
+
let hour12;
|
|
6861
|
+
let meridiem;
|
|
6862
|
+
if (parsedHour24 === 0) {
|
|
6863
|
+
hour12 = 12;
|
|
6864
|
+
meridiem = 'am';
|
|
6865
|
+
}
|
|
6866
|
+
else if (parsedHour24 === 12) {
|
|
6867
|
+
hour12 = 12;
|
|
6868
|
+
meridiem = 'pm';
|
|
6869
|
+
}
|
|
6870
|
+
else if (parsedHour24 > 12) {
|
|
6871
|
+
hour12 = parsedHour24 - 12;
|
|
6872
|
+
meridiem = 'pm';
|
|
6873
|
+
}
|
|
6874
|
+
else {
|
|
6875
|
+
hour12 = parsedHour24;
|
|
6876
|
+
meridiem = 'am';
|
|
6877
|
+
}
|
|
6878
|
+
setHour(hour12);
|
|
6879
|
+
setMinute(parsedMinute);
|
|
6880
|
+
setMeridiem(meridiem);
|
|
6881
|
+
onChange({
|
|
6882
|
+
hour: hour12,
|
|
6883
|
+
minute: parsedMinute,
|
|
6884
|
+
meridiem: meridiem,
|
|
6885
|
+
});
|
|
6886
|
+
return;
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6532
6889
|
// Parse formats like "1:30 PM", "1:30PM", "1:30 pm", "1:30pm"
|
|
6533
6890
|
const timePattern12Hour = /^(\d{1,2}):(\d{1,2})\s*(am|pm|AM|PM)$/i;
|
|
6534
6891
|
const match12Hour = trimmedValue.match(timePattern12Hour);
|
|
@@ -6694,133 +7051,6 @@ const TimePicker = ({ column, schema, prefix }) => {
|
|
|
6694
7051
|
}, justifyContent: 'start', children: [jsx(IoMdClock, {}), !!value ? `${displayedTime}` : ''] }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { maxH: "70vh", overflowY: "auto", children: jsx(Popover.Body, { overflow: "visible", children: jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, labels: timePickerLabels }) }) }) })) : (jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { children: jsx(Popover.Body, { children: jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, labels: timePickerLabels }) }) }) }) }))] }) }));
|
|
6695
7052
|
};
|
|
6696
7053
|
|
|
6697
|
-
dayjs.extend(utc);
|
|
6698
|
-
dayjs.extend(timezone);
|
|
6699
|
-
dayjs.extend(customParseFormat);
|
|
6700
|
-
function DatePickerInput({ value, onChange, placeholder = 'Select a date', dateFormat = 'YYYY-MM-DD', displayFormat = 'YYYY-MM-DD', labels = {
|
|
6701
|
-
monthNamesShort: [
|
|
6702
|
-
'Jan',
|
|
6703
|
-
'Feb',
|
|
6704
|
-
'Mar',
|
|
6705
|
-
'Apr',
|
|
6706
|
-
'May',
|
|
6707
|
-
'Jun',
|
|
6708
|
-
'Jul',
|
|
6709
|
-
'Aug',
|
|
6710
|
-
'Sep',
|
|
6711
|
-
'Oct',
|
|
6712
|
-
'Nov',
|
|
6713
|
-
'Dec',
|
|
6714
|
-
],
|
|
6715
|
-
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
6716
|
-
backButtonLabel: 'Back',
|
|
6717
|
-
forwardButtonLabel: 'Next',
|
|
6718
|
-
}, timezone = 'Asia/Hong_Kong', minDate, maxDate, firstDayOfWeek, showOutsideDays, monthsToDisplay = 1, insideDialog = false, readOnly = false, }) {
|
|
6719
|
-
const [open, setOpen] = useState(false);
|
|
6720
|
-
const [inputValue, setInputValue] = useState('');
|
|
6721
|
-
// Update input value when prop value changes
|
|
6722
|
-
useEffect(() => {
|
|
6723
|
-
if (value) {
|
|
6724
|
-
const formatted = typeof value === 'string'
|
|
6725
|
-
? dayjs(value).tz(timezone).isValid()
|
|
6726
|
-
? dayjs(value).tz(timezone).format(displayFormat)
|
|
6727
|
-
: ''
|
|
6728
|
-
: dayjs(value).tz(timezone).format(displayFormat);
|
|
6729
|
-
setInputValue(formatted);
|
|
6730
|
-
}
|
|
6731
|
-
else {
|
|
6732
|
-
setInputValue('');
|
|
6733
|
-
}
|
|
6734
|
-
}, [value, displayFormat, timezone]);
|
|
6735
|
-
// Convert value to Date object for DatePicker
|
|
6736
|
-
const selectedDate = value
|
|
6737
|
-
? typeof value === 'string'
|
|
6738
|
-
? dayjs(value).tz(timezone).isValid()
|
|
6739
|
-
? dayjs(value).tz(timezone).toDate()
|
|
6740
|
-
: new Date()
|
|
6741
|
-
: value
|
|
6742
|
-
: new Date();
|
|
6743
|
-
// Shared function to parse and validate input value
|
|
6744
|
-
const parseAndValidateInput = (inputVal) => {
|
|
6745
|
-
// If empty, clear the value
|
|
6746
|
-
if (!inputVal.trim()) {
|
|
6747
|
-
onChange?.(undefined);
|
|
6748
|
-
setInputValue('');
|
|
6749
|
-
return;
|
|
6750
|
-
}
|
|
6751
|
-
// Try parsing with displayFormat first
|
|
6752
|
-
let parsedDate = dayjs(inputVal, displayFormat, true);
|
|
6753
|
-
// If that fails, try common date formats
|
|
6754
|
-
if (!parsedDate.isValid()) {
|
|
6755
|
-
parsedDate = dayjs(inputVal);
|
|
6756
|
-
}
|
|
6757
|
-
// If still invalid, try parsing with dateFormat
|
|
6758
|
-
if (!parsedDate.isValid()) {
|
|
6759
|
-
parsedDate = dayjs(inputVal, dateFormat, true);
|
|
6760
|
-
}
|
|
6761
|
-
// If valid, check constraints and update
|
|
6762
|
-
if (parsedDate.isValid()) {
|
|
6763
|
-
const dateObj = parsedDate.tz(timezone).toDate();
|
|
6764
|
-
// Check min/max constraints
|
|
6765
|
-
if (minDate && dateObj < minDate) {
|
|
6766
|
-
// Invalid: before minDate, reset to prop value
|
|
6767
|
-
resetToPropValue();
|
|
6768
|
-
return;
|
|
6769
|
-
}
|
|
6770
|
-
if (maxDate && dateObj > maxDate) {
|
|
6771
|
-
// Invalid: after maxDate, reset to prop value
|
|
6772
|
-
resetToPropValue();
|
|
6773
|
-
return;
|
|
6774
|
-
}
|
|
6775
|
-
// Valid date - format and update
|
|
6776
|
-
const formattedDate = parsedDate.tz(timezone).format(dateFormat);
|
|
6777
|
-
const formattedDisplay = parsedDate.tz(timezone).format(displayFormat);
|
|
6778
|
-
onChange?.(formattedDate);
|
|
6779
|
-
setInputValue(formattedDisplay);
|
|
6780
|
-
}
|
|
6781
|
-
else {
|
|
6782
|
-
// Invalid date - reset to prop value
|
|
6783
|
-
resetToPropValue();
|
|
6784
|
-
}
|
|
6785
|
-
};
|
|
6786
|
-
// Helper function to reset input to prop value
|
|
6787
|
-
const resetToPropValue = () => {
|
|
6788
|
-
if (value) {
|
|
6789
|
-
const formatted = typeof value === 'string'
|
|
6790
|
-
? dayjs(value).tz(timezone).isValid()
|
|
6791
|
-
? dayjs(value).tz(timezone).format(displayFormat)
|
|
6792
|
-
: ''
|
|
6793
|
-
: dayjs(value).tz(timezone).format(displayFormat);
|
|
6794
|
-
setInputValue(formatted);
|
|
6795
|
-
}
|
|
6796
|
-
else {
|
|
6797
|
-
setInputValue('');
|
|
6798
|
-
}
|
|
6799
|
-
};
|
|
6800
|
-
const handleInputChange = (e) => {
|
|
6801
|
-
// Only update the input value, don't parse yet
|
|
6802
|
-
setInputValue(e.target.value);
|
|
6803
|
-
};
|
|
6804
|
-
const handleInputBlur = () => {
|
|
6805
|
-
// Parse and validate when input loses focus
|
|
6806
|
-
parseAndValidateInput(inputValue);
|
|
6807
|
-
};
|
|
6808
|
-
const handleKeyDown = (e) => {
|
|
6809
|
-
// Parse and validate when Enter is pressed
|
|
6810
|
-
if (e.key === 'Enter') {
|
|
6811
|
-
e.preventDefault();
|
|
6812
|
-
parseAndValidateInput(inputValue);
|
|
6813
|
-
}
|
|
6814
|
-
};
|
|
6815
|
-
const handleDateSelected = ({ date }) => {
|
|
6816
|
-
const formattedDate = dayjs(date).tz(timezone).format(dateFormat);
|
|
6817
|
-
onChange?.(formattedDate);
|
|
6818
|
-
setOpen(false);
|
|
6819
|
-
};
|
|
6820
|
-
const datePickerContent = (jsx(DatePicker$1, { selected: selectedDate, onDateSelected: handleDateSelected, labels: labels, minDate: minDate, maxDate: maxDate, firstDayOfWeek: firstDayOfWeek, showOutsideDays: showOutsideDays, monthsToDisplay: monthsToDisplay }));
|
|
6821
|
-
return (jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsx(InputGroup, { endElement: jsx(Popover.Trigger, { asChild: true, children: jsx(IconButton, { variant: "ghost", size: "2xs", "aria-label": "Open calendar", onClick: () => setOpen(true), children: jsx(Icon, { children: jsx(MdDateRange, {}) }) }) }), children: jsx(Input, { value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onKeyDown: handleKeyDown, placeholder: placeholder, readOnly: readOnly }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minH: "25rem", children: jsx(Popover.Body, { children: datePickerContent }) }) })) : (jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minH: "25rem", children: jsx(Popover.Body, { children: datePickerContent }) }) }) }))] }));
|
|
6822
|
-
}
|
|
6823
|
-
|
|
6824
7054
|
dayjs.extend(utc);
|
|
6825
7055
|
dayjs.extend(timezone);
|
|
6826
7056
|
function IsoTimePicker({ hour, setHour, minute, setMinute, second, setSecond,
|
|
@@ -7064,7 +7294,20 @@ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Ko
|
|
|
7064
7294
|
});
|
|
7065
7295
|
}
|
|
7066
7296
|
};
|
|
7297
|
+
const [inputValue, setInputValue] = useState('');
|
|
7298
|
+
// Sync inputValue with currentValue when time changes externally
|
|
7299
|
+
useEffect(() => {
|
|
7300
|
+
if (hour !== null && minute !== null && second !== null) {
|
|
7301
|
+
const formattedValue = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second.toString().padStart(2, '0')}`;
|
|
7302
|
+
setInputValue(formattedValue);
|
|
7303
|
+
}
|
|
7304
|
+
else {
|
|
7305
|
+
setInputValue('');
|
|
7306
|
+
}
|
|
7307
|
+
}, [hour, minute, second]);
|
|
7067
7308
|
const handleInputValueChange = (details) => {
|
|
7309
|
+
// Update local input value state
|
|
7310
|
+
setInputValue(details.inputValue);
|
|
7068
7311
|
// Filter the collection based on input, but don't parse yet
|
|
7069
7312
|
filter(details.inputValue);
|
|
7070
7313
|
};
|
|
@@ -7074,24 +7317,26 @@ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Ko
|
|
|
7074
7317
|
};
|
|
7075
7318
|
const handleBlur = (e) => {
|
|
7076
7319
|
// Parse and commit the input value when losing focus
|
|
7077
|
-
const
|
|
7078
|
-
|
|
7079
|
-
|
|
7320
|
+
const inputVal = e.target.value;
|
|
7321
|
+
setInputValue(inputVal);
|
|
7322
|
+
if (inputVal) {
|
|
7323
|
+
parseAndCommitInput(inputVal);
|
|
7080
7324
|
}
|
|
7081
7325
|
};
|
|
7082
7326
|
const handleKeyDown = (e) => {
|
|
7083
7327
|
// Commit input on Enter key
|
|
7084
7328
|
if (e.key === 'Enter') {
|
|
7085
7329
|
e.preventDefault();
|
|
7086
|
-
const
|
|
7087
|
-
|
|
7088
|
-
|
|
7330
|
+
const inputVal = e.currentTarget.value;
|
|
7331
|
+
setInputValue(inputVal);
|
|
7332
|
+
if (inputVal) {
|
|
7333
|
+
parseAndCommitInput(inputVal);
|
|
7089
7334
|
}
|
|
7090
7335
|
// Blur the input
|
|
7091
7336
|
e.currentTarget?.blur();
|
|
7092
7337
|
}
|
|
7093
7338
|
};
|
|
7094
|
-
return (jsx(Flex, { direction: "column", gap: 3, children: jsxs(Flex, { alignItems: "center", gap: "2", width: "auto", minWidth: "300px", children: [jsxs(Combobox.Root, { collection: collection, value: currentValue ? [currentValue] : [], onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, allowCustomValue: true, selectionBehavior: "replace", openOnClick: true, flex: 1, children: [jsxs(Combobox.Control, { children: [jsx(InputGroup$1, { startElement: jsx(BsClock, {}), children: jsx(Combobox.Input, { placeholder: labels.placeholder, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown }) }), jsx(Combobox.IndicatorGroup, { children: jsx(Combobox.Trigger, {}) })] }), jsx(Portal, { disabled: !portalled, children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [jsx(Combobox.Empty, { children: labels.emptyMessage }), collection.items.map((item) => (jsxs(Combobox.Item, { item: item, children: [jsxs(Flex, { alignItems: "center", gap: 2, width: "100%", children: [jsx(Text, { flex: 1, children: item.label }), item.durationText && (jsx(Tag$1.Root, { size: "sm", children: jsx(Tag$1.Label, { children: item.durationText }) }))] }), jsx(Combobox.ItemIndicator, {})] }, item.value)))] }) }) })] }), durationDiff && (jsx(Tag$1.Root, { size: "sm", children: jsx(Tag$1.Label, { children: durationDiff }) })), jsx(Button$1, { onClick: handleClear, size: "sm", variant: "ghost", children: jsx(Icon, { children: jsx(MdCancel, {}) }) })] }) }));
|
|
7339
|
+
return (jsx(Flex, { direction: "column", gap: 3, children: jsxs(Flex, { alignItems: "center", gap: "2", width: "auto", minWidth: "300px", children: [jsxs(Combobox.Root, { collection: collection, value: currentValue ? [currentValue] : [], onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, allowCustomValue: true, selectionBehavior: "replace", openOnClick: true, flex: 1, children: [jsxs(Combobox.Control, { children: [jsx(InputGroup$1, { startElement: jsx(BsClock, {}), children: jsx(Combobox.Input, { value: inputValue, placeholder: labels.placeholder, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown }) }), jsx(Combobox.IndicatorGroup, { children: jsx(Combobox.Trigger, {}) })] }), jsx(Portal, { disabled: !portalled, children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [jsx(Combobox.Empty, { children: labels.emptyMessage }), collection.items.map((item) => (jsxs(Combobox.Item, { item: item, children: [jsxs(Flex, { alignItems: "center", gap: 2, width: "100%", children: [jsx(Text, { flex: 1, children: item.label }), item.durationText && (jsx(Tag$1.Root, { size: "sm", children: jsx(Tag$1.Label, { children: item.durationText }) }))] }), jsx(Combobox.ItemIndicator, {})] }, item.value)))] }) }) })] }), durationDiff && (jsx(Tag$1.Root, { size: "sm", children: jsx(Tag$1.Label, { children: durationDiff }) })), jsx(Button$1, { onClick: handleClear, size: "sm", variant: "ghost", children: jsx(Icon, { children: jsx(MdCancel, {}) }) })] }) }));
|
|
7095
7340
|
}
|
|
7096
7341
|
|
|
7097
7342
|
dayjs.extend(utc);
|
|
@@ -7114,7 +7359,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7114
7359
|
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
7115
7360
|
backButtonLabel: 'Back',
|
|
7116
7361
|
forwardButtonLabel: 'Next',
|
|
7117
|
-
}, timePickerLabels, timezone = 'Asia/Hong_Kong', startTime, minDate, maxDate, portalled = false, }) {
|
|
7362
|
+
}, timePickerLabels, timezone = 'Asia/Hong_Kong', startTime, minDate, maxDate, portalled = false, defaultDate, defaultTime, }) {
|
|
7118
7363
|
console.log('[DateTimePicker] Component initialized with props:', {
|
|
7119
7364
|
value,
|
|
7120
7365
|
format,
|
|
@@ -7189,13 +7434,77 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7189
7434
|
value,
|
|
7190
7435
|
initialTime,
|
|
7191
7436
|
});
|
|
7437
|
+
// Normalize startTime to ignore milliseconds (needed for effectiveDefaultDate calculation)
|
|
7438
|
+
const normalizedStartTime = startTime
|
|
7439
|
+
? dayjs(startTime).tz(timezone).millisecond(0).toISOString()
|
|
7440
|
+
: undefined;
|
|
7441
|
+
// Calculate effective defaultDate: use prop if provided, otherwise use startTime date, otherwise use today
|
|
7442
|
+
const effectiveDefaultDate = useMemo(() => {
|
|
7443
|
+
if (defaultDate) {
|
|
7444
|
+
return defaultDate;
|
|
7445
|
+
}
|
|
7446
|
+
if (normalizedStartTime &&
|
|
7447
|
+
dayjs(normalizedStartTime).tz(timezone).isValid()) {
|
|
7448
|
+
return dayjs(normalizedStartTime).tz(timezone).format('YYYY-MM-DD');
|
|
7449
|
+
}
|
|
7450
|
+
return dayjs().tz(timezone).format('YYYY-MM-DD');
|
|
7451
|
+
}, [defaultDate, normalizedStartTime, timezone]);
|
|
7452
|
+
// Initialize time with default values if no value is provided
|
|
7453
|
+
const getInitialTimeValues = () => {
|
|
7454
|
+
if (value && initialTime.hour12 !== null) {
|
|
7455
|
+
return initialTime;
|
|
7456
|
+
}
|
|
7457
|
+
// If no value or no time in value, use defaultTime or 00:00
|
|
7458
|
+
if (defaultTime) {
|
|
7459
|
+
if (format === 'iso-date-time') {
|
|
7460
|
+
const defaultTime24 = defaultTime;
|
|
7461
|
+
return {
|
|
7462
|
+
hour12: null,
|
|
7463
|
+
minute: defaultTime24.minute ?? 0,
|
|
7464
|
+
meridiem: null,
|
|
7465
|
+
hour24: defaultTime24.hour ?? 0,
|
|
7466
|
+
second: showSeconds ? defaultTime24.second ?? 0 : null,
|
|
7467
|
+
};
|
|
7468
|
+
}
|
|
7469
|
+
else {
|
|
7470
|
+
const defaultTime12 = defaultTime;
|
|
7471
|
+
return {
|
|
7472
|
+
hour12: defaultTime12.hour ?? 12,
|
|
7473
|
+
minute: defaultTime12.minute ?? 0,
|
|
7474
|
+
meridiem: defaultTime12.meridiem ?? 'am',
|
|
7475
|
+
hour24: null,
|
|
7476
|
+
second: null,
|
|
7477
|
+
};
|
|
7478
|
+
}
|
|
7479
|
+
}
|
|
7480
|
+
// Default to 00:00
|
|
7481
|
+
if (format === 'iso-date-time') {
|
|
7482
|
+
return {
|
|
7483
|
+
hour12: null,
|
|
7484
|
+
minute: 0,
|
|
7485
|
+
meridiem: null,
|
|
7486
|
+
hour24: 0,
|
|
7487
|
+
second: showSeconds ? 0 : null,
|
|
7488
|
+
};
|
|
7489
|
+
}
|
|
7490
|
+
else {
|
|
7491
|
+
return {
|
|
7492
|
+
hour12: 12,
|
|
7493
|
+
minute: 0,
|
|
7494
|
+
meridiem: 'am',
|
|
7495
|
+
hour24: null,
|
|
7496
|
+
second: null,
|
|
7497
|
+
};
|
|
7498
|
+
}
|
|
7499
|
+
};
|
|
7500
|
+
const initialTimeValues = getInitialTimeValues();
|
|
7192
7501
|
// Time state for 12-hour format
|
|
7193
|
-
const [hour12, setHour12] = useState(
|
|
7194
|
-
const [minute, setMinute] = useState(
|
|
7195
|
-
const [meridiem, setMeridiem] = useState(
|
|
7502
|
+
const [hour12, setHour12] = useState(initialTimeValues.hour12);
|
|
7503
|
+
const [minute, setMinute] = useState(initialTimeValues.minute);
|
|
7504
|
+
const [meridiem, setMeridiem] = useState(initialTimeValues.meridiem);
|
|
7196
7505
|
// Time state for 24-hour format
|
|
7197
|
-
const [hour24, setHour24] = useState(
|
|
7198
|
-
const [second, setSecond] = useState(
|
|
7506
|
+
const [hour24, setHour24] = useState(initialTimeValues.hour24);
|
|
7507
|
+
const [second, setSecond] = useState(initialTimeValues.second);
|
|
7199
7508
|
// Sync selectedDate and time states when value prop changes
|
|
7200
7509
|
useEffect(() => {
|
|
7201
7510
|
console.log('[DateTimePicker] useEffect triggered - value changed:', {
|
|
@@ -7203,27 +7512,47 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7203
7512
|
timezone,
|
|
7204
7513
|
format,
|
|
7205
7514
|
});
|
|
7206
|
-
// If value is null, undefined, or invalid, clear
|
|
7515
|
+
// If value is null, undefined, or invalid, clear date but keep default time values
|
|
7207
7516
|
if (!value || value === null || value === undefined) {
|
|
7208
|
-
console.log('[DateTimePicker] Value is null/undefined, clearing
|
|
7517
|
+
console.log('[DateTimePicker] Value is null/undefined, clearing date but keeping default time');
|
|
7209
7518
|
setSelectedDate('');
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7519
|
+
// Keep default time values instead of clearing them
|
|
7520
|
+
if (format === 'iso-date-time') {
|
|
7521
|
+
setHour24(defaultTime ? defaultTime.hour ?? 0 : 0);
|
|
7522
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7523
|
+
setSecond(showSeconds
|
|
7524
|
+
? defaultTime
|
|
7525
|
+
? defaultTime.second ?? 0
|
|
7526
|
+
: 0
|
|
7527
|
+
: null);
|
|
7528
|
+
}
|
|
7529
|
+
else {
|
|
7530
|
+
setHour12(defaultTime ? defaultTime.hour ?? 12 : 12);
|
|
7531
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7532
|
+
setMeridiem(defaultTime ? defaultTime.meridiem ?? 'am' : 'am');
|
|
7533
|
+
}
|
|
7215
7534
|
return;
|
|
7216
7535
|
}
|
|
7217
7536
|
// Check if value is valid
|
|
7218
7537
|
const dateObj = dayjs(value).tz(timezone);
|
|
7219
7538
|
if (!dateObj.isValid()) {
|
|
7220
|
-
console.log('[DateTimePicker] Invalid value, clearing
|
|
7539
|
+
console.log('[DateTimePicker] Invalid value, clearing date but keeping default time');
|
|
7221
7540
|
setSelectedDate('');
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7541
|
+
// Keep default time values instead of clearing them
|
|
7542
|
+
if (format === 'iso-date-time') {
|
|
7543
|
+
setHour24(defaultTime ? defaultTime.hour ?? 0 : 0);
|
|
7544
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7545
|
+
setSecond(showSeconds
|
|
7546
|
+
? defaultTime
|
|
7547
|
+
? defaultTime.second ?? 0
|
|
7548
|
+
: 0
|
|
7549
|
+
: null);
|
|
7550
|
+
}
|
|
7551
|
+
else {
|
|
7552
|
+
setHour12(defaultTime ? defaultTime.hour ?? 12 : 12);
|
|
7553
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7554
|
+
setMeridiem(defaultTime ? defaultTime.meridiem ?? 'am' : 'am');
|
|
7555
|
+
}
|
|
7227
7556
|
return;
|
|
7228
7557
|
}
|
|
7229
7558
|
const dateString = getDateString(value);
|
|
@@ -7279,16 +7608,76 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7279
7608
|
onChange?.(undefined);
|
|
7280
7609
|
return;
|
|
7281
7610
|
}
|
|
7611
|
+
// Check if time values are null - if so, use defaultTime or set to 00:00
|
|
7612
|
+
const hasTimeValues = format === 'iso-date-time'
|
|
7613
|
+
? hour24 !== null || minute !== null
|
|
7614
|
+
: hour12 !== null || minute !== null || meridiem !== null;
|
|
7615
|
+
let timeDataToUse = undefined;
|
|
7616
|
+
if (!hasTimeValues) {
|
|
7617
|
+
// Use defaultTime if provided, otherwise default to 00:00
|
|
7618
|
+
if (defaultTime) {
|
|
7619
|
+
console.log('[DateTimePicker] No time values set, using defaultTime');
|
|
7620
|
+
if (format === 'iso-date-time') {
|
|
7621
|
+
const defaultTime24 = defaultTime;
|
|
7622
|
+
setHour24(defaultTime24.hour ?? 0);
|
|
7623
|
+
setMinute(defaultTime24.minute ?? 0);
|
|
7624
|
+
if (showSeconds) {
|
|
7625
|
+
setSecond(defaultTime24.second ?? 0);
|
|
7626
|
+
}
|
|
7627
|
+
timeDataToUse = {
|
|
7628
|
+
hour: defaultTime24.hour ?? 0,
|
|
7629
|
+
minute: defaultTime24.minute ?? 0,
|
|
7630
|
+
second: showSeconds ? defaultTime24.second ?? 0 : undefined,
|
|
7631
|
+
};
|
|
7632
|
+
}
|
|
7633
|
+
else {
|
|
7634
|
+
const defaultTime12 = defaultTime;
|
|
7635
|
+
setHour12(defaultTime12.hour ?? 12);
|
|
7636
|
+
setMinute(defaultTime12.minute ?? 0);
|
|
7637
|
+
setMeridiem(defaultTime12.meridiem ?? 'am');
|
|
7638
|
+
timeDataToUse = {
|
|
7639
|
+
hour: defaultTime12.hour ?? 12,
|
|
7640
|
+
minute: defaultTime12.minute ?? 0,
|
|
7641
|
+
meridiem: defaultTime12.meridiem ?? 'am',
|
|
7642
|
+
};
|
|
7643
|
+
}
|
|
7644
|
+
}
|
|
7645
|
+
else {
|
|
7646
|
+
console.log('[DateTimePicker] No time values set, defaulting to 00:00');
|
|
7647
|
+
if (format === 'iso-date-time') {
|
|
7648
|
+
setHour24(0);
|
|
7649
|
+
setMinute(0);
|
|
7650
|
+
if (showSeconds) {
|
|
7651
|
+
setSecond(0);
|
|
7652
|
+
}
|
|
7653
|
+
timeDataToUse = {
|
|
7654
|
+
hour: 0,
|
|
7655
|
+
minute: 0,
|
|
7656
|
+
second: showSeconds ? 0 : undefined,
|
|
7657
|
+
};
|
|
7658
|
+
}
|
|
7659
|
+
else {
|
|
7660
|
+
setHour12(12);
|
|
7661
|
+
setMinute(0);
|
|
7662
|
+
setMeridiem('am');
|
|
7663
|
+
timeDataToUse = {
|
|
7664
|
+
hour: 12,
|
|
7665
|
+
minute: 0,
|
|
7666
|
+
meridiem: 'am',
|
|
7667
|
+
};
|
|
7668
|
+
}
|
|
7669
|
+
}
|
|
7670
|
+
}
|
|
7282
7671
|
// When showSeconds is false, ignore seconds from the date
|
|
7283
7672
|
if (!showSeconds) {
|
|
7284
7673
|
const dateWithoutSeconds = dateObj.second(0).millisecond(0).toISOString();
|
|
7285
7674
|
console.log('[DateTimePicker] Updating date without seconds:', dateWithoutSeconds);
|
|
7286
|
-
updateDateTime(dateWithoutSeconds);
|
|
7675
|
+
updateDateTime(dateWithoutSeconds, timeDataToUse);
|
|
7287
7676
|
}
|
|
7288
7677
|
else {
|
|
7289
7678
|
const dateWithSeconds = dateObj.toISOString();
|
|
7290
7679
|
console.log('[DateTimePicker] Updating date with seconds:', dateWithSeconds);
|
|
7291
|
-
updateDateTime(dateWithSeconds);
|
|
7680
|
+
updateDateTime(dateWithSeconds, timeDataToUse);
|
|
7292
7681
|
}
|
|
7293
7682
|
};
|
|
7294
7683
|
const handleTimeChange = (timeData) => {
|
|
@@ -7318,17 +7707,36 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7318
7707
|
setMinute(data.minute);
|
|
7319
7708
|
setMeridiem(data.meridiem);
|
|
7320
7709
|
}
|
|
7321
|
-
// Use selectedDate if valid, otherwise clear all fields
|
|
7710
|
+
// Use selectedDate if valid, otherwise use effectiveDefaultDate or clear all fields
|
|
7322
7711
|
if (!selectedDate || !dayjs(selectedDate).isValid()) {
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7712
|
+
// If effectiveDefaultDate is available, use it instead of clearing
|
|
7713
|
+
if (effectiveDefaultDate && dayjs(effectiveDefaultDate).isValid()) {
|
|
7714
|
+
console.log('[DateTimePicker] No valid selectedDate, using effectiveDefaultDate:', effectiveDefaultDate);
|
|
7715
|
+
setSelectedDate(effectiveDefaultDate);
|
|
7716
|
+
const dateObj = dayjs(effectiveDefaultDate).tz(timezone);
|
|
7717
|
+
if (dateObj.isValid()) {
|
|
7718
|
+
updateDateTime(dateObj.toISOString(), timeData);
|
|
7719
|
+
}
|
|
7720
|
+
else {
|
|
7721
|
+
console.warn('[DateTimePicker] Invalid effectiveDefaultDate, clearing fields');
|
|
7722
|
+
setSelectedDate('');
|
|
7723
|
+
setHour12(null);
|
|
7724
|
+
setMinute(null);
|
|
7725
|
+
setMeridiem(null);
|
|
7726
|
+
setHour24(null);
|
|
7727
|
+
setSecond(null);
|
|
7728
|
+
onChange?.(undefined);
|
|
7729
|
+
}
|
|
7730
|
+
return;
|
|
7731
|
+
}
|
|
7732
|
+
else {
|
|
7733
|
+
console.log('[DateTimePicker] No valid selectedDate and no effectiveDefaultDate, keeping time values but no date');
|
|
7734
|
+
// Keep the time values that were just set, but don't set a date
|
|
7735
|
+
// This should rarely happen as effectiveDefaultDate always defaults to today
|
|
7736
|
+
setSelectedDate('');
|
|
7737
|
+
onChange?.(undefined);
|
|
7738
|
+
return;
|
|
7739
|
+
}
|
|
7332
7740
|
}
|
|
7333
7741
|
const dateObj = dayjs(selectedDate).tz(timezone);
|
|
7334
7742
|
if (dateObj.isValid()) {
|
|
@@ -7471,18 +7879,24 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7471
7879
|
};
|
|
7472
7880
|
const handleClear = () => {
|
|
7473
7881
|
setSelectedDate('');
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7882
|
+
// Reset to default time values instead of clearing them
|
|
7883
|
+
if (format === 'iso-date-time') {
|
|
7884
|
+
setHour24(defaultTime ? defaultTime.hour ?? 0 : 0);
|
|
7885
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7886
|
+
setSecond(showSeconds
|
|
7887
|
+
? defaultTime
|
|
7888
|
+
? defaultTime.second ?? 0
|
|
7889
|
+
: 0
|
|
7890
|
+
: null);
|
|
7891
|
+
}
|
|
7892
|
+
else {
|
|
7893
|
+
setHour12(defaultTime ? defaultTime.hour ?? 12 : 12);
|
|
7894
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7895
|
+
setMeridiem(defaultTime ? defaultTime.meridiem ?? 'am' : 'am');
|
|
7896
|
+
}
|
|
7479
7897
|
onChange?.(undefined);
|
|
7480
7898
|
};
|
|
7481
7899
|
const isISO = format === 'iso-date-time';
|
|
7482
|
-
// Normalize startTime to ignore milliseconds
|
|
7483
|
-
const normalizedStartTime = startTime
|
|
7484
|
-
? dayjs(startTime).tz(timezone).millisecond(0).toISOString()
|
|
7485
|
-
: undefined;
|
|
7486
7900
|
// Determine minDate: prioritize explicit minDate prop, then fall back to startTime
|
|
7487
7901
|
const effectiveMinDate = minDate
|
|
7488
7902
|
? minDate
|
|
@@ -7560,7 +7974,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7560
7974
|
const dateObj = dayjs.tz(selectedDate, timezone);
|
|
7561
7975
|
return dateObj.isValid() ? dateObj.format('Z') : null;
|
|
7562
7976
|
}, [selectedDate, timezone]);
|
|
7563
|
-
return (jsxs(Flex, { direction: "column", gap:
|
|
7977
|
+
return (jsxs(Flex, { direction: "column", gap: 2, children: [jsx(DatePickerInput, { value: selectedDate || undefined, onChange: (date) => {
|
|
7564
7978
|
if (date) {
|
|
7565
7979
|
handleDateChange(date);
|
|
7566
7980
|
}
|
|
@@ -7568,7 +7982,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7568
7982
|
setSelectedDate('');
|
|
7569
7983
|
onChange?.(undefined);
|
|
7570
7984
|
}
|
|
7571
|
-
}, placeholder: "Select a date", dateFormat: "YYYY-MM-DD", displayFormat: "YYYY-MM-DD", labels: labels, timezone: timezone, minDate: effectiveMinDate, maxDate: maxDate, monthsToDisplay: 1, readOnly:
|
|
7985
|
+
}, placeholder: "Select a date", dateFormat: "YYYY-MM-DD", displayFormat: "YYYY-MM-DD", labels: labels, timezone: timezone, minDate: effectiveMinDate, maxDate: maxDate, monthsToDisplay: 1, readOnly: false }), jsxs(Grid, { templateColumns: "1fr auto", alignItems: "center", gap: 2, children: [isISO ? (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 })) : (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 })), jsx(Button$1, { onClick: handleClear, size: "sm", variant: "outline", colorScheme: "red", children: jsx(Icon, { as: FaTrash }) })] }), displayText && (jsxs(Flex, { gap: 2, children: [jsx(Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: displayText }), timezoneOffset && (jsx(Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: timezoneOffset })), jsx(Text, { fontSize: "sm", color: { base: 'gray.600', _dark: 'gray.600' }, children: timezone })] }))] }));
|
|
7572
7986
|
}
|
|
7573
7987
|
|
|
7574
7988
|
dayjs.extend(utc);
|
|
@@ -7630,7 +8044,7 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
|
|
|
7630
8044
|
return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
7631
8045
|
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
7632
8046
|
setOpen(true);
|
|
7633
|
-
}, justifyContent: 'start', children: [jsx(MdDateRange, {}), displayDate || ''] }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minW: "
|
|
8047
|
+
}, justifyContent: 'start', children: [jsx(MdDateRange, {}), displayDate || ''] }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minW: "350px", minH: "10rem", children: jsx(Popover.Body, { children: dateTimePickerContent }) }) })) : (jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minW: "350px", minH: "10rem", children: jsx(Popover.Body, { children: dateTimePickerContent }) }) }) }))] }) }));
|
|
7634
8048
|
};
|
|
7635
8049
|
|
|
7636
8050
|
const SchemaRenderer = ({ schema, prefix, column, }) => {
|