@bsol-oss/react-datatable5 13.0.1-beta.3 → 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 +661 -209
- package/dist/index.mjs +661 -209
- 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/components/Form/components/fields/FilePicker.d.ts +1 -1
- package/dist/types/components/Form/components/viewers/EnumViewer.d.ts +1 -1
- package/dist/types/components/Form/utils/useFormI18n.d.ts +2 -2
- 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.js
CHANGED
|
@@ -31,8 +31,8 @@ var addFormats = require('ajv-formats');
|
|
|
31
31
|
var dayjs = require('dayjs');
|
|
32
32
|
var utc = require('dayjs/plugin/utc');
|
|
33
33
|
var timezone = require('dayjs/plugin/timezone');
|
|
34
|
-
var ti = require('react-icons/ti');
|
|
35
34
|
var customParseFormat = require('dayjs/plugin/customParseFormat');
|
|
35
|
+
var ti = require('react-icons/ti');
|
|
36
36
|
var matchSorterUtils = require('@tanstack/match-sorter-utils');
|
|
37
37
|
|
|
38
38
|
function _interopNamespaceDefault(e) {
|
|
@@ -4146,6 +4146,22 @@ const convertAjvErrorsToFieldErrors = (errors, schema) => {
|
|
|
4146
4146
|
// Get the schema node for this field to check for custom error messages
|
|
4147
4147
|
const fieldSchema = getSchemaNodeForField(schema, fieldName);
|
|
4148
4148
|
const customMessage = fieldSchema?.errorMessages?.[error.keyword];
|
|
4149
|
+
// Debug log when error message is missing
|
|
4150
|
+
if (!customMessage) {
|
|
4151
|
+
console.debug(`[Form Validation] Missing error message for field '${fieldName}' with keyword '${error.keyword}'. Add errorMessages.${error.keyword} to schema for field '${fieldName}'`, {
|
|
4152
|
+
fieldName,
|
|
4153
|
+
keyword: error.keyword,
|
|
4154
|
+
instancePath: error.instancePath,
|
|
4155
|
+
schemaPath: error.schemaPath,
|
|
4156
|
+
params: error.params,
|
|
4157
|
+
fieldSchema: fieldSchema
|
|
4158
|
+
? {
|
|
4159
|
+
type: fieldSchema.type,
|
|
4160
|
+
errorMessages: fieldSchema.errorMessages,
|
|
4161
|
+
}
|
|
4162
|
+
: undefined,
|
|
4163
|
+
});
|
|
4164
|
+
}
|
|
4149
4165
|
// Provide helpful fallback message if no custom message is provided
|
|
4150
4166
|
const fallbackMessage = customMessage ||
|
|
4151
4167
|
`Missing error message for ${error.keyword}. Add errorMessages.${error.keyword} to schema for field '${fieldName}'`;
|
|
@@ -4388,7 +4404,7 @@ function removeIndex(str) {
|
|
|
4388
4404
|
*
|
|
4389
4405
|
* @param column - The column name
|
|
4390
4406
|
* @param prefix - The prefix for the field (usually empty string or parent path)
|
|
4391
|
-
* @param schema -
|
|
4407
|
+
* @param schema - Required schema object with title property
|
|
4392
4408
|
* @returns Object with label helper functions
|
|
4393
4409
|
*
|
|
4394
4410
|
* @example
|
|
@@ -4421,9 +4437,21 @@ const useFormI18n = (column, prefix = '', schema) => {
|
|
|
4421
4437
|
* Uses schema.title if available, otherwise: translate.t(removeIndex(`${colLabel}.field_label`))
|
|
4422
4438
|
*/
|
|
4423
4439
|
label: (options) => {
|
|
4424
|
-
if (schema
|
|
4440
|
+
if (schema.title) {
|
|
4425
4441
|
return schema.title;
|
|
4426
4442
|
}
|
|
4443
|
+
// Debug log when field title is missing
|
|
4444
|
+
console.debug(`[Form Field Label] Missing title for field '${colLabel}'. Add title property to schema for field '${colLabel}'.`, {
|
|
4445
|
+
fieldName: column,
|
|
4446
|
+
colLabel,
|
|
4447
|
+
prefix,
|
|
4448
|
+
schema: {
|
|
4449
|
+
type: schema.type,
|
|
4450
|
+
errorMessages: schema.errorMessages
|
|
4451
|
+
? Object.keys(schema.errorMessages)
|
|
4452
|
+
: undefined,
|
|
4453
|
+
},
|
|
4454
|
+
});
|
|
4427
4455
|
return translate.t(removeIndex(`${colLabel}.field_label`), options);
|
|
4428
4456
|
},
|
|
4429
4457
|
/**
|
|
@@ -4517,6 +4545,9 @@ const CustomInput = ({ column, schema, prefix }) => {
|
|
|
4517
4545
|
}));
|
|
4518
4546
|
};
|
|
4519
4547
|
|
|
4548
|
+
dayjs.extend(utc);
|
|
4549
|
+
dayjs.extend(timezone);
|
|
4550
|
+
dayjs.extend(customParseFormat);
|
|
4520
4551
|
const Calendar = ({ calendars, getBackProps, getForwardProps, getDateProps, firstDayOfWeek = 0, }) => {
|
|
4521
4552
|
const { labels } = React.useContext(DatePickerContext);
|
|
4522
4553
|
const { monthNamesShort, weekdayNamesShort, backButtonLabel, forwardButtonLabel, } = labels;
|
|
@@ -4586,6 +4617,9 @@ const DatePickerContext = React.createContext({
|
|
|
4586
4617
|
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
4587
4618
|
backButtonLabel: 'Back',
|
|
4588
4619
|
forwardButtonLabel: 'Next',
|
|
4620
|
+
todayLabel: 'Today',
|
|
4621
|
+
yesterdayLabel: 'Yesterday',
|
|
4622
|
+
tomorrowLabel: 'Tomorrow',
|
|
4589
4623
|
},
|
|
4590
4624
|
});
|
|
4591
4625
|
const DatePicker$1 = ({ labels = {
|
|
@@ -4606,6 +4640,9 @@ const DatePicker$1 = ({ labels = {
|
|
|
4606
4640
|
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
4607
4641
|
backButtonLabel: 'Back',
|
|
4608
4642
|
forwardButtonLabel: 'Next',
|
|
4643
|
+
todayLabel: 'Today',
|
|
4644
|
+
yesterdayLabel: 'Yesterday',
|
|
4645
|
+
tomorrowLabel: 'Tomorrow',
|
|
4609
4646
|
}, onDateSelected, selected, firstDayOfWeek, showOutsideDays, date, minDate, maxDate, monthsToDisplay, render, }) => {
|
|
4610
4647
|
const calendarData = useCalendar({
|
|
4611
4648
|
onDateSelected,
|
|
@@ -4620,9 +4657,164 @@ const DatePicker$1 = ({ labels = {
|
|
|
4620
4657
|
return (jsxRuntime.jsx(DatePickerContext.Provider, { value: { labels }, children: render ? (render(calendarData)) : (jsxRuntime.jsx(Calendar, { ...calendarData,
|
|
4621
4658
|
firstDayOfWeek })) }));
|
|
4622
4659
|
};
|
|
4660
|
+
function DatePickerInput({ value, onChange, placeholder = 'Select a date', dateFormat = 'YYYY-MM-DD', displayFormat = 'YYYY-MM-DD', labels = {
|
|
4661
|
+
monthNamesShort: [
|
|
4662
|
+
'Jan',
|
|
4663
|
+
'Feb',
|
|
4664
|
+
'Mar',
|
|
4665
|
+
'Apr',
|
|
4666
|
+
'May',
|
|
4667
|
+
'Jun',
|
|
4668
|
+
'Jul',
|
|
4669
|
+
'Aug',
|
|
4670
|
+
'Sep',
|
|
4671
|
+
'Oct',
|
|
4672
|
+
'Nov',
|
|
4673
|
+
'Dec',
|
|
4674
|
+
],
|
|
4675
|
+
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
4676
|
+
backButtonLabel: 'Back',
|
|
4677
|
+
forwardButtonLabel: 'Next',
|
|
4678
|
+
todayLabel: 'Today',
|
|
4679
|
+
yesterdayLabel: 'Yesterday',
|
|
4680
|
+
tomorrowLabel: 'Tomorrow',
|
|
4681
|
+
}, timezone = 'Asia/Hong_Kong', minDate, maxDate, firstDayOfWeek, showOutsideDays, monthsToDisplay = 1, insideDialog = false, readOnly = false, showHelperButtons = true, }) {
|
|
4682
|
+
const [open, setOpen] = React.useState(false);
|
|
4683
|
+
const [inputValue, setInputValue] = React.useState('');
|
|
4684
|
+
// Update input value when prop value changes
|
|
4685
|
+
React.useEffect(() => {
|
|
4686
|
+
if (value) {
|
|
4687
|
+
const formatted = typeof value === 'string'
|
|
4688
|
+
? dayjs(value).tz(timezone).isValid()
|
|
4689
|
+
? dayjs(value).tz(timezone).format(displayFormat)
|
|
4690
|
+
: ''
|
|
4691
|
+
: dayjs(value).tz(timezone).format(displayFormat);
|
|
4692
|
+
setInputValue(formatted);
|
|
4693
|
+
}
|
|
4694
|
+
else {
|
|
4695
|
+
setInputValue('');
|
|
4696
|
+
}
|
|
4697
|
+
}, [value, displayFormat, timezone]);
|
|
4698
|
+
// Convert value to Date object for DatePicker
|
|
4699
|
+
const selectedDate = value
|
|
4700
|
+
? typeof value === 'string'
|
|
4701
|
+
? dayjs(value).tz(timezone).isValid()
|
|
4702
|
+
? dayjs(value).tz(timezone).toDate()
|
|
4703
|
+
: new Date()
|
|
4704
|
+
: value
|
|
4705
|
+
: new Date();
|
|
4706
|
+
// Shared function to parse and validate input value
|
|
4707
|
+
const parseAndValidateInput = (inputVal) => {
|
|
4708
|
+
// If empty, clear the value
|
|
4709
|
+
if (!inputVal.trim()) {
|
|
4710
|
+
onChange?.(undefined);
|
|
4711
|
+
setInputValue('');
|
|
4712
|
+
return;
|
|
4713
|
+
}
|
|
4714
|
+
// Try parsing with displayFormat first
|
|
4715
|
+
let parsedDate = dayjs(inputVal, displayFormat, true);
|
|
4716
|
+
// If that fails, try common date formats
|
|
4717
|
+
if (!parsedDate.isValid()) {
|
|
4718
|
+
parsedDate = dayjs(inputVal);
|
|
4719
|
+
}
|
|
4720
|
+
// If still invalid, try parsing with dateFormat
|
|
4721
|
+
if (!parsedDate.isValid()) {
|
|
4722
|
+
parsedDate = dayjs(inputVal, dateFormat, true);
|
|
4723
|
+
}
|
|
4724
|
+
// If valid, check constraints and update
|
|
4725
|
+
if (parsedDate.isValid()) {
|
|
4726
|
+
const dateObj = parsedDate.tz(timezone).toDate();
|
|
4727
|
+
// Check min/max constraints
|
|
4728
|
+
if (minDate && dateObj < minDate) {
|
|
4729
|
+
// Invalid: before minDate, reset to prop value
|
|
4730
|
+
resetToPropValue();
|
|
4731
|
+
return;
|
|
4732
|
+
}
|
|
4733
|
+
if (maxDate && dateObj > maxDate) {
|
|
4734
|
+
// Invalid: after maxDate, reset to prop value
|
|
4735
|
+
resetToPropValue();
|
|
4736
|
+
return;
|
|
4737
|
+
}
|
|
4738
|
+
// Valid date - format and update
|
|
4739
|
+
const formattedDate = parsedDate.tz(timezone).format(dateFormat);
|
|
4740
|
+
const formattedDisplay = parsedDate.tz(timezone).format(displayFormat);
|
|
4741
|
+
onChange?.(formattedDate);
|
|
4742
|
+
setInputValue(formattedDisplay);
|
|
4743
|
+
}
|
|
4744
|
+
else {
|
|
4745
|
+
// Invalid date - reset to prop value
|
|
4746
|
+
resetToPropValue();
|
|
4747
|
+
}
|
|
4748
|
+
};
|
|
4749
|
+
// Helper function to reset input to prop value
|
|
4750
|
+
const resetToPropValue = () => {
|
|
4751
|
+
if (value) {
|
|
4752
|
+
const formatted = typeof value === 'string'
|
|
4753
|
+
? dayjs(value).tz(timezone).isValid()
|
|
4754
|
+
? dayjs(value).tz(timezone).format(displayFormat)
|
|
4755
|
+
: ''
|
|
4756
|
+
: dayjs(value).tz(timezone).format(displayFormat);
|
|
4757
|
+
setInputValue(formatted);
|
|
4758
|
+
}
|
|
4759
|
+
else {
|
|
4760
|
+
setInputValue('');
|
|
4761
|
+
}
|
|
4762
|
+
};
|
|
4763
|
+
const handleInputChange = (e) => {
|
|
4764
|
+
// Only update the input value, don't parse yet
|
|
4765
|
+
setInputValue(e.target.value);
|
|
4766
|
+
};
|
|
4767
|
+
const handleInputBlur = () => {
|
|
4768
|
+
// Parse and validate when input loses focus
|
|
4769
|
+
parseAndValidateInput(inputValue);
|
|
4770
|
+
};
|
|
4771
|
+
const handleKeyDown = (e) => {
|
|
4772
|
+
// Parse and validate when Enter is pressed
|
|
4773
|
+
if (e.key === 'Enter') {
|
|
4774
|
+
e.preventDefault();
|
|
4775
|
+
parseAndValidateInput(inputValue);
|
|
4776
|
+
}
|
|
4777
|
+
};
|
|
4778
|
+
const handleDateSelected = ({ date }) => {
|
|
4779
|
+
const formattedDate = dayjs(date).tz(timezone).format(dateFormat);
|
|
4780
|
+
onChange?.(formattedDate);
|
|
4781
|
+
setOpen(false);
|
|
4782
|
+
};
|
|
4783
|
+
// Helper function to get dates in the correct timezone
|
|
4784
|
+
const getToday = () => dayjs().tz(timezone).startOf('day').toDate();
|
|
4785
|
+
const getYesterday = () => dayjs().tz(timezone).subtract(1, 'day').startOf('day').toDate();
|
|
4786
|
+
const getTomorrow = () => dayjs().tz(timezone).add(1, 'day').startOf('day').toDate();
|
|
4787
|
+
// Check if a date is within min/max constraints
|
|
4788
|
+
const isDateValid = (date) => {
|
|
4789
|
+
if (minDate) {
|
|
4790
|
+
const minDateStart = dayjs(minDate).tz(timezone).startOf('day').toDate();
|
|
4791
|
+
const dateStart = dayjs(date).tz(timezone).startOf('day').toDate();
|
|
4792
|
+
if (dateStart < minDateStart)
|
|
4793
|
+
return false;
|
|
4794
|
+
}
|
|
4795
|
+
if (maxDate) {
|
|
4796
|
+
const maxDateStart = dayjs(maxDate).tz(timezone).startOf('day').toDate();
|
|
4797
|
+
const dateStart = dayjs(date).tz(timezone).startOf('day').toDate();
|
|
4798
|
+
if (dateStart > maxDateStart)
|
|
4799
|
+
return false;
|
|
4800
|
+
}
|
|
4801
|
+
return true;
|
|
4802
|
+
};
|
|
4803
|
+
const handleHelperButtonClick = (date) => {
|
|
4804
|
+
if (isDateValid(date)) {
|
|
4805
|
+
handleDateSelected({ date });
|
|
4806
|
+
}
|
|
4807
|
+
};
|
|
4808
|
+
const today = getToday();
|
|
4809
|
+
const yesterday = getYesterday();
|
|
4810
|
+
const tomorrow = getTomorrow();
|
|
4811
|
+
const datePickerContent = (jsxRuntime.jsxs(react.Grid, { gap: 2, children: [showHelperButtons && (jsxRuntime.jsxs(react.Grid, { templateColumns: "repeat(3, 1fr)", gap: 2, children: [jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleHelperButtonClick(yesterday), disabled: !isDateValid(yesterday), children: labels.yesterdayLabel ?? 'Yesterday' }), jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleHelperButtonClick(today), disabled: !isDateValid(today), children: labels.todayLabel ?? 'Today' }), jsxRuntime.jsx(react.Button, { size: "sm", variant: "outline", onClick: () => handleHelperButtonClick(tomorrow), disabled: !isDateValid(tomorrow), children: labels.tomorrowLabel ?? 'Tomorrow' })] })), jsxRuntime.jsx(DatePicker$1, { selected: selectedDate, onDateSelected: handleDateSelected, labels: labels, minDate: minDate, maxDate: maxDate, firstDayOfWeek: firstDayOfWeek, showOutsideDays: showOutsideDays, monthsToDisplay: monthsToDisplay })] }));
|
|
4812
|
+
return (jsxRuntime.jsxs(react.Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsxRuntime.jsx(InputGroup, { endElement: jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsx(react.IconButton, { variant: "ghost", size: "2xs", "aria-label": "Open calendar", onClick: () => setOpen(true), children: jsxRuntime.jsx(react.Icon, { children: jsxRuntime.jsx(md.MdDateRange, {}) }) }) }), children: jsxRuntime.jsx(react.Input, { value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onKeyDown: handleKeyDown, placeholder: placeholder, readOnly: readOnly }) }), insideDialog ? (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { children: datePickerContent }) }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { children: datePickerContent }) }) }) }))] }));
|
|
4813
|
+
}
|
|
4623
4814
|
|
|
4624
4815
|
dayjs.extend(utc);
|
|
4625
4816
|
dayjs.extend(timezone);
|
|
4817
|
+
dayjs.extend(customParseFormat);
|
|
4626
4818
|
const DatePicker = ({ column, schema, prefix }) => {
|
|
4627
4819
|
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
4628
4820
|
const { timezone, dateTimePickerLabels, insideDialog } = useSchemaContext();
|
|
@@ -4631,15 +4823,29 @@ const DatePicker = ({ column, schema, prefix }) => {
|
|
|
4631
4823
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4632
4824
|
const colLabel = formI18n.colLabel;
|
|
4633
4825
|
const [open, setOpen] = React.useState(false);
|
|
4826
|
+
const [inputValue, setInputValue] = React.useState('');
|
|
4634
4827
|
const selectedDate = watch(colLabel);
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4828
|
+
// Update input value when form value changes
|
|
4829
|
+
React.useEffect(() => {
|
|
4830
|
+
if (selectedDate) {
|
|
4831
|
+
const parsedDate = dayjs(selectedDate).tz(timezone);
|
|
4832
|
+
if (parsedDate.isValid()) {
|
|
4833
|
+
const formatted = parsedDate.format(displayDateFormat);
|
|
4834
|
+
setInputValue(formatted);
|
|
4835
|
+
}
|
|
4836
|
+
else {
|
|
4837
|
+
setInputValue('');
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4840
|
+
else {
|
|
4841
|
+
setInputValue('');
|
|
4842
|
+
}
|
|
4843
|
+
}, [selectedDate, displayDateFormat, timezone]);
|
|
4844
|
+
// Format and validate existing value
|
|
4638
4845
|
React.useEffect(() => {
|
|
4639
4846
|
try {
|
|
4640
4847
|
if (selectedDate) {
|
|
4641
4848
|
// Parse the selectedDate as UTC or in a specific timezone to avoid +8 hour shift
|
|
4642
|
-
// For example, parse as UTC:
|
|
4643
4849
|
const parsedDate = dayjs(selectedDate).tz(timezone);
|
|
4644
4850
|
if (!parsedDate.isValid())
|
|
4645
4851
|
return;
|
|
@@ -4657,7 +4863,7 @@ const DatePicker = ({ column, schema, prefix }) => {
|
|
|
4657
4863
|
catch (e) {
|
|
4658
4864
|
console.error(e);
|
|
4659
4865
|
}
|
|
4660
|
-
}, [selectedDate, dateFormat, colLabel, setValue]);
|
|
4866
|
+
}, [selectedDate, dateFormat, colLabel, setValue, timezone]);
|
|
4661
4867
|
const datePickerLabels = {
|
|
4662
4868
|
monthNamesShort: dateTimePickerLabels?.monthNamesShort ?? [
|
|
4663
4869
|
'January',
|
|
@@ -4685,14 +4891,92 @@ const DatePicker = ({ column, schema, prefix }) => {
|
|
|
4685
4891
|
backButtonLabel: dateTimePickerLabels?.backButtonLabel ?? 'Back',
|
|
4686
4892
|
forwardButtonLabel: dateTimePickerLabels?.forwardButtonLabel ?? 'Forward',
|
|
4687
4893
|
};
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4894
|
+
// Convert value to Date object for DatePicker
|
|
4895
|
+
const selectedDateObj = selectedDate
|
|
4896
|
+
? dayjs(selectedDate).tz(timezone).isValid()
|
|
4897
|
+
? dayjs(selectedDate).tz(timezone).toDate()
|
|
4898
|
+
: new Date()
|
|
4899
|
+
: new Date();
|
|
4900
|
+
// Shared function to parse and validate input value
|
|
4901
|
+
const parseAndValidateInput = (inputVal) => {
|
|
4902
|
+
// If empty, clear the value
|
|
4903
|
+
if (!inputVal.trim()) {
|
|
4904
|
+
setValue(colLabel, undefined, {
|
|
4905
|
+
shouldValidate: true,
|
|
4906
|
+
shouldDirty: true,
|
|
4907
|
+
});
|
|
4908
|
+
setInputValue('');
|
|
4909
|
+
return;
|
|
4910
|
+
}
|
|
4911
|
+
// Try parsing with displayDateFormat first
|
|
4912
|
+
let parsedDate = dayjs(inputVal, displayDateFormat, true);
|
|
4913
|
+
// If that fails, try common date formats
|
|
4914
|
+
if (!parsedDate.isValid()) {
|
|
4915
|
+
parsedDate = dayjs(inputVal);
|
|
4916
|
+
}
|
|
4917
|
+
// If still invalid, try parsing with dateFormat
|
|
4918
|
+
if (!parsedDate.isValid()) {
|
|
4919
|
+
parsedDate = dayjs(inputVal, dateFormat, true);
|
|
4920
|
+
}
|
|
4921
|
+
// If valid, format and update
|
|
4922
|
+
if (parsedDate.isValid()) {
|
|
4923
|
+
const formattedDate = parsedDate.tz(timezone).format(dateFormat);
|
|
4924
|
+
const formattedDisplay = parsedDate
|
|
4925
|
+
.tz(timezone)
|
|
4926
|
+
.format(displayDateFormat);
|
|
4927
|
+
setValue(colLabel, formattedDate, {
|
|
4928
|
+
shouldValidate: true,
|
|
4929
|
+
shouldDirty: true,
|
|
4930
|
+
});
|
|
4931
|
+
setInputValue(formattedDisplay);
|
|
4932
|
+
}
|
|
4933
|
+
else {
|
|
4934
|
+
// Invalid date - reset to prop value
|
|
4935
|
+
resetToPropValue();
|
|
4936
|
+
}
|
|
4937
|
+
};
|
|
4938
|
+
// Helper function to reset input to prop value
|
|
4939
|
+
const resetToPropValue = () => {
|
|
4940
|
+
if (selectedDate) {
|
|
4941
|
+
const parsedDate = dayjs(selectedDate).tz(timezone);
|
|
4942
|
+
if (parsedDate.isValid()) {
|
|
4943
|
+
const formatted = parsedDate.format(displayDateFormat);
|
|
4944
|
+
setInputValue(formatted);
|
|
4945
|
+
}
|
|
4946
|
+
else {
|
|
4947
|
+
setInputValue('');
|
|
4948
|
+
}
|
|
4949
|
+
}
|
|
4950
|
+
else {
|
|
4951
|
+
setInputValue('');
|
|
4952
|
+
}
|
|
4953
|
+
};
|
|
4954
|
+
const handleInputChange = (e) => {
|
|
4955
|
+
// Only update the input value, don't parse yet
|
|
4956
|
+
setInputValue(e.target.value);
|
|
4957
|
+
};
|
|
4958
|
+
const handleInputBlur = () => {
|
|
4959
|
+
// Parse and validate when input loses focus
|
|
4960
|
+
parseAndValidateInput(inputValue);
|
|
4961
|
+
};
|
|
4962
|
+
const handleKeyDown = (e) => {
|
|
4963
|
+
// Parse and validate when Enter is pressed
|
|
4964
|
+
if (e.key === 'Enter') {
|
|
4965
|
+
e.preventDefault();
|
|
4966
|
+
parseAndValidateInput(inputValue);
|
|
4967
|
+
}
|
|
4968
|
+
};
|
|
4969
|
+
const handleDateSelected = ({ date }) => {
|
|
4970
|
+
const formattedDate = dayjs(date).tz(timezone).format(dateFormat);
|
|
4971
|
+
setValue(colLabel, formattedDate, {
|
|
4972
|
+
shouldValidate: true,
|
|
4973
|
+
shouldDirty: true,
|
|
4974
|
+
});
|
|
4975
|
+
setOpen(false);
|
|
4976
|
+
};
|
|
4977
|
+
const datePickerContent = (jsxRuntime.jsx(DatePicker$1, { selected: selectedDateObj, onDateSelected: handleDateSelected, labels: datePickerLabels }));
|
|
4692
4978
|
return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4693
|
-
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.
|
|
4694
|
-
setOpen(true);
|
|
4695
|
-
}, justifyContent: 'start', children: [jsxRuntime.jsx(md.MdDateRange, {}), selectedDate !== undefined ? `${displayDate}` : ''] }) }), insideDialog ? (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { children: datePickerContent }) }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { children: datePickerContent }) }) }) }))] }) }));
|
|
4979
|
+
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(InputGroup, { endElement: jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsx(react.IconButton, { variant: "ghost", size: "2xs", "aria-label": "Open calendar", onClick: () => setOpen(true), children: jsxRuntime.jsx(react.Icon, { children: jsxRuntime.jsx(md.MdDateRange, {}) }) }) }), children: jsxRuntime.jsx(react.Input, { value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onKeyDown: handleKeyDown, placeholder: formI18n.label(), size: "sm" }) }), insideDialog ? (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { children: datePickerContent }) }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { children: datePickerContent }) }) }) }))] }) }));
|
|
4696
4980
|
};
|
|
4697
4981
|
|
|
4698
4982
|
dayjs.extend(utc);
|
|
@@ -4700,7 +4984,7 @@ dayjs.extend(timezone);
|
|
|
4700
4984
|
const DateRangePicker = ({ column, schema, prefix, }) => {
|
|
4701
4985
|
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
4702
4986
|
const { timezone, insideDialog } = useSchemaContext();
|
|
4703
|
-
const formI18n = useFormI18n(column, prefix);
|
|
4987
|
+
const formI18n = useFormI18n(column, prefix, schema);
|
|
4704
4988
|
const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
|
|
4705
4989
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4706
4990
|
const colLabel = formI18n.colLabel;
|
|
@@ -5345,7 +5629,7 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
|
|
|
5345
5629
|
}) })) }))] }));
|
|
5346
5630
|
};
|
|
5347
5631
|
|
|
5348
|
-
function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels,
|
|
5632
|
+
function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, }) {
|
|
5349
5633
|
const [selectedFile, setSelectedFile] = React.useState(undefined);
|
|
5350
5634
|
const [activeTab, setActiveTab] = React.useState('browse');
|
|
5351
5635
|
const [uploadingFiles, setUploadingFiles] = React.useState(new Set());
|
|
@@ -5429,7 +5713,7 @@ function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly =
|
|
|
5429
5713
|
const FilePicker = ({ column, schema, prefix }) => {
|
|
5430
5714
|
const { setValue, formState: { errors }, watch, } = reactHookForm.useFormContext();
|
|
5431
5715
|
const { filePickerLabels } = useSchemaContext();
|
|
5432
|
-
const formI18n = useFormI18n(column, prefix);
|
|
5716
|
+
const formI18n = useFormI18n(column, prefix, schema);
|
|
5433
5717
|
const { required, gridColumn = 'span 12', gridRow = 'span 1', type, } = schema;
|
|
5434
5718
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5435
5719
|
const isSingleSelect = type === 'string';
|
|
@@ -5505,7 +5789,7 @@ const FilePicker = ({ column, schema, prefix }) => {
|
|
|
5505
5789
|
const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
5506
5790
|
const { setValue, formState: { errors }, watch, } = reactHookForm.useFormContext();
|
|
5507
5791
|
const { filePickerLabels } = useSchemaContext();
|
|
5508
|
-
const formI18n = useFormI18n(column, prefix);
|
|
5792
|
+
const formI18n = useFormI18n(column, prefix, schema);
|
|
5509
5793
|
const { required, gridColumn = 'span 12', gridRow = 'span 1', filePicker, type, } = schema;
|
|
5510
5794
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5511
5795
|
const isSingleSelect = type === 'string';
|
|
@@ -5600,9 +5884,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
5600
5884
|
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5601
5885
|
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [jsxRuntime.jsx(react.VStack, { align: "stretch", gap: 2, children: jsxRuntime.jsx(react.Button, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ??
|
|
5602
5886
|
formI18n.t('browse_library') ??
|
|
5603
|
-
'Browse from Library' }) }), jsxRuntime.jsx(MediaBrowserDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
|
|
5604
|
-
filePickerLabels?.dialogTitle ??
|
|
5605
|
-
'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, onUploadFile: onUploadFile, enableUpload: enableUpload, labels: filePickerLabels, colLabel: colLabel }), jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
|
|
5887
|
+
'Browse from Library' }) }), jsxRuntime.jsx(MediaBrowserDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ?? formI18n.label() ?? 'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, onUploadFile: onUploadFile, enableUpload: enableUpload, labels: filePickerLabels, colLabel: colLabel }), jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
|
|
5606
5888
|
const file = fileMap.get(fileId);
|
|
5607
5889
|
const isImage = file
|
|
5608
5890
|
? /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name)
|
|
@@ -5693,7 +5975,8 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
|
|
|
5693
5975
|
}
|
|
5694
5976
|
// Use schema's loadInitialValues (required for id-picker)
|
|
5695
5977
|
if (!loadInitialValues) {
|
|
5696
|
-
|
|
5978
|
+
console.warn(`loadInitialValues is required in schema for IdPicker field '${column}'. Returning empty idMap.`);
|
|
5979
|
+
return { data: [], count: 0 };
|
|
5697
5980
|
}
|
|
5698
5981
|
const result = await loadInitialValues({
|
|
5699
5982
|
ids: missingIds,
|
|
@@ -6425,14 +6708,74 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
|
|
|
6425
6708
|
}
|
|
6426
6709
|
}
|
|
6427
6710
|
}
|
|
6428
|
-
|
|
6711
|
+
// Sort options by time (convert to 24-hour for proper chronological sorting)
|
|
6712
|
+
return options.sort((a, b) => {
|
|
6713
|
+
// Convert 12-hour to 24-hour for comparison
|
|
6714
|
+
let hour24A = a.hour;
|
|
6715
|
+
if (a.meridiem === 'am' && a.hour === 12)
|
|
6716
|
+
hour24A = 0;
|
|
6717
|
+
else if (a.meridiem === 'pm' && a.hour < 12)
|
|
6718
|
+
hour24A = a.hour + 12;
|
|
6719
|
+
let hour24B = b.hour;
|
|
6720
|
+
if (b.meridiem === 'am' && b.hour === 12)
|
|
6721
|
+
hour24B = 0;
|
|
6722
|
+
else if (b.meridiem === 'pm' && b.hour < 12)
|
|
6723
|
+
hour24B = b.hour + 12;
|
|
6724
|
+
// Compare by hour first, then minute
|
|
6725
|
+
if (hour24A !== hour24B) {
|
|
6726
|
+
return hour24A - hour24B;
|
|
6727
|
+
}
|
|
6728
|
+
return a.minute - b.minute;
|
|
6729
|
+
});
|
|
6429
6730
|
}, [startTime, selectedDate, timezone]);
|
|
6430
|
-
|
|
6731
|
+
// itemToString returns only the clean display text (no metadata)
|
|
6732
|
+
const itemToString = React.useMemo(() => {
|
|
6733
|
+
return (item) => {
|
|
6734
|
+
return item.searchText; // Clean display text only
|
|
6735
|
+
};
|
|
6736
|
+
}, []);
|
|
6737
|
+
// Custom filter function that filters by time and supports 24-hour format input
|
|
6738
|
+
const customTimeFilter = React.useMemo(() => {
|
|
6739
|
+
return (itemText, filterText) => {
|
|
6740
|
+
if (!filterText) {
|
|
6741
|
+
return true; // Show all items when no filter
|
|
6742
|
+
}
|
|
6743
|
+
const lowerItemText = itemText.toLowerCase();
|
|
6744
|
+
const lowerFilterText = filterText.toLowerCase();
|
|
6745
|
+
// First, try matching against the display text (12-hour format)
|
|
6746
|
+
if (lowerItemText.includes(lowerFilterText)) {
|
|
6747
|
+
return true;
|
|
6748
|
+
}
|
|
6749
|
+
// Find the corresponding item to check 24-hour format matches
|
|
6750
|
+
const item = timeOptions.find((opt) => opt.searchText.toLowerCase() === lowerItemText);
|
|
6751
|
+
if (!item) {
|
|
6752
|
+
return false;
|
|
6753
|
+
}
|
|
6754
|
+
// Convert item to 24-hour format for matching
|
|
6755
|
+
let hour24 = item.hour;
|
|
6756
|
+
if (item.meridiem === 'am' && item.hour === 12)
|
|
6757
|
+
hour24 = 0;
|
|
6758
|
+
else if (item.meridiem === 'pm' && item.hour < 12)
|
|
6759
|
+
hour24 = item.hour + 12;
|
|
6760
|
+
const hour24Str = hour24.toString().padStart(2, '0');
|
|
6761
|
+
const minuteStr = item.minute.toString().padStart(2, '0');
|
|
6762
|
+
// Check if filterText matches 24-hour format variations
|
|
6763
|
+
const formats = [
|
|
6764
|
+
`${hour24Str}:${minuteStr}`, // "13:30"
|
|
6765
|
+
`${hour24Str}${minuteStr}`, // "1330"
|
|
6766
|
+
hour24Str, // "13"
|
|
6767
|
+
`${hour24}:${minuteStr}`, // "13:30" (without padding)
|
|
6768
|
+
hour24.toString(), // "13" (without padding)
|
|
6769
|
+
];
|
|
6770
|
+
return formats.some((format) => format.toLowerCase().includes(lowerFilterText) ||
|
|
6771
|
+
lowerFilterText.includes(format.toLowerCase()));
|
|
6772
|
+
};
|
|
6773
|
+
}, [timeOptions]);
|
|
6431
6774
|
const { collection, filter } = react.useListCollection({
|
|
6432
6775
|
initialItems: timeOptions,
|
|
6433
|
-
itemToString:
|
|
6776
|
+
itemToString: itemToString,
|
|
6434
6777
|
itemToValue: (item) => item.value,
|
|
6435
|
-
filter:
|
|
6778
|
+
filter: customTimeFilter,
|
|
6436
6779
|
});
|
|
6437
6780
|
// Get current value string for combobox
|
|
6438
6781
|
const currentValue = React.useMemo(() => {
|
|
@@ -6522,6 +6865,47 @@ const TimePicker$1 = ({ hour, setHour, minute, setMinute, meridiem, setMeridiem,
|
|
|
6522
6865
|
if (!trimmedValue) {
|
|
6523
6866
|
return;
|
|
6524
6867
|
}
|
|
6868
|
+
// Parse 24-hour format first (e.g., "13:30", "14:00", "1330", "1400", "9:05", "905")
|
|
6869
|
+
const timePattern24Hour = /^(\d{1,2}):?(\d{2})$/;
|
|
6870
|
+
const match24Hour = trimmedValue.match(timePattern24Hour);
|
|
6871
|
+
if (match24Hour) {
|
|
6872
|
+
const parsedHour24 = parseInt(match24Hour[1], 10);
|
|
6873
|
+
const parsedMinute = parseInt(match24Hour[2], 10);
|
|
6874
|
+
// Validate 24-hour format ranges
|
|
6875
|
+
if (parsedHour24 >= 0 &&
|
|
6876
|
+
parsedHour24 <= 23 &&
|
|
6877
|
+
parsedMinute >= 0 &&
|
|
6878
|
+
parsedMinute <= 59) {
|
|
6879
|
+
// Convert 24-hour to 12-hour format
|
|
6880
|
+
let hour12;
|
|
6881
|
+
let meridiem;
|
|
6882
|
+
if (parsedHour24 === 0) {
|
|
6883
|
+
hour12 = 12;
|
|
6884
|
+
meridiem = 'am';
|
|
6885
|
+
}
|
|
6886
|
+
else if (parsedHour24 === 12) {
|
|
6887
|
+
hour12 = 12;
|
|
6888
|
+
meridiem = 'pm';
|
|
6889
|
+
}
|
|
6890
|
+
else if (parsedHour24 > 12) {
|
|
6891
|
+
hour12 = parsedHour24 - 12;
|
|
6892
|
+
meridiem = 'pm';
|
|
6893
|
+
}
|
|
6894
|
+
else {
|
|
6895
|
+
hour12 = parsedHour24;
|
|
6896
|
+
meridiem = 'am';
|
|
6897
|
+
}
|
|
6898
|
+
setHour(hour12);
|
|
6899
|
+
setMinute(parsedMinute);
|
|
6900
|
+
setMeridiem(meridiem);
|
|
6901
|
+
onChange({
|
|
6902
|
+
hour: hour12,
|
|
6903
|
+
minute: parsedMinute,
|
|
6904
|
+
meridiem: meridiem,
|
|
6905
|
+
});
|
|
6906
|
+
return;
|
|
6907
|
+
}
|
|
6908
|
+
}
|
|
6525
6909
|
// Parse formats like "1:30 PM", "1:30PM", "1:30 pm", "1:30pm"
|
|
6526
6910
|
const timePattern12Hour = /^(\d{1,2}):(\d{1,2})\s*(am|pm|AM|PM)$/i;
|
|
6527
6911
|
const match12Hour = trimmedValue.match(timePattern12Hour);
|
|
@@ -6687,133 +7071,6 @@ const TimePicker = ({ column, schema, prefix }) => {
|
|
|
6687
7071
|
}, 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 }) }) }) }) }))] }) }));
|
|
6688
7072
|
};
|
|
6689
7073
|
|
|
6690
|
-
dayjs.extend(utc);
|
|
6691
|
-
dayjs.extend(timezone);
|
|
6692
|
-
dayjs.extend(customParseFormat);
|
|
6693
|
-
function DatePickerInput({ value, onChange, placeholder = 'Select a date', dateFormat = 'YYYY-MM-DD', displayFormat = 'YYYY-MM-DD', labels = {
|
|
6694
|
-
monthNamesShort: [
|
|
6695
|
-
'Jan',
|
|
6696
|
-
'Feb',
|
|
6697
|
-
'Mar',
|
|
6698
|
-
'Apr',
|
|
6699
|
-
'May',
|
|
6700
|
-
'Jun',
|
|
6701
|
-
'Jul',
|
|
6702
|
-
'Aug',
|
|
6703
|
-
'Sep',
|
|
6704
|
-
'Oct',
|
|
6705
|
-
'Nov',
|
|
6706
|
-
'Dec',
|
|
6707
|
-
],
|
|
6708
|
-
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
6709
|
-
backButtonLabel: 'Back',
|
|
6710
|
-
forwardButtonLabel: 'Next',
|
|
6711
|
-
}, timezone = 'Asia/Hong_Kong', minDate, maxDate, firstDayOfWeek, showOutsideDays, monthsToDisplay = 1, insideDialog = false, readOnly = false, }) {
|
|
6712
|
-
const [open, setOpen] = React.useState(false);
|
|
6713
|
-
const [inputValue, setInputValue] = React.useState('');
|
|
6714
|
-
// Update input value when prop value changes
|
|
6715
|
-
React.useEffect(() => {
|
|
6716
|
-
if (value) {
|
|
6717
|
-
const formatted = typeof value === 'string'
|
|
6718
|
-
? dayjs(value).tz(timezone).isValid()
|
|
6719
|
-
? dayjs(value).tz(timezone).format(displayFormat)
|
|
6720
|
-
: ''
|
|
6721
|
-
: dayjs(value).tz(timezone).format(displayFormat);
|
|
6722
|
-
setInputValue(formatted);
|
|
6723
|
-
}
|
|
6724
|
-
else {
|
|
6725
|
-
setInputValue('');
|
|
6726
|
-
}
|
|
6727
|
-
}, [value, displayFormat, timezone]);
|
|
6728
|
-
// Convert value to Date object for DatePicker
|
|
6729
|
-
const selectedDate = value
|
|
6730
|
-
? typeof value === 'string'
|
|
6731
|
-
? dayjs(value).tz(timezone).isValid()
|
|
6732
|
-
? dayjs(value).tz(timezone).toDate()
|
|
6733
|
-
: new Date()
|
|
6734
|
-
: value
|
|
6735
|
-
: new Date();
|
|
6736
|
-
// Shared function to parse and validate input value
|
|
6737
|
-
const parseAndValidateInput = (inputVal) => {
|
|
6738
|
-
// If empty, clear the value
|
|
6739
|
-
if (!inputVal.trim()) {
|
|
6740
|
-
onChange?.(undefined);
|
|
6741
|
-
setInputValue('');
|
|
6742
|
-
return;
|
|
6743
|
-
}
|
|
6744
|
-
// Try parsing with displayFormat first
|
|
6745
|
-
let parsedDate = dayjs(inputVal, displayFormat, true);
|
|
6746
|
-
// If that fails, try common date formats
|
|
6747
|
-
if (!parsedDate.isValid()) {
|
|
6748
|
-
parsedDate = dayjs(inputVal);
|
|
6749
|
-
}
|
|
6750
|
-
// If still invalid, try parsing with dateFormat
|
|
6751
|
-
if (!parsedDate.isValid()) {
|
|
6752
|
-
parsedDate = dayjs(inputVal, dateFormat, true);
|
|
6753
|
-
}
|
|
6754
|
-
// If valid, check constraints and update
|
|
6755
|
-
if (parsedDate.isValid()) {
|
|
6756
|
-
const dateObj = parsedDate.tz(timezone).toDate();
|
|
6757
|
-
// Check min/max constraints
|
|
6758
|
-
if (minDate && dateObj < minDate) {
|
|
6759
|
-
// Invalid: before minDate, reset to prop value
|
|
6760
|
-
resetToPropValue();
|
|
6761
|
-
return;
|
|
6762
|
-
}
|
|
6763
|
-
if (maxDate && dateObj > maxDate) {
|
|
6764
|
-
// Invalid: after maxDate, reset to prop value
|
|
6765
|
-
resetToPropValue();
|
|
6766
|
-
return;
|
|
6767
|
-
}
|
|
6768
|
-
// Valid date - format and update
|
|
6769
|
-
const formattedDate = parsedDate.tz(timezone).format(dateFormat);
|
|
6770
|
-
const formattedDisplay = parsedDate.tz(timezone).format(displayFormat);
|
|
6771
|
-
onChange?.(formattedDate);
|
|
6772
|
-
setInputValue(formattedDisplay);
|
|
6773
|
-
}
|
|
6774
|
-
else {
|
|
6775
|
-
// Invalid date - reset to prop value
|
|
6776
|
-
resetToPropValue();
|
|
6777
|
-
}
|
|
6778
|
-
};
|
|
6779
|
-
// Helper function to reset input to prop value
|
|
6780
|
-
const resetToPropValue = () => {
|
|
6781
|
-
if (value) {
|
|
6782
|
-
const formatted = typeof value === 'string'
|
|
6783
|
-
? dayjs(value).tz(timezone).isValid()
|
|
6784
|
-
? dayjs(value).tz(timezone).format(displayFormat)
|
|
6785
|
-
: ''
|
|
6786
|
-
: dayjs(value).tz(timezone).format(displayFormat);
|
|
6787
|
-
setInputValue(formatted);
|
|
6788
|
-
}
|
|
6789
|
-
else {
|
|
6790
|
-
setInputValue('');
|
|
6791
|
-
}
|
|
6792
|
-
};
|
|
6793
|
-
const handleInputChange = (e) => {
|
|
6794
|
-
// Only update the input value, don't parse yet
|
|
6795
|
-
setInputValue(e.target.value);
|
|
6796
|
-
};
|
|
6797
|
-
const handleInputBlur = () => {
|
|
6798
|
-
// Parse and validate when input loses focus
|
|
6799
|
-
parseAndValidateInput(inputValue);
|
|
6800
|
-
};
|
|
6801
|
-
const handleKeyDown = (e) => {
|
|
6802
|
-
// Parse and validate when Enter is pressed
|
|
6803
|
-
if (e.key === 'Enter') {
|
|
6804
|
-
e.preventDefault();
|
|
6805
|
-
parseAndValidateInput(inputValue);
|
|
6806
|
-
}
|
|
6807
|
-
};
|
|
6808
|
-
const handleDateSelected = ({ date }) => {
|
|
6809
|
-
const formattedDate = dayjs(date).tz(timezone).format(dateFormat);
|
|
6810
|
-
onChange?.(formattedDate);
|
|
6811
|
-
setOpen(false);
|
|
6812
|
-
};
|
|
6813
|
-
const datePickerContent = (jsxRuntime.jsx(DatePicker$1, { selected: selectedDate, onDateSelected: handleDateSelected, labels: labels, minDate: minDate, maxDate: maxDate, firstDayOfWeek: firstDayOfWeek, showOutsideDays: showOutsideDays, monthsToDisplay: monthsToDisplay }));
|
|
6814
|
-
return (jsxRuntime.jsxs(react.Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, autoFocus: false, children: [jsxRuntime.jsx(InputGroup, { endElement: jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsx(react.IconButton, { variant: "ghost", size: "2xs", "aria-label": "Open calendar", onClick: () => setOpen(true), children: jsxRuntime.jsx(react.Icon, { children: jsxRuntime.jsx(md.MdDateRange, {}) }) }) }), children: jsxRuntime.jsx(react.Input, { value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onKeyDown: handleKeyDown, placeholder: placeholder, readOnly: readOnly }) }), insideDialog ? (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { children: datePickerContent }) }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { children: datePickerContent }) }) }) }))] }));
|
|
6815
|
-
}
|
|
6816
|
-
|
|
6817
7074
|
dayjs.extend(utc);
|
|
6818
7075
|
dayjs.extend(timezone);
|
|
6819
7076
|
function IsoTimePicker({ hour, setHour, minute, setMinute, second, setSecond,
|
|
@@ -7057,7 +7314,20 @@ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Ko
|
|
|
7057
7314
|
});
|
|
7058
7315
|
}
|
|
7059
7316
|
};
|
|
7317
|
+
const [inputValue, setInputValue] = React.useState('');
|
|
7318
|
+
// Sync inputValue with currentValue when time changes externally
|
|
7319
|
+
React.useEffect(() => {
|
|
7320
|
+
if (hour !== null && minute !== null && second !== null) {
|
|
7321
|
+
const formattedValue = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second.toString().padStart(2, '0')}`;
|
|
7322
|
+
setInputValue(formattedValue);
|
|
7323
|
+
}
|
|
7324
|
+
else {
|
|
7325
|
+
setInputValue('');
|
|
7326
|
+
}
|
|
7327
|
+
}, [hour, minute, second]);
|
|
7060
7328
|
const handleInputValueChange = (details) => {
|
|
7329
|
+
// Update local input value state
|
|
7330
|
+
setInputValue(details.inputValue);
|
|
7061
7331
|
// Filter the collection based on input, but don't parse yet
|
|
7062
7332
|
filter(details.inputValue);
|
|
7063
7333
|
};
|
|
@@ -7067,24 +7337,26 @@ onChange = (_newValue) => { }, startTime, selectedDate, timezone = 'Asia/Hong_Ko
|
|
|
7067
7337
|
};
|
|
7068
7338
|
const handleBlur = (e) => {
|
|
7069
7339
|
// Parse and commit the input value when losing focus
|
|
7070
|
-
const
|
|
7071
|
-
|
|
7072
|
-
|
|
7340
|
+
const inputVal = e.target.value;
|
|
7341
|
+
setInputValue(inputVal);
|
|
7342
|
+
if (inputVal) {
|
|
7343
|
+
parseAndCommitInput(inputVal);
|
|
7073
7344
|
}
|
|
7074
7345
|
};
|
|
7075
7346
|
const handleKeyDown = (e) => {
|
|
7076
7347
|
// Commit input on Enter key
|
|
7077
7348
|
if (e.key === 'Enter') {
|
|
7078
7349
|
e.preventDefault();
|
|
7079
|
-
const
|
|
7080
|
-
|
|
7081
|
-
|
|
7350
|
+
const inputVal = e.currentTarget.value;
|
|
7351
|
+
setInputValue(inputVal);
|
|
7352
|
+
if (inputVal) {
|
|
7353
|
+
parseAndCommitInput(inputVal);
|
|
7082
7354
|
}
|
|
7083
7355
|
// Blur the input
|
|
7084
7356
|
e.currentTarget?.blur();
|
|
7085
7357
|
}
|
|
7086
7358
|
};
|
|
7087
|
-
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, 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, {}) }) })] }) }));
|
|
7359
|
+
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, {}) }) })] }) }));
|
|
7088
7360
|
}
|
|
7089
7361
|
|
|
7090
7362
|
dayjs.extend(utc);
|
|
@@ -7107,7 +7379,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7107
7379
|
weekdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
7108
7380
|
backButtonLabel: 'Back',
|
|
7109
7381
|
forwardButtonLabel: 'Next',
|
|
7110
|
-
}, timePickerLabels, timezone = 'Asia/Hong_Kong', startTime, minDate, maxDate, portalled = false, }) {
|
|
7382
|
+
}, timePickerLabels, timezone = 'Asia/Hong_Kong', startTime, minDate, maxDate, portalled = false, defaultDate, defaultTime, }) {
|
|
7111
7383
|
console.log('[DateTimePicker] Component initialized with props:', {
|
|
7112
7384
|
value,
|
|
7113
7385
|
format,
|
|
@@ -7182,13 +7454,77 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7182
7454
|
value,
|
|
7183
7455
|
initialTime,
|
|
7184
7456
|
});
|
|
7457
|
+
// Normalize startTime to ignore milliseconds (needed for effectiveDefaultDate calculation)
|
|
7458
|
+
const normalizedStartTime = startTime
|
|
7459
|
+
? dayjs(startTime).tz(timezone).millisecond(0).toISOString()
|
|
7460
|
+
: undefined;
|
|
7461
|
+
// Calculate effective defaultDate: use prop if provided, otherwise use startTime date, otherwise use today
|
|
7462
|
+
const effectiveDefaultDate = React.useMemo(() => {
|
|
7463
|
+
if (defaultDate) {
|
|
7464
|
+
return defaultDate;
|
|
7465
|
+
}
|
|
7466
|
+
if (normalizedStartTime &&
|
|
7467
|
+
dayjs(normalizedStartTime).tz(timezone).isValid()) {
|
|
7468
|
+
return dayjs(normalizedStartTime).tz(timezone).format('YYYY-MM-DD');
|
|
7469
|
+
}
|
|
7470
|
+
return dayjs().tz(timezone).format('YYYY-MM-DD');
|
|
7471
|
+
}, [defaultDate, normalizedStartTime, timezone]);
|
|
7472
|
+
// Initialize time with default values if no value is provided
|
|
7473
|
+
const getInitialTimeValues = () => {
|
|
7474
|
+
if (value && initialTime.hour12 !== null) {
|
|
7475
|
+
return initialTime;
|
|
7476
|
+
}
|
|
7477
|
+
// If no value or no time in value, use defaultTime or 00:00
|
|
7478
|
+
if (defaultTime) {
|
|
7479
|
+
if (format === 'iso-date-time') {
|
|
7480
|
+
const defaultTime24 = defaultTime;
|
|
7481
|
+
return {
|
|
7482
|
+
hour12: null,
|
|
7483
|
+
minute: defaultTime24.minute ?? 0,
|
|
7484
|
+
meridiem: null,
|
|
7485
|
+
hour24: defaultTime24.hour ?? 0,
|
|
7486
|
+
second: showSeconds ? defaultTime24.second ?? 0 : null,
|
|
7487
|
+
};
|
|
7488
|
+
}
|
|
7489
|
+
else {
|
|
7490
|
+
const defaultTime12 = defaultTime;
|
|
7491
|
+
return {
|
|
7492
|
+
hour12: defaultTime12.hour ?? 12,
|
|
7493
|
+
minute: defaultTime12.minute ?? 0,
|
|
7494
|
+
meridiem: defaultTime12.meridiem ?? 'am',
|
|
7495
|
+
hour24: null,
|
|
7496
|
+
second: null,
|
|
7497
|
+
};
|
|
7498
|
+
}
|
|
7499
|
+
}
|
|
7500
|
+
// Default to 00:00
|
|
7501
|
+
if (format === 'iso-date-time') {
|
|
7502
|
+
return {
|
|
7503
|
+
hour12: null,
|
|
7504
|
+
minute: 0,
|
|
7505
|
+
meridiem: null,
|
|
7506
|
+
hour24: 0,
|
|
7507
|
+
second: showSeconds ? 0 : null,
|
|
7508
|
+
};
|
|
7509
|
+
}
|
|
7510
|
+
else {
|
|
7511
|
+
return {
|
|
7512
|
+
hour12: 12,
|
|
7513
|
+
minute: 0,
|
|
7514
|
+
meridiem: 'am',
|
|
7515
|
+
hour24: null,
|
|
7516
|
+
second: null,
|
|
7517
|
+
};
|
|
7518
|
+
}
|
|
7519
|
+
};
|
|
7520
|
+
const initialTimeValues = getInitialTimeValues();
|
|
7185
7521
|
// Time state for 12-hour format
|
|
7186
|
-
const [hour12, setHour12] = React.useState(
|
|
7187
|
-
const [minute, setMinute] = React.useState(
|
|
7188
|
-
const [meridiem, setMeridiem] = React.useState(
|
|
7522
|
+
const [hour12, setHour12] = React.useState(initialTimeValues.hour12);
|
|
7523
|
+
const [minute, setMinute] = React.useState(initialTimeValues.minute);
|
|
7524
|
+
const [meridiem, setMeridiem] = React.useState(initialTimeValues.meridiem);
|
|
7189
7525
|
// Time state for 24-hour format
|
|
7190
|
-
const [hour24, setHour24] = React.useState(
|
|
7191
|
-
const [second, setSecond] = React.useState(
|
|
7526
|
+
const [hour24, setHour24] = React.useState(initialTimeValues.hour24);
|
|
7527
|
+
const [second, setSecond] = React.useState(initialTimeValues.second);
|
|
7192
7528
|
// Sync selectedDate and time states when value prop changes
|
|
7193
7529
|
React.useEffect(() => {
|
|
7194
7530
|
console.log('[DateTimePicker] useEffect triggered - value changed:', {
|
|
@@ -7196,27 +7532,47 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7196
7532
|
timezone,
|
|
7197
7533
|
format,
|
|
7198
7534
|
});
|
|
7199
|
-
// If value is null, undefined, or invalid, clear
|
|
7535
|
+
// If value is null, undefined, or invalid, clear date but keep default time values
|
|
7200
7536
|
if (!value || value === null || value === undefined) {
|
|
7201
|
-
console.log('[DateTimePicker] Value is null/undefined, clearing
|
|
7537
|
+
console.log('[DateTimePicker] Value is null/undefined, clearing date but keeping default time');
|
|
7202
7538
|
setSelectedDate('');
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7539
|
+
// Keep default time values instead of clearing them
|
|
7540
|
+
if (format === 'iso-date-time') {
|
|
7541
|
+
setHour24(defaultTime ? defaultTime.hour ?? 0 : 0);
|
|
7542
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7543
|
+
setSecond(showSeconds
|
|
7544
|
+
? defaultTime
|
|
7545
|
+
? defaultTime.second ?? 0
|
|
7546
|
+
: 0
|
|
7547
|
+
: null);
|
|
7548
|
+
}
|
|
7549
|
+
else {
|
|
7550
|
+
setHour12(defaultTime ? defaultTime.hour ?? 12 : 12);
|
|
7551
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7552
|
+
setMeridiem(defaultTime ? defaultTime.meridiem ?? 'am' : 'am');
|
|
7553
|
+
}
|
|
7208
7554
|
return;
|
|
7209
7555
|
}
|
|
7210
7556
|
// Check if value is valid
|
|
7211
7557
|
const dateObj = dayjs(value).tz(timezone);
|
|
7212
7558
|
if (!dateObj.isValid()) {
|
|
7213
|
-
console.log('[DateTimePicker] Invalid value, clearing
|
|
7559
|
+
console.log('[DateTimePicker] Invalid value, clearing date but keeping default time');
|
|
7214
7560
|
setSelectedDate('');
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7561
|
+
// Keep default time values instead of clearing them
|
|
7562
|
+
if (format === 'iso-date-time') {
|
|
7563
|
+
setHour24(defaultTime ? defaultTime.hour ?? 0 : 0);
|
|
7564
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7565
|
+
setSecond(showSeconds
|
|
7566
|
+
? defaultTime
|
|
7567
|
+
? defaultTime.second ?? 0
|
|
7568
|
+
: 0
|
|
7569
|
+
: null);
|
|
7570
|
+
}
|
|
7571
|
+
else {
|
|
7572
|
+
setHour12(defaultTime ? defaultTime.hour ?? 12 : 12);
|
|
7573
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7574
|
+
setMeridiem(defaultTime ? defaultTime.meridiem ?? 'am' : 'am');
|
|
7575
|
+
}
|
|
7220
7576
|
return;
|
|
7221
7577
|
}
|
|
7222
7578
|
const dateString = getDateString(value);
|
|
@@ -7272,16 +7628,76 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7272
7628
|
onChange?.(undefined);
|
|
7273
7629
|
return;
|
|
7274
7630
|
}
|
|
7631
|
+
// Check if time values are null - if so, use defaultTime or set to 00:00
|
|
7632
|
+
const hasTimeValues = format === 'iso-date-time'
|
|
7633
|
+
? hour24 !== null || minute !== null
|
|
7634
|
+
: hour12 !== null || minute !== null || meridiem !== null;
|
|
7635
|
+
let timeDataToUse = undefined;
|
|
7636
|
+
if (!hasTimeValues) {
|
|
7637
|
+
// Use defaultTime if provided, otherwise default to 00:00
|
|
7638
|
+
if (defaultTime) {
|
|
7639
|
+
console.log('[DateTimePicker] No time values set, using defaultTime');
|
|
7640
|
+
if (format === 'iso-date-time') {
|
|
7641
|
+
const defaultTime24 = defaultTime;
|
|
7642
|
+
setHour24(defaultTime24.hour ?? 0);
|
|
7643
|
+
setMinute(defaultTime24.minute ?? 0);
|
|
7644
|
+
if (showSeconds) {
|
|
7645
|
+
setSecond(defaultTime24.second ?? 0);
|
|
7646
|
+
}
|
|
7647
|
+
timeDataToUse = {
|
|
7648
|
+
hour: defaultTime24.hour ?? 0,
|
|
7649
|
+
minute: defaultTime24.minute ?? 0,
|
|
7650
|
+
second: showSeconds ? defaultTime24.second ?? 0 : undefined,
|
|
7651
|
+
};
|
|
7652
|
+
}
|
|
7653
|
+
else {
|
|
7654
|
+
const defaultTime12 = defaultTime;
|
|
7655
|
+
setHour12(defaultTime12.hour ?? 12);
|
|
7656
|
+
setMinute(defaultTime12.minute ?? 0);
|
|
7657
|
+
setMeridiem(defaultTime12.meridiem ?? 'am');
|
|
7658
|
+
timeDataToUse = {
|
|
7659
|
+
hour: defaultTime12.hour ?? 12,
|
|
7660
|
+
minute: defaultTime12.minute ?? 0,
|
|
7661
|
+
meridiem: defaultTime12.meridiem ?? 'am',
|
|
7662
|
+
};
|
|
7663
|
+
}
|
|
7664
|
+
}
|
|
7665
|
+
else {
|
|
7666
|
+
console.log('[DateTimePicker] No time values set, defaulting to 00:00');
|
|
7667
|
+
if (format === 'iso-date-time') {
|
|
7668
|
+
setHour24(0);
|
|
7669
|
+
setMinute(0);
|
|
7670
|
+
if (showSeconds) {
|
|
7671
|
+
setSecond(0);
|
|
7672
|
+
}
|
|
7673
|
+
timeDataToUse = {
|
|
7674
|
+
hour: 0,
|
|
7675
|
+
minute: 0,
|
|
7676
|
+
second: showSeconds ? 0 : undefined,
|
|
7677
|
+
};
|
|
7678
|
+
}
|
|
7679
|
+
else {
|
|
7680
|
+
setHour12(12);
|
|
7681
|
+
setMinute(0);
|
|
7682
|
+
setMeridiem('am');
|
|
7683
|
+
timeDataToUse = {
|
|
7684
|
+
hour: 12,
|
|
7685
|
+
minute: 0,
|
|
7686
|
+
meridiem: 'am',
|
|
7687
|
+
};
|
|
7688
|
+
}
|
|
7689
|
+
}
|
|
7690
|
+
}
|
|
7275
7691
|
// When showSeconds is false, ignore seconds from the date
|
|
7276
7692
|
if (!showSeconds) {
|
|
7277
7693
|
const dateWithoutSeconds = dateObj.second(0).millisecond(0).toISOString();
|
|
7278
7694
|
console.log('[DateTimePicker] Updating date without seconds:', dateWithoutSeconds);
|
|
7279
|
-
updateDateTime(dateWithoutSeconds);
|
|
7695
|
+
updateDateTime(dateWithoutSeconds, timeDataToUse);
|
|
7280
7696
|
}
|
|
7281
7697
|
else {
|
|
7282
7698
|
const dateWithSeconds = dateObj.toISOString();
|
|
7283
7699
|
console.log('[DateTimePicker] Updating date with seconds:', dateWithSeconds);
|
|
7284
|
-
updateDateTime(dateWithSeconds);
|
|
7700
|
+
updateDateTime(dateWithSeconds, timeDataToUse);
|
|
7285
7701
|
}
|
|
7286
7702
|
};
|
|
7287
7703
|
const handleTimeChange = (timeData) => {
|
|
@@ -7311,17 +7727,36 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7311
7727
|
setMinute(data.minute);
|
|
7312
7728
|
setMeridiem(data.meridiem);
|
|
7313
7729
|
}
|
|
7314
|
-
// Use selectedDate if valid, otherwise clear all fields
|
|
7730
|
+
// Use selectedDate if valid, otherwise use effectiveDefaultDate or clear all fields
|
|
7315
7731
|
if (!selectedDate || !dayjs(selectedDate).isValid()) {
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7732
|
+
// If effectiveDefaultDate is available, use it instead of clearing
|
|
7733
|
+
if (effectiveDefaultDate && dayjs(effectiveDefaultDate).isValid()) {
|
|
7734
|
+
console.log('[DateTimePicker] No valid selectedDate, using effectiveDefaultDate:', effectiveDefaultDate);
|
|
7735
|
+
setSelectedDate(effectiveDefaultDate);
|
|
7736
|
+
const dateObj = dayjs(effectiveDefaultDate).tz(timezone);
|
|
7737
|
+
if (dateObj.isValid()) {
|
|
7738
|
+
updateDateTime(dateObj.toISOString(), timeData);
|
|
7739
|
+
}
|
|
7740
|
+
else {
|
|
7741
|
+
console.warn('[DateTimePicker] Invalid effectiveDefaultDate, clearing fields');
|
|
7742
|
+
setSelectedDate('');
|
|
7743
|
+
setHour12(null);
|
|
7744
|
+
setMinute(null);
|
|
7745
|
+
setMeridiem(null);
|
|
7746
|
+
setHour24(null);
|
|
7747
|
+
setSecond(null);
|
|
7748
|
+
onChange?.(undefined);
|
|
7749
|
+
}
|
|
7750
|
+
return;
|
|
7751
|
+
}
|
|
7752
|
+
else {
|
|
7753
|
+
console.log('[DateTimePicker] No valid selectedDate and no effectiveDefaultDate, keeping time values but no date');
|
|
7754
|
+
// Keep the time values that were just set, but don't set a date
|
|
7755
|
+
// This should rarely happen as effectiveDefaultDate always defaults to today
|
|
7756
|
+
setSelectedDate('');
|
|
7757
|
+
onChange?.(undefined);
|
|
7758
|
+
return;
|
|
7759
|
+
}
|
|
7325
7760
|
}
|
|
7326
7761
|
const dateObj = dayjs(selectedDate).tz(timezone);
|
|
7327
7762
|
if (dateObj.isValid()) {
|
|
@@ -7464,18 +7899,24 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7464
7899
|
};
|
|
7465
7900
|
const handleClear = () => {
|
|
7466
7901
|
setSelectedDate('');
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7902
|
+
// Reset to default time values instead of clearing them
|
|
7903
|
+
if (format === 'iso-date-time') {
|
|
7904
|
+
setHour24(defaultTime ? defaultTime.hour ?? 0 : 0);
|
|
7905
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7906
|
+
setSecond(showSeconds
|
|
7907
|
+
? defaultTime
|
|
7908
|
+
? defaultTime.second ?? 0
|
|
7909
|
+
: 0
|
|
7910
|
+
: null);
|
|
7911
|
+
}
|
|
7912
|
+
else {
|
|
7913
|
+
setHour12(defaultTime ? defaultTime.hour ?? 12 : 12);
|
|
7914
|
+
setMinute(defaultTime ? defaultTime.minute ?? 0 : 0);
|
|
7915
|
+
setMeridiem(defaultTime ? defaultTime.meridiem ?? 'am' : 'am');
|
|
7916
|
+
}
|
|
7472
7917
|
onChange?.(undefined);
|
|
7473
7918
|
};
|
|
7474
7919
|
const isISO = format === 'iso-date-time';
|
|
7475
|
-
// Normalize startTime to ignore milliseconds
|
|
7476
|
-
const normalizedStartTime = startTime
|
|
7477
|
-
? dayjs(startTime).tz(timezone).millisecond(0).toISOString()
|
|
7478
|
-
: undefined;
|
|
7479
7920
|
// Determine minDate: prioritize explicit minDate prop, then fall back to startTime
|
|
7480
7921
|
const effectiveMinDate = minDate
|
|
7481
7922
|
? minDate
|
|
@@ -7553,7 +7994,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7553
7994
|
const dateObj = dayjs.tz(selectedDate, timezone);
|
|
7554
7995
|
return dateObj.isValid() ? dateObj.format('Z') : null;
|
|
7555
7996
|
}, [selectedDate, timezone]);
|
|
7556
|
-
return (jsxRuntime.jsxs(react.Flex, { direction: "column", gap:
|
|
7997
|
+
return (jsxRuntime.jsxs(react.Flex, { direction: "column", gap: 2, children: [jsxRuntime.jsx(DatePickerInput, { value: selectedDate || undefined, onChange: (date) => {
|
|
7557
7998
|
if (date) {
|
|
7558
7999
|
handleDateChange(date);
|
|
7559
8000
|
}
|
|
@@ -7561,7 +8002,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7561
8002
|
setSelectedDate('');
|
|
7562
8003
|
onChange?.(undefined);
|
|
7563
8004
|
}
|
|
7564
|
-
}, placeholder: "Select a date", dateFormat: "YYYY-MM-DD", displayFormat: "YYYY-MM-DD", labels: labels, timezone: timezone, minDate: effectiveMinDate, maxDate: maxDate, monthsToDisplay: 1, readOnly:
|
|
8005
|
+
}, 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 })] }))] }));
|
|
7565
8006
|
}
|
|
7566
8007
|
|
|
7567
8008
|
dayjs.extend(utc);
|
|
@@ -7623,7 +8064,7 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
|
|
|
7623
8064
|
return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
7624
8065
|
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: () => {
|
|
7625
8066
|
setOpen(true);
|
|
7626
|
-
}, justifyContent: 'start', children: [jsxRuntime.jsx(md.MdDateRange, {}), displayDate || ''] }) }), insideDialog ? (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "
|
|
8067
|
+
}, 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 }) }) }) }))] }) }));
|
|
7627
8068
|
};
|
|
7628
8069
|
|
|
7629
8070
|
const SchemaRenderer = ({ schema, prefix, column, }) => {
|
|
@@ -7776,15 +8217,15 @@ const DateViewer = ({ column, schema, prefix }) => {
|
|
|
7776
8217
|
|
|
7777
8218
|
const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
|
|
7778
8219
|
const { watch, formState: { errors }, } = reactHookForm.useFormContext();
|
|
7779
|
-
const formI18n = useFormI18n(column, prefix);
|
|
8220
|
+
const formI18n = useFormI18n(column, prefix, schema);
|
|
7780
8221
|
const { required } = schema;
|
|
7781
8222
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
7782
|
-
const { gridColumn =
|
|
8223
|
+
const { gridColumn = 'span 12', gridRow = 'span 1', renderDisplay } = schema;
|
|
7783
8224
|
const colLabel = formI18n.colLabel;
|
|
7784
8225
|
const watchEnum = watch(colLabel);
|
|
7785
8226
|
const watchEnums = (watch(colLabel) ?? []);
|
|
7786
|
-
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems:
|
|
7787
|
-
gridRow, children: [isMultiple && (jsxRuntime.jsx(react.Flex, { flexFlow:
|
|
8227
|
+
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
8228
|
+
gridRow, children: [isMultiple && (jsxRuntime.jsx(react.Flex, { flexFlow: 'wrap', gap: 1, children: watchEnums.map((enumValue) => {
|
|
7788
8229
|
const item = enumValue;
|
|
7789
8230
|
if (item === undefined) {
|
|
7790
8231
|
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: "undefined" });
|
|
@@ -7792,7 +8233,7 @@ const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
|
|
|
7792
8233
|
return (jsxRuntime.jsx(Tag, { size: "lg", children: !!renderDisplay === true
|
|
7793
8234
|
? renderDisplay(item)
|
|
7794
8235
|
: formI18n.t(item) }, item));
|
|
7795
|
-
}) })), !isMultiple && jsxRuntime.jsx(react.Text, { children: formI18n.t(watchEnum) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color:
|
|
8236
|
+
}) })), !isMultiple && jsxRuntime.jsx(react.Text, { children: formI18n.t(watchEnum) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: 'red.400', children: formI18n.required() }))] }));
|
|
7796
8237
|
};
|
|
7797
8238
|
|
|
7798
8239
|
const FileViewer = ({ column, schema, prefix }) => {
|
|
@@ -8205,6 +8646,17 @@ const FormBody = () => {
|
|
|
8205
8646
|
|
|
8206
8647
|
const FormTitle = () => {
|
|
8207
8648
|
const { schema } = useSchemaContext();
|
|
8649
|
+
// Debug log when form title is missing
|
|
8650
|
+
if (!schema.title) {
|
|
8651
|
+
console.debug('[Form Title] Missing title in root schema. Add title property to schema.', {
|
|
8652
|
+
schema: {
|
|
8653
|
+
type: schema.type,
|
|
8654
|
+
properties: schema.properties
|
|
8655
|
+
? Object.keys(schema.properties)
|
|
8656
|
+
: undefined,
|
|
8657
|
+
},
|
|
8658
|
+
});
|
|
8659
|
+
}
|
|
8208
8660
|
return jsxRuntime.jsx(react.Heading, { children: schema.title ?? 'Form' });
|
|
8209
8661
|
};
|
|
8210
8662
|
|