@bsol-oss/react-datatable5 13.0.1-beta.6 → 13.0.1-beta.8
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 +1 -0
- package/dist/index.js +294 -154
- package/dist/index.mjs +296 -156
- package/dist/types/components/Form/components/fields/useIdPickerData.d.ts +4 -0
- package/dist/types/components/Form/components/types/CustomJSONSchema7.d.ts +1 -0
- package/dist/types/components/Form/utils/useFormI18n.d.ts +14 -30
- package/package.json +7 -4
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, CheckboxCard as CheckboxCard$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, Tooltip as Tooltip$1, Group, InputElement, Icon, EmptyState as EmptyState$2, VStack, List, Table as Table$1, Checkbox as Checkbox$1, Card, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Clipboard, Badge, Link, Tag as Tag$1, Image, Alert, Field as Field$1, Popover, useFilter, useListCollection, Combobox, Tabs, Skeleton, NumberInput,
|
|
2
|
+
import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, CheckboxCard as CheckboxCard$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, Tooltip as Tooltip$1, Group, InputElement, Icon, EmptyState as EmptyState$2, VStack, List, Table as Table$1, Checkbox as Checkbox$1, Card, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Clipboard, Badge, Link, Tag as Tag$1, Image, Alert, Field as Field$1, Popover, useFilter, useListCollection, Combobox, Tabs, useCombobox, Show, Skeleton, NumberInput, RadioCard, CheckboxGroup, InputGroup as InputGroup$1, Center, Heading, Stack } from '@chakra-ui/react';
|
|
3
3
|
import { AiOutlineColumnWidth } from 'react-icons/ai';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { createContext, useContext, useState, useMemo, useCallback, useEffect, useRef, forwardRef } from 'react';
|
|
6
6
|
import { LuX, LuCheck, LuChevronRight, LuCopy, LuExternalLink, LuSearch, LuImage, LuFile } from 'react-icons/lu';
|
|
7
7
|
import { MdOutlineSort, MdFilterAlt, MdSearch, MdOutlineChecklist, MdClear, MdOutlineViewColumn, MdFilterListAlt, MdPushPin, MdCancel, MdDateRange } from 'react-icons/md';
|
|
8
8
|
import { FaUpDown, FaGripLinesVertical, FaTrash } from 'react-icons/fa6';
|
|
9
|
-
import { BiDownArrow, BiUpArrow, BiError } from 'react-icons/bi';
|
|
9
|
+
import { BiDownArrow, BiUpArrow, BiX, BiError } from 'react-icons/bi';
|
|
10
10
|
import { CgClose, CgTrash } from 'react-icons/cg';
|
|
11
11
|
import { HiMiniEllipsisHorizontal, HiChevronLeft, HiChevronRight } from 'react-icons/hi2';
|
|
12
12
|
import { IoMdEye, IoMdCheckbox, IoMdClock } from 'react-icons/io';
|
|
@@ -4373,39 +4373,31 @@ const FormRoot = ({ schema, idMap, setIdMap, form, translate, children, order =
|
|
|
4373
4373
|
}, children: jsx(FormProvider, { ...form, children: children }) }));
|
|
4374
4374
|
};
|
|
4375
4375
|
|
|
4376
|
-
function removeIndex(str) {
|
|
4377
|
-
return str.replace(/\.\d+\./g, ".");
|
|
4378
|
-
}
|
|
4379
|
-
|
|
4380
4376
|
/**
|
|
4381
|
-
* Custom hook for form field labels
|
|
4382
|
-
* Automatically handles colLabel construction
|
|
4383
|
-
* Uses schema.title
|
|
4377
|
+
* Custom hook for form field labels.
|
|
4378
|
+
* Automatically handles colLabel construction.
|
|
4379
|
+
* Uses schema.title for labels and schema.errorMessages for error messages.
|
|
4384
4380
|
*
|
|
4385
4381
|
* @param column - The column name
|
|
4386
4382
|
* @param prefix - The prefix for the field (usually empty string or parent path)
|
|
4387
|
-
* @param schema - Required schema object with title
|
|
4383
|
+
* @param schema - Required schema object with title and errorMessages properties
|
|
4388
4384
|
* @returns Object with label helper functions
|
|
4389
4385
|
*
|
|
4390
4386
|
* @example
|
|
4391
4387
|
* ```tsx
|
|
4392
4388
|
* const formI18n = useFormI18n(column, prefix, schema);
|
|
4393
4389
|
*
|
|
4394
|
-
* // Get field label (
|
|
4390
|
+
* // Get field label (from schema.title)
|
|
4395
4391
|
* <Field label={formI18n.label()} />
|
|
4396
4392
|
*
|
|
4397
|
-
* // Get required error message
|
|
4393
|
+
* // Get required error message (from schema.errorMessages?.required)
|
|
4398
4394
|
* <Text>{formI18n.required()}</Text>
|
|
4399
4395
|
*
|
|
4400
|
-
* // Get custom text
|
|
4401
|
-
* <Text>{formI18n.t('add_more')}</Text>
|
|
4402
|
-
*
|
|
4403
4396
|
* // Access the raw colLabel
|
|
4404
4397
|
* const colLabel = formI18n.colLabel;
|
|
4405
4398
|
* ```
|
|
4406
4399
|
*/
|
|
4407
4400
|
const useFormI18n = (column, prefix = '', schema) => {
|
|
4408
|
-
const { translate } = useSchemaContext();
|
|
4409
4401
|
const colLabel = `${prefix}${column}`;
|
|
4410
4402
|
return {
|
|
4411
4403
|
/**
|
|
@@ -4413,10 +4405,10 @@ const useFormI18n = (column, prefix = '', schema) => {
|
|
|
4413
4405
|
*/
|
|
4414
4406
|
colLabel,
|
|
4415
4407
|
/**
|
|
4416
|
-
* Get the field label from schema title
|
|
4417
|
-
*
|
|
4408
|
+
* Get the field label from schema title property.
|
|
4409
|
+
* Logs a debug message if title is missing.
|
|
4418
4410
|
*/
|
|
4419
|
-
label: (
|
|
4411
|
+
label: () => {
|
|
4420
4412
|
if (schema.title) {
|
|
4421
4413
|
return schema.title;
|
|
4422
4414
|
}
|
|
@@ -4432,29 +4424,36 @@ const useFormI18n = (column, prefix = '', schema) => {
|
|
|
4432
4424
|
: undefined,
|
|
4433
4425
|
},
|
|
4434
4426
|
});
|
|
4435
|
-
|
|
4427
|
+
// Return column name as fallback
|
|
4428
|
+
return column;
|
|
4436
4429
|
},
|
|
4437
4430
|
/**
|
|
4438
|
-
* Get the required error message
|
|
4439
|
-
*
|
|
4431
|
+
* Get the required error message from schema.errorMessages?.required.
|
|
4432
|
+
* Returns a helpful fallback message if not provided.
|
|
4440
4433
|
*/
|
|
4441
|
-
required: (
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4434
|
+
required: () => {
|
|
4435
|
+
const errorMessage = schema.errorMessages?.required;
|
|
4436
|
+
if (errorMessage) {
|
|
4437
|
+
return errorMessage;
|
|
4438
|
+
}
|
|
4439
|
+
// Debug log when error message is missing
|
|
4440
|
+
console.debug(`[Form Field Required] Missing error message for required field '${colLabel}'. Add errorMessages.required to schema for field '${colLabel}'.`, {
|
|
4441
|
+
fieldName: column,
|
|
4442
|
+
colLabel,
|
|
4443
|
+
prefix,
|
|
4444
|
+
schema: {
|
|
4445
|
+
type: schema.type,
|
|
4446
|
+
title: schema.title,
|
|
4447
|
+
required: schema.required,
|
|
4448
|
+
hasErrorMessages: !!schema.errorMessages,
|
|
4449
|
+
errorMessageKeys: schema.errorMessages
|
|
4450
|
+
? Object.keys(schema.errorMessages)
|
|
4451
|
+
: undefined,
|
|
4452
|
+
},
|
|
4453
|
+
});
|
|
4454
|
+
// Return helpful fallback message
|
|
4455
|
+
return `Missing error message for required. Add errorMessages.required to schema for field '${colLabel}'`;
|
|
4453
4456
|
},
|
|
4454
|
-
/**
|
|
4455
|
-
* Access to the original translate object for edge cases
|
|
4456
|
-
*/
|
|
4457
|
-
translate,
|
|
4458
4457
|
};
|
|
4459
4458
|
};
|
|
4460
4459
|
|
|
@@ -5070,27 +5069,82 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
5070
5069
|
const watchEnum = watch(colLabel);
|
|
5071
5070
|
const watchEnums = (watch(colLabel) ?? []);
|
|
5072
5071
|
const dataList = schema.enum ?? [];
|
|
5072
|
+
// Helper function to render enum value (returns ReactNode)
|
|
5073
|
+
// If renderDisplay is provided, use it; otherwise show the enum string value directly
|
|
5074
|
+
const renderEnumValue = (value) => {
|
|
5075
|
+
if (renderDisplay) {
|
|
5076
|
+
return renderDisplay(value);
|
|
5077
|
+
}
|
|
5078
|
+
// If no renderDisplay provided, show the enum string value directly
|
|
5079
|
+
return value;
|
|
5080
|
+
};
|
|
5081
|
+
// Helper function to get string representation for input display
|
|
5082
|
+
// Converts ReactNode to string for combobox input display
|
|
5083
|
+
const getDisplayString = (value) => {
|
|
5084
|
+
if (renderDisplay) {
|
|
5085
|
+
const rendered = renderDisplay(value);
|
|
5086
|
+
// If renderDisplay returns a string, use it directly
|
|
5087
|
+
if (typeof rendered === 'string') {
|
|
5088
|
+
return rendered;
|
|
5089
|
+
}
|
|
5090
|
+
// If it's a React element, try to extract text content
|
|
5091
|
+
// For now, fallback to the raw value if we can't extract text
|
|
5092
|
+
// In most cases, renderDisplay should return a string or simple element
|
|
5093
|
+
if (typeof rendered === 'object' &&
|
|
5094
|
+
rendered !== null &&
|
|
5095
|
+
'props' in rendered) {
|
|
5096
|
+
const props = rendered.props;
|
|
5097
|
+
// Try to extract text from React element props
|
|
5098
|
+
if (props?.children) {
|
|
5099
|
+
const children = props.children;
|
|
5100
|
+
if (typeof children === 'string') {
|
|
5101
|
+
return children;
|
|
5102
|
+
}
|
|
5103
|
+
}
|
|
5104
|
+
}
|
|
5105
|
+
// Fallback: use raw value if we can't extract string
|
|
5106
|
+
return value;
|
|
5107
|
+
}
|
|
5108
|
+
return value;
|
|
5109
|
+
};
|
|
5110
|
+
// Debug log when renderDisplay is missing
|
|
5111
|
+
if (!renderDisplay) {
|
|
5112
|
+
console.debug(`[EnumPicker] Missing renderDisplay for field '${colLabel}'. Add renderDisplay function to schema for field '${colLabel}' to provide custom UI rendering. Currently showing enum string values directly.`, {
|
|
5113
|
+
fieldName: column,
|
|
5114
|
+
colLabel,
|
|
5115
|
+
prefix,
|
|
5116
|
+
enumValues: dataList,
|
|
5117
|
+
});
|
|
5118
|
+
}
|
|
5073
5119
|
// Current value for combobox (array format)
|
|
5074
5120
|
const currentValue = isMultiple
|
|
5075
5121
|
? watchEnums.filter((val) => val != null && val !== '')
|
|
5076
5122
|
: watchEnum
|
|
5077
5123
|
? [watchEnum]
|
|
5078
5124
|
: [];
|
|
5079
|
-
//
|
|
5125
|
+
// Track input focus state for single selection
|
|
5126
|
+
const [isInputFocused, setIsInputFocused] = useState(false);
|
|
5127
|
+
// Get the selected value for single selection display
|
|
5128
|
+
const selectedSingleValue = !isMultiple && watchEnum ? watchEnum : null;
|
|
5129
|
+
const selectedSingleRendered = selectedSingleValue
|
|
5130
|
+
? renderEnumValue(selectedSingleValue)
|
|
5131
|
+
: null;
|
|
5132
|
+
const isSelectedSingleValueString = typeof selectedSingleRendered === 'string';
|
|
5080
5133
|
const comboboxItems = useMemo(() => {
|
|
5081
5134
|
return dataList.map((item) => ({
|
|
5082
|
-
label:
|
|
5083
|
-
? String(renderDisplay(item))
|
|
5084
|
-
: formI18n.t(item),
|
|
5135
|
+
label: item, // Internal: used for search/filtering only
|
|
5085
5136
|
value: item,
|
|
5137
|
+
raw: item, // Passed to renderEnumValue for UI rendering
|
|
5138
|
+
displayLabel: getDisplayString(item), // Used for input display when selected
|
|
5086
5139
|
}));
|
|
5087
|
-
}, [dataList, renderDisplay
|
|
5140
|
+
}, [dataList, renderDisplay]);
|
|
5088
5141
|
// Use filter hook for combobox
|
|
5089
5142
|
const { contains } = useFilter({ sensitivity: 'base' });
|
|
5090
5143
|
// Create collection for combobox
|
|
5144
|
+
// itemToString uses displayLabel to show rendered display in input when selected
|
|
5091
5145
|
const { collection, filter } = useListCollection({
|
|
5092
5146
|
initialItems: comboboxItems,
|
|
5093
|
-
itemToString: (item) => item.
|
|
5147
|
+
itemToString: (item) => item.displayLabel, // Use displayLabel for selected value display
|
|
5094
5148
|
itemToValue: (item) => item.value,
|
|
5095
5149
|
filter: contains,
|
|
5096
5150
|
});
|
|
@@ -5114,9 +5168,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
5114
5168
|
setValue(colLabel, details.value);
|
|
5115
5169
|
}
|
|
5116
5170
|
}, children: jsx(HStack, { gap: "6", children: dataList.map((item) => {
|
|
5117
|
-
return (jsxs(RadioGroup$1.Item, { value: item, children: [jsx(RadioGroup$1.ItemHiddenInput, {}), jsx(RadioGroup$1.ItemIndicator, {}), jsx(RadioGroup$1.ItemText, { children:
|
|
5118
|
-
? renderDisplay(item)
|
|
5119
|
-
: formI18n.t(item) })] }, `${colLabel}-${item}`));
|
|
5171
|
+
return (jsxs(RadioGroup$1.Item, { value: item, children: [jsx(RadioGroup$1.ItemHiddenInput, {}), jsx(RadioGroup$1.ItemIndicator, {}), jsx(RadioGroup$1.ItemText, { children: renderEnumValue(item) })] }, `${colLabel}-${item}`));
|
|
5120
5172
|
}) }) }) }));
|
|
5121
5173
|
}
|
|
5122
5174
|
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
@@ -5127,16 +5179,31 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
5127
5179
|
return (jsx(Tag, { size: "lg", closable: true, onClick: () => {
|
|
5128
5180
|
const newValue = currentValue.filter((val) => val !== enumValue);
|
|
5129
5181
|
setValue(colLabel, newValue);
|
|
5130
|
-
}, children:
|
|
5131
|
-
? renderDisplay(enumValue)
|
|
5132
|
-
: formI18n.t(enumValue) }, enumValue));
|
|
5182
|
+
}, children: renderEnumValue(enumValue) }, enumValue));
|
|
5133
5183
|
}) })), jsxs(Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", positioning: insideDialog
|
|
5134
5184
|
? { strategy: 'fixed', hideWhenDetached: true }
|
|
5135
|
-
: undefined, children: [jsxs(Combobox.Control, {
|
|
5185
|
+
: undefined, children: [jsxs(Combobox.Control, { position: "relative", children: [!isMultiple &&
|
|
5186
|
+
selectedSingleValue &&
|
|
5187
|
+
!isInputFocused &&
|
|
5188
|
+
!isSelectedSingleValueString &&
|
|
5189
|
+
selectedSingleRendered && (jsx(Box, { position: "absolute", left: 3, top: "50%", transform: "translateY(-50%)", pointerEvents: "none", zIndex: 1, maxWidth: "calc(100% - 60px)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: selectedSingleRendered })), jsx(Combobox.Input, { placeholder: !isMultiple && selectedSingleValue && !isInputFocused
|
|
5190
|
+
? undefined
|
|
5191
|
+
: enumPickerLabels?.typeToSearch ?? 'Type to search', onFocus: () => setIsInputFocused(true), onBlur: () => setIsInputFocused(false), style: {
|
|
5192
|
+
color: !isMultiple &&
|
|
5193
|
+
selectedSingleValue &&
|
|
5194
|
+
!isInputFocused &&
|
|
5195
|
+
!isSelectedSingleValueString
|
|
5196
|
+
? 'transparent'
|
|
5197
|
+
: undefined,
|
|
5198
|
+
caretColor: !isMultiple &&
|
|
5199
|
+
selectedSingleValue &&
|
|
5200
|
+
!isInputFocused &&
|
|
5201
|
+
!isSelectedSingleValueString
|
|
5202
|
+
? 'transparent'
|
|
5203
|
+
: undefined,
|
|
5204
|
+
} }), jsxs(Combobox.IndicatorGroup, { children: [!isMultiple && currentValue.length > 0 && (jsx(Combobox.ClearTrigger, { onClick: () => {
|
|
5136
5205
|
setValue(colLabel, '');
|
|
5137
|
-
} })), jsx(Combobox.Trigger, {})] })] }), insideDialog ? (jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ??
|
|
5138
|
-
formI18n.t('empty_search_result') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsx(Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
|
|
5139
|
-
formI18n.t('empty_search_result') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) }) }))] })] }));
|
|
5206
|
+
} })), jsx(Combobox.Trigger, {})] })] }), insideDialog ? (jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? 'Total'}: ${collection.items.length}` })), collection.items.length === 0 ? (jsx(Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ?? 'No results found' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: renderEnumValue(item.raw) }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? 'Total'}: ${collection.items.length}` })), collection.items.length === 0 ? (jsx(Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ?? 'No results found' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: renderEnumValue(item.raw) }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) }) }))] })] }));
|
|
5140
5207
|
};
|
|
5141
5208
|
|
|
5142
5209
|
function isEnteringWindow(_ref) {
|
|
@@ -5751,7 +5818,7 @@ const FilePicker = ({ column, schema, prefix }) => {
|
|
|
5751
5818
|
const newFiles = files.filter(({ name }) => !currentFiles.some((cur) => cur.name === name));
|
|
5752
5819
|
setValue(colLabel, [...currentFiles, ...newFiles]);
|
|
5753
5820
|
}
|
|
5754
|
-
}, placeholder: filePickerLabels?.fileDropzone ??
|
|
5821
|
+
}, placeholder: filePickerLabels?.fileDropzone ?? 'Drop files here' }) }), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFiles.map((file, index) => {
|
|
5755
5822
|
const fileIdentifier = getFileIdentifier(file, index);
|
|
5756
5823
|
const fileName = getFileName(file);
|
|
5757
5824
|
const fileSize = getFileSize(file);
|
|
@@ -5862,9 +5929,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
5862
5929
|
}
|
|
5863
5930
|
};
|
|
5864
5931
|
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5865
|
-
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [jsx(VStack, { align: "stretch", gap: 2, children: jsx(Button$1, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ??
|
|
5866
|
-
formI18n.t('browse_library') ??
|
|
5867
|
-
'Browse from Library' }) }), 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 }), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
|
|
5932
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [jsx(VStack, { align: "stretch", gap: 2, children: jsx(Button$1, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ?? 'Browse from Library' }) }), 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 }), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
|
|
5868
5933
|
const file = fileMap.get(fileId);
|
|
5869
5934
|
const isImage = file
|
|
5870
5935
|
? /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name)
|
|
@@ -5880,15 +5945,46 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
5880
5945
|
}) })] }));
|
|
5881
5946
|
};
|
|
5882
5947
|
|
|
5883
|
-
// Default renderDisplay function that
|
|
5948
|
+
// Default renderDisplay function that intelligently displays items
|
|
5949
|
+
// If item is an object, tries to find common display fields (name, title, label, etc.)
|
|
5950
|
+
// Otherwise falls back to JSON.stringify
|
|
5884
5951
|
const defaultRenderDisplay = (item) => {
|
|
5952
|
+
// Check if item is an object (not null, not array, not primitive)
|
|
5953
|
+
if (item !== null &&
|
|
5954
|
+
typeof item === 'object' &&
|
|
5955
|
+
!Array.isArray(item) &&
|
|
5956
|
+
!(item instanceof Date)) {
|
|
5957
|
+
const obj = item;
|
|
5958
|
+
// Try common display fields in order of preference
|
|
5959
|
+
const displayFields = [
|
|
5960
|
+
'name',
|
|
5961
|
+
'title',
|
|
5962
|
+
'label',
|
|
5963
|
+
'displayName',
|
|
5964
|
+
'display_name',
|
|
5965
|
+
'text',
|
|
5966
|
+
'value',
|
|
5967
|
+
];
|
|
5968
|
+
for (const field of displayFields) {
|
|
5969
|
+
if (obj[field] !== undefined && obj[field] !== null) {
|
|
5970
|
+
const value = obj[field];
|
|
5971
|
+
// Return the value if it's a string or number, otherwise stringify it
|
|
5972
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
5973
|
+
return String(value);
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5976
|
+
}
|
|
5977
|
+
// If no display field found, fall back to JSON.stringify
|
|
5978
|
+
return JSON.stringify(item);
|
|
5979
|
+
}
|
|
5980
|
+
// For non-objects (primitives, arrays, dates), use JSON.stringify
|
|
5885
5981
|
return JSON.stringify(item);
|
|
5886
5982
|
};
|
|
5887
5983
|
|
|
5888
5984
|
const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
|
|
5889
5985
|
const { watch, getValues, formState: { errors }, setValue, } = useFormContext();
|
|
5890
5986
|
const { idMap, setIdMap, idPickerLabels, insideDialog } = useSchemaContext();
|
|
5891
|
-
const { renderDisplay, loadInitialValues, foreign_key, variant } = schema;
|
|
5987
|
+
const { renderDisplay, itemToValue: schemaItemToValue, loadInitialValues, foreign_key, variant, } = schema;
|
|
5892
5988
|
// loadInitialValues should be provided in schema for id-picker fields
|
|
5893
5989
|
// It's used to load the record of the id so the display is human-readable
|
|
5894
5990
|
if (variant === 'id-picker' && !loadInitialValues) {
|
|
@@ -6021,17 +6117,51 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
|
|
|
6021
6117
|
// Depend on idMapKey which only changes when items we care about change
|
|
6022
6118
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6023
6119
|
}, [currentValueKey, idMapKey]);
|
|
6120
|
+
// Default itemToValue function: extract value from item using column_ref
|
|
6121
|
+
const defaultItemToValue = (item) => String(item[column_ref]);
|
|
6122
|
+
// Use schema's itemToValue if provided, otherwise use default
|
|
6123
|
+
const itemToValueFn = schemaItemToValue
|
|
6124
|
+
? (item) => schemaItemToValue(item)
|
|
6125
|
+
: defaultItemToValue;
|
|
6126
|
+
// itemToString function: convert item to readable string using renderDisplay
|
|
6127
|
+
// This ensures items can always be displayed as readable strings in the combobox
|
|
6128
|
+
const renderFn = renderDisplay || defaultRenderDisplay;
|
|
6129
|
+
const itemToStringFn = (item) => {
|
|
6130
|
+
const rendered = renderFn(item);
|
|
6131
|
+
// If already a string or number, return it
|
|
6132
|
+
if (typeof rendered === 'string')
|
|
6133
|
+
return rendered;
|
|
6134
|
+
if (typeof rendered === 'number')
|
|
6135
|
+
return String(rendered);
|
|
6136
|
+
// For ReactNode, fall back to defaultRenderDisplay which converts to string
|
|
6137
|
+
return String(defaultRenderDisplay(item));
|
|
6138
|
+
};
|
|
6024
6139
|
// Transform data for combobox collection
|
|
6025
6140
|
// label is used for filtering/searching (must be a string)
|
|
6141
|
+
// displayLabel is used for input display when selected (string representation of rendered display)
|
|
6026
6142
|
// raw item is stored for custom rendering
|
|
6027
6143
|
// Also include items from idMap that match currentValue (for initial values display)
|
|
6028
6144
|
const comboboxItems = useMemo(() => {
|
|
6029
6145
|
const renderFn = renderDisplay || defaultRenderDisplay;
|
|
6146
|
+
// Helper to convert rendered display to string for displayLabel
|
|
6147
|
+
// For ReactNodes (non-string/number), we can't safely stringify due to circular refs
|
|
6148
|
+
// So we use the label (which is already a string) as fallback
|
|
6149
|
+
const getDisplayString = (rendered, fallbackLabel) => {
|
|
6150
|
+
if (typeof rendered === 'string')
|
|
6151
|
+
return rendered;
|
|
6152
|
+
if (typeof rendered === 'number')
|
|
6153
|
+
return String(rendered);
|
|
6154
|
+
// For ReactNode, use the fallback label (which is already a string representation)
|
|
6155
|
+
// The actual ReactNode will be rendered in the overlay, not in the input
|
|
6156
|
+
return fallbackLabel;
|
|
6157
|
+
};
|
|
6030
6158
|
const itemsFromDataList = dataList.map((item) => {
|
|
6031
6159
|
const rendered = renderFn(item);
|
|
6160
|
+
const label = typeof rendered === 'string' ? rendered : JSON.stringify(item); // Use string for filtering
|
|
6032
6161
|
return {
|
|
6033
|
-
label
|
|
6034
|
-
|
|
6162
|
+
label, // Use string for filtering
|
|
6163
|
+
displayLabel: getDisplayString(rendered, label), // String representation for input display
|
|
6164
|
+
value: itemToValueFn(item),
|
|
6035
6165
|
raw: item,
|
|
6036
6166
|
};
|
|
6037
6167
|
});
|
|
@@ -6040,25 +6170,28 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
|
|
|
6040
6170
|
const itemsFromIdMap = idMapItems
|
|
6041
6171
|
.map((item) => {
|
|
6042
6172
|
// Check if this item is already in itemsFromDataList
|
|
6043
|
-
const alreadyIncluded = itemsFromDataList.some((i) => i.value ===
|
|
6173
|
+
const alreadyIncluded = itemsFromDataList.some((i) => i.value === itemToValueFn(item));
|
|
6044
6174
|
if (alreadyIncluded)
|
|
6045
6175
|
return null;
|
|
6046
6176
|
const rendered = renderFn(item);
|
|
6177
|
+
const label = typeof rendered === 'string' ? rendered : JSON.stringify(item);
|
|
6047
6178
|
return {
|
|
6048
|
-
label
|
|
6049
|
-
|
|
6179
|
+
label,
|
|
6180
|
+
displayLabel: getDisplayString(rendered, label), // String representation for input display
|
|
6181
|
+
value: itemToValueFn(item),
|
|
6050
6182
|
raw: item,
|
|
6051
6183
|
};
|
|
6052
6184
|
})
|
|
6053
6185
|
.filter((item) => item !== null);
|
|
6054
6186
|
return [...itemsFromIdMap, ...itemsFromDataList];
|
|
6055
|
-
}, [dataList, column_ref, renderDisplay, idMapItems]);
|
|
6187
|
+
}, [dataList, column_ref, renderDisplay, idMapItems, itemToValueFn]);
|
|
6056
6188
|
// Use filter hook for combobox
|
|
6057
6189
|
const { contains } = useFilter({ sensitivity: 'base' });
|
|
6058
6190
|
// Create collection for combobox
|
|
6191
|
+
// itemToString uses displayLabel to show rendered display in input when selected
|
|
6059
6192
|
const { collection, filter, set } = useListCollection({
|
|
6060
6193
|
initialItems: comboboxItems,
|
|
6061
|
-
itemToString: (item) => item.
|
|
6194
|
+
itemToString: (item) => item.displayLabel, // Use displayLabel for selected value display
|
|
6062
6195
|
itemToValue: (item) => item.value,
|
|
6063
6196
|
filter: contains,
|
|
6064
6197
|
});
|
|
@@ -6113,6 +6246,8 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
|
|
|
6113
6246
|
idPickerLabels,
|
|
6114
6247
|
insideDialog: insideDialog ?? false,
|
|
6115
6248
|
renderDisplay,
|
|
6249
|
+
itemToValue: itemToValueFn,
|
|
6250
|
+
itemToString: itemToStringFn,
|
|
6116
6251
|
loadInitialValues: loadInitialValues ??
|
|
6117
6252
|
(async () => ({ data: { data: [], count: 0 }, idMap: {} })), // Fallback if not provided
|
|
6118
6253
|
column_ref,
|
|
@@ -6123,60 +6258,69 @@ const useIdPickerData = ({ column, schema, prefix, isMultiple, }) => {
|
|
|
6123
6258
|
|
|
6124
6259
|
const IdPickerSingle = ({ column, schema, prefix, }) => {
|
|
6125
6260
|
const formI18n = useFormI18n(column, prefix, schema);
|
|
6126
|
-
const { required, gridColumn = 'span 12', gridRow = 'span 1'
|
|
6261
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
6127
6262
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
6128
|
-
const { colLabel, currentValue, searchText, setSearchText, isLoading, isFetching, isPending, isError, isSearching,
|
|
6263
|
+
const { colLabel, currentValue, searchText, setSearchText, isLoading, isFetching, isPending, isError, isSearching, collection, filter, idMap, idPickerLabels, insideDialog, renderDisplay: renderDisplayFn, itemToValue, itemToString, errors, setValue, } = useIdPickerData({
|
|
6129
6264
|
column,
|
|
6130
6265
|
schema,
|
|
6131
6266
|
prefix,
|
|
6132
6267
|
isMultiple: false,
|
|
6133
6268
|
});
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
const
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6269
|
+
// Get the selected value for single selection display
|
|
6270
|
+
const selectedId = currentValue.length > 0 ? currentValue[0] : null;
|
|
6271
|
+
const selectedItem = selectedId
|
|
6272
|
+
? idMap[selectedId]
|
|
6273
|
+
: undefined;
|
|
6274
|
+
// Use itemToValue to get the combobox value from the selected item, or use the ID directly
|
|
6275
|
+
const comboboxValue = selectedItem
|
|
6276
|
+
? itemToString(selectedItem)
|
|
6277
|
+
: selectedId || '';
|
|
6278
|
+
// itemToString is available from the hook and can be used to get a readable string
|
|
6279
|
+
// representation of any item. The collection's itemToString is automatically used
|
|
6280
|
+
// by the combobox to display selected values.
|
|
6281
|
+
// Use useCombobox hook to control input value
|
|
6282
|
+
const combobox = useCombobox({
|
|
6283
|
+
collection,
|
|
6284
|
+
value: [comboboxValue],
|
|
6285
|
+
onInputValueChange(e) {
|
|
6286
|
+
setSearchText(e.inputValue);
|
|
6287
|
+
filter(e.inputValue);
|
|
6288
|
+
},
|
|
6289
|
+
onValueChange(e) {
|
|
6290
|
+
setValue(colLabel, e.value[0] || '');
|
|
6291
|
+
// Clear the input value after selection
|
|
6292
|
+
setSearchText('');
|
|
6293
|
+
},
|
|
6294
|
+
multiple: false,
|
|
6295
|
+
closeOnSelect: true,
|
|
6296
|
+
openOnClick: true,
|
|
6297
|
+
invalid: !!errors[colLabel],
|
|
6298
|
+
});
|
|
6299
|
+
// Use renderDisplay from hook (which comes from schema) or fallback to default
|
|
6300
|
+
const renderDisplayFunction = renderDisplayFn || defaultRenderDisplay;
|
|
6301
|
+
// Get the selected value for single selection display (already computed above)
|
|
6302
|
+
const selectedRendered = selectedItem
|
|
6303
|
+
? renderDisplayFunction(selectedItem)
|
|
6304
|
+
: null;
|
|
6305
|
+
return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
6306
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxs(Combobox.RootProvider, { value: combobox, width: "100%", children: [jsx(Show, { when: selectedId && selectedRendered, children: jsxs(HStack, { justifyContent: 'space-between', children: [jsx(Box, { children: selectedRendered }), currentValue.length > 0 && (jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => {
|
|
6307
|
+
setValue(colLabel, '');
|
|
6308
|
+
}, children: jsx(Icon, { children: jsx(BiX, {}) }) }))] }) }), jsx(Show, { when: !selectedId || !selectedRendered, children: jsxs(Combobox.Control, { position: "relative", children: [jsx(Combobox.Input, { placeholder: idPickerLabels?.typeToSearch ?? 'Type to search' }), jsxs(Combobox.IndicatorGroup, { children: [(isFetching || isLoading || isPending) && jsx(Spinner, { size: "xs" }), isError && (jsx(Icon, { color: "fg.error", children: jsx(BiError, {}) })), jsx(Combobox.Trigger, {})] })] }) }), insideDialog ? (jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: idPickerLabels?.emptySearchResult ?? 'Loading failed' })) : isFetching || isLoading || isPending || isSearching ? (
|
|
6309
|
+
// Show skeleton items to prevent UI shift
|
|
6310
|
+
jsx(Fragment, { children: Array.from({ length: 5 }).map((_, index) => (jsx(Flex, { p: 2, align: "center", gap: 2, children: jsx(Skeleton, { height: "20px", flex: "1" }) }, `skeleton-${index}`))) })) : collection.items.length === 0 ? (jsx(Combobox.Empty, { children: searchText
|
|
6311
|
+
? idPickerLabels?.emptySearchResult ?? 'No results found'
|
|
6312
|
+
: idPickerLabels?.initialResults ??
|
|
6313
|
+
'Start typing to search' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [renderDisplayFunction(item.raw), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: idPickerLabels?.emptySearchResult ?? 'Loading failed' })) : isFetching || isLoading || isPending || isSearching ? (
|
|
6161
6314
|
// Show skeleton items to prevent UI shift
|
|
6162
6315
|
jsx(Fragment, { children: Array.from({ length: 5 }).map((_, index) => (jsx(Flex, { p: 2, align: "center", gap: 2, children: jsx(Skeleton, { height: "20px", flex: "1" }) }, `skeleton-${index}`))) })) : collection.items.length === 0 ? (jsx(Combobox.Empty, { children: searchText
|
|
6163
6316
|
? idPickerLabels?.emptySearchResult ?? 'No results found'
|
|
6164
6317
|
: idPickerLabels?.initialResults ??
|
|
6165
|
-
'Start typing to search' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (
|
|
6166
|
-
? renderDisplayFunction(item.raw)
|
|
6167
|
-
: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: idPickerLabels?.emptySearchResult ?? 'Loading failed' })) : isFetching || isLoading || isPending || isSearching ? (
|
|
6168
|
-
// Show skeleton items to prevent UI shift
|
|
6169
|
-
jsx(Fragment, { children: Array.from({ length: 5 }).map((_, index) => (jsx(Flex, { p: 2, align: "center", gap: 2, children: jsx(Skeleton, { height: "20px", flex: "1" }) }, `skeleton-${index}`))) })) : collection.items.length === 0 ? (jsx(Combobox.Empty, { children: searchText
|
|
6170
|
-
? idPickerLabels?.emptySearchResult ?? 'No results found'
|
|
6171
|
-
: idPickerLabels?.initialResults ??
|
|
6172
|
-
'Start typing to search' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: !!renderDisplayFunction === true
|
|
6173
|
-
? renderDisplayFunction(item.raw)
|
|
6174
|
-
: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) }) }))] })] }));
|
|
6318
|
+
'Start typing to search' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsx(Combobox.Item, { item: item, children: renderDisplayFunction(item.raw) }, item.value ?? `item-${index}`))) })) }) }) }))] }) }));
|
|
6175
6319
|
};
|
|
6176
6320
|
|
|
6177
6321
|
const IdPickerMultiple = ({ column, schema, prefix, }) => {
|
|
6178
6322
|
const formI18n = useFormI18n(column, prefix, schema);
|
|
6179
|
-
const { required, gridColumn = 'span 12', gridRow = 'span 1'
|
|
6323
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1' } = schema;
|
|
6180
6324
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
6181
6325
|
const { colLabel, currentValue, searchText, setSearchText, isLoading, isFetching, isPending, isError, isSearching, isLoadingInitialValues, isFetchingInitialValues, missingIds, collection, idMap, idPickerLabels, insideDialog, renderDisplay: renderDisplayFn, errors, setValue, } = useIdPickerData({
|
|
6182
6326
|
column,
|
|
@@ -6190,7 +6334,8 @@ const IdPickerMultiple = ({ column, schema, prefix, }) => {
|
|
|
6190
6334
|
const handleValueChange = (details) => {
|
|
6191
6335
|
setValue(colLabel, details.value);
|
|
6192
6336
|
};
|
|
6193
|
-
|
|
6337
|
+
// Use renderDisplay from hook (which comes from schema) or fallback to default
|
|
6338
|
+
const renderDisplayFunction = renderDisplayFn || defaultRenderDisplay;
|
|
6194
6339
|
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
6195
6340
|
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [currentValue.length > 0 && (jsx(Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((id) => {
|
|
6196
6341
|
const item = idMap[id];
|
|
@@ -6215,16 +6360,12 @@ const IdPickerMultiple = ({ column, schema, prefix, }) => {
|
|
|
6215
6360
|
jsx(Fragment, { children: Array.from({ length: 5 }).map((_, index) => (jsx(Flex, { p: 2, align: "center", gap: 2, children: jsx(Skeleton, { height: "20px", flex: "1" }) }, `skeleton-${index}`))) })) : collection.items.length === 0 ? (jsx(Combobox.Empty, { children: searchText
|
|
6216
6361
|
? idPickerLabels?.emptySearchResult ?? 'No results found'
|
|
6217
6362
|
: idPickerLabels?.initialResults ??
|
|
6218
|
-
'Start typing to search' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.
|
|
6219
|
-
? renderDisplayFunction(item.raw)
|
|
6220
|
-
: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: idPickerLabels?.emptySearchResult ?? 'Loading failed' })) : isFetching || isLoading || isPending || isSearching ? (
|
|
6363
|
+
'Start typing to search' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [renderDisplayFunction(item.raw), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: idPickerLabels?.emptySearchResult ?? 'Loading failed' })) : isFetching || isLoading || isPending || isSearching ? (
|
|
6221
6364
|
// Show skeleton items to prevent UI shift
|
|
6222
6365
|
jsx(Fragment, { children: Array.from({ length: 5 }).map((_, index) => (jsx(Flex, { p: 2, align: "center", gap: 2, children: jsx(Skeleton, { height: "20px", flex: "1" }) }, `skeleton-${index}`))) })) : collection.items.length === 0 ? (jsx(Combobox.Empty, { children: searchText
|
|
6223
6366
|
? idPickerLabels?.emptySearchResult ?? 'No results found'
|
|
6224
6367
|
: idPickerLabels?.initialResults ??
|
|
6225
|
-
'Start typing to search' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.
|
|
6226
|
-
? renderDisplayFunction(item.raw)
|
|
6227
|
-
: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) }) }))] })] }));
|
|
6368
|
+
'Start typing to search' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [renderDisplayFunction(item.raw), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) }) }))] })] }));
|
|
6228
6369
|
};
|
|
6229
6370
|
|
|
6230
6371
|
const NumberInputRoot = React.forwardRef(function NumberInput$1(props, ref) {
|
|
@@ -7361,7 +7502,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7361
7502
|
backButtonLabel: 'Back',
|
|
7362
7503
|
forwardButtonLabel: 'Next',
|
|
7363
7504
|
}, timePickerLabels, timezone = 'Asia/Hong_Kong', startTime, minDate, maxDate, portalled = false, defaultDate, defaultTime, }) {
|
|
7364
|
-
console.
|
|
7505
|
+
console.debug('[DateTimePicker] Component initialized with props:', {
|
|
7365
7506
|
value,
|
|
7366
7507
|
format,
|
|
7367
7508
|
showSeconds,
|
|
@@ -7380,13 +7521,13 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7380
7521
|
const [selectedDate, setSelectedDate] = useState(getDateString(value));
|
|
7381
7522
|
// Helper to get time values from value prop with timezone
|
|
7382
7523
|
const getTimeFromValue = useCallback((val) => {
|
|
7383
|
-
console.
|
|
7524
|
+
console.debug('[DateTimePicker] getTimeFromValue called:', {
|
|
7384
7525
|
val,
|
|
7385
7526
|
timezone,
|
|
7386
7527
|
showSeconds,
|
|
7387
7528
|
});
|
|
7388
7529
|
if (!val) {
|
|
7389
|
-
console.
|
|
7530
|
+
console.debug('[DateTimePicker] No value provided, returning nulls');
|
|
7390
7531
|
return {
|
|
7391
7532
|
hour12: null,
|
|
7392
7533
|
minute: null,
|
|
@@ -7396,7 +7537,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7396
7537
|
};
|
|
7397
7538
|
}
|
|
7398
7539
|
const dateObj = dayjs(val).tz(timezone);
|
|
7399
|
-
console.
|
|
7540
|
+
console.debug('[DateTimePicker] Parsed date object:', {
|
|
7400
7541
|
original: val,
|
|
7401
7542
|
timezone,
|
|
7402
7543
|
isValid: dateObj.isValid(),
|
|
@@ -7406,7 +7547,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7406
7547
|
second: dateObj.second(),
|
|
7407
7548
|
});
|
|
7408
7549
|
if (!dateObj.isValid()) {
|
|
7409
|
-
console.
|
|
7550
|
+
console.debug('[DateTimePicker] Invalid date object, returning nulls');
|
|
7410
7551
|
return {
|
|
7411
7552
|
hour12: null,
|
|
7412
7553
|
minute: null,
|
|
@@ -7427,11 +7568,11 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7427
7568
|
hour24: hour24Value,
|
|
7428
7569
|
second: secondValue,
|
|
7429
7570
|
};
|
|
7430
|
-
console.
|
|
7571
|
+
console.debug('[DateTimePicker] Extracted time values:', result);
|
|
7431
7572
|
return result;
|
|
7432
7573
|
}, [timezone, showSeconds]);
|
|
7433
7574
|
const initialTime = getTimeFromValue(value);
|
|
7434
|
-
console.
|
|
7575
|
+
console.debug('[DateTimePicker] Initial time from value:', {
|
|
7435
7576
|
value,
|
|
7436
7577
|
initialTime,
|
|
7437
7578
|
});
|
|
@@ -7508,14 +7649,14 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7508
7649
|
const [second, setSecond] = useState(initialTimeValues.second);
|
|
7509
7650
|
// Sync selectedDate and time states when value prop changes
|
|
7510
7651
|
useEffect(() => {
|
|
7511
|
-
console.
|
|
7652
|
+
console.debug('[DateTimePicker] useEffect triggered - value changed:', {
|
|
7512
7653
|
value,
|
|
7513
7654
|
timezone,
|
|
7514
7655
|
format,
|
|
7515
7656
|
});
|
|
7516
7657
|
// If value is null, undefined, or invalid, clear date but keep default time values
|
|
7517
7658
|
if (!value || value === null || value === undefined) {
|
|
7518
|
-
console.
|
|
7659
|
+
console.debug('[DateTimePicker] Value is null/undefined, clearing date but keeping default time');
|
|
7519
7660
|
setSelectedDate('');
|
|
7520
7661
|
// Keep default time values instead of clearing them
|
|
7521
7662
|
if (format === 'iso-date-time') {
|
|
@@ -7537,7 +7678,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7537
7678
|
// Check if value is valid
|
|
7538
7679
|
const dateObj = dayjs(value).tz(timezone);
|
|
7539
7680
|
if (!dateObj.isValid()) {
|
|
7540
|
-
console.
|
|
7681
|
+
console.debug('[DateTimePicker] Invalid value, clearing date but keeping default time');
|
|
7541
7682
|
setSelectedDate('');
|
|
7542
7683
|
// Keep default time values instead of clearing them
|
|
7543
7684
|
if (format === 'iso-date-time') {
|
|
@@ -7557,10 +7698,10 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7557
7698
|
return;
|
|
7558
7699
|
}
|
|
7559
7700
|
const dateString = getDateString(value);
|
|
7560
|
-
console.
|
|
7701
|
+
console.debug('[DateTimePicker] Setting selectedDate:', dateString);
|
|
7561
7702
|
setSelectedDate(dateString);
|
|
7562
7703
|
const timeData = getTimeFromValue(value);
|
|
7563
|
-
console.
|
|
7704
|
+
console.debug('[DateTimePicker] Updating time states:', {
|
|
7564
7705
|
timeData,
|
|
7565
7706
|
});
|
|
7566
7707
|
setHour12(timeData.hour12);
|
|
@@ -7570,7 +7711,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7570
7711
|
setSecond(timeData.second);
|
|
7571
7712
|
}, [value, getTimeFromValue, getDateString, timezone]);
|
|
7572
7713
|
const handleDateChange = (date) => {
|
|
7573
|
-
console.
|
|
7714
|
+
console.debug('[DateTimePicker] handleDateChange called:', {
|
|
7574
7715
|
date,
|
|
7575
7716
|
timezone,
|
|
7576
7717
|
showSeconds,
|
|
@@ -7578,7 +7719,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7578
7719
|
});
|
|
7579
7720
|
// If date is empty or invalid, clear all fields
|
|
7580
7721
|
if (!date || date === '') {
|
|
7581
|
-
console.
|
|
7722
|
+
console.debug('[DateTimePicker] Empty date, clearing all fields');
|
|
7582
7723
|
setSelectedDate('');
|
|
7583
7724
|
setHour12(null);
|
|
7584
7725
|
setMinute(null);
|
|
@@ -7591,7 +7732,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7591
7732
|
setSelectedDate(date);
|
|
7592
7733
|
// Parse the date string (YYYY-MM-DD) in the specified timezone
|
|
7593
7734
|
const dateObj = dayjs.tz(date, timezone);
|
|
7594
|
-
console.
|
|
7735
|
+
console.debug('[DateTimePicker] Parsed date object:', {
|
|
7595
7736
|
date,
|
|
7596
7737
|
timezone,
|
|
7597
7738
|
isValid: dateObj.isValid(),
|
|
@@ -7617,7 +7758,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7617
7758
|
if (!hasTimeValues) {
|
|
7618
7759
|
// Use defaultTime if provided, otherwise default to 00:00
|
|
7619
7760
|
if (defaultTime) {
|
|
7620
|
-
console.
|
|
7761
|
+
console.debug('[DateTimePicker] No time values set, using defaultTime');
|
|
7621
7762
|
if (format === 'iso-date-time') {
|
|
7622
7763
|
const defaultTime24 = defaultTime;
|
|
7623
7764
|
setHour24(defaultTime24.hour ?? 0);
|
|
@@ -7644,7 +7785,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7644
7785
|
}
|
|
7645
7786
|
}
|
|
7646
7787
|
else {
|
|
7647
|
-
console.
|
|
7788
|
+
console.debug('[DateTimePicker] No time values set, defaulting to 00:00');
|
|
7648
7789
|
if (format === 'iso-date-time') {
|
|
7649
7790
|
setHour24(0);
|
|
7650
7791
|
setMinute(0);
|
|
@@ -7672,17 +7813,17 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7672
7813
|
// When showSeconds is false, ignore seconds from the date
|
|
7673
7814
|
if (!showSeconds) {
|
|
7674
7815
|
const dateWithoutSeconds = dateObj.second(0).millisecond(0).toISOString();
|
|
7675
|
-
console.
|
|
7816
|
+
console.debug('[DateTimePicker] Updating date without seconds:', dateWithoutSeconds);
|
|
7676
7817
|
updateDateTime(dateWithoutSeconds, timeDataToUse);
|
|
7677
7818
|
}
|
|
7678
7819
|
else {
|
|
7679
7820
|
const dateWithSeconds = dateObj.toISOString();
|
|
7680
|
-
console.
|
|
7821
|
+
console.debug('[DateTimePicker] Updating date with seconds:', dateWithSeconds);
|
|
7681
7822
|
updateDateTime(dateWithSeconds, timeDataToUse);
|
|
7682
7823
|
}
|
|
7683
7824
|
};
|
|
7684
7825
|
const handleTimeChange = (timeData) => {
|
|
7685
|
-
console.
|
|
7826
|
+
console.debug('[DateTimePicker] handleTimeChange called:', {
|
|
7686
7827
|
timeData,
|
|
7687
7828
|
format,
|
|
7688
7829
|
selectedDate,
|
|
@@ -7690,7 +7831,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7690
7831
|
});
|
|
7691
7832
|
if (format === 'iso-date-time') {
|
|
7692
7833
|
const data = timeData;
|
|
7693
|
-
console.
|
|
7834
|
+
console.debug('[DateTimePicker] ISO format - setting 24-hour time:', data);
|
|
7694
7835
|
setHour24(data.hour);
|
|
7695
7836
|
setMinute(data.minute);
|
|
7696
7837
|
if (showSeconds) {
|
|
@@ -7703,7 +7844,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7703
7844
|
}
|
|
7704
7845
|
else {
|
|
7705
7846
|
const data = timeData;
|
|
7706
|
-
console.
|
|
7847
|
+
console.debug('[DateTimePicker] 12-hour format - setting time:', data);
|
|
7707
7848
|
setHour12(data.hour);
|
|
7708
7849
|
setMinute(data.minute);
|
|
7709
7850
|
setMeridiem(data.meridiem);
|
|
@@ -7712,7 +7853,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7712
7853
|
if (!selectedDate || !dayjs(selectedDate).isValid()) {
|
|
7713
7854
|
// If effectiveDefaultDate is available, use it instead of clearing
|
|
7714
7855
|
if (effectiveDefaultDate && dayjs(effectiveDefaultDate).isValid()) {
|
|
7715
|
-
console.
|
|
7856
|
+
console.debug('[DateTimePicker] No valid selectedDate, using effectiveDefaultDate:', effectiveDefaultDate);
|
|
7716
7857
|
setSelectedDate(effectiveDefaultDate);
|
|
7717
7858
|
const dateObj = dayjs(effectiveDefaultDate).tz(timezone);
|
|
7718
7859
|
if (dateObj.isValid()) {
|
|
@@ -7731,7 +7872,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7731
7872
|
return;
|
|
7732
7873
|
}
|
|
7733
7874
|
else {
|
|
7734
|
-
console.
|
|
7875
|
+
console.debug('[DateTimePicker] No valid selectedDate and no effectiveDefaultDate, keeping time values but no date');
|
|
7735
7876
|
// Keep the time values that were just set, but don't set a date
|
|
7736
7877
|
// This should rarely happen as effectiveDefaultDate always defaults to today
|
|
7737
7878
|
setSelectedDate('');
|
|
@@ -7755,14 +7896,14 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7755
7896
|
}
|
|
7756
7897
|
};
|
|
7757
7898
|
const updateDateTime = (date, timeData) => {
|
|
7758
|
-
console.
|
|
7899
|
+
console.debug('[DateTimePicker] updateDateTime called:', {
|
|
7759
7900
|
date,
|
|
7760
7901
|
timeData,
|
|
7761
7902
|
format,
|
|
7762
7903
|
currentStates: { hour12, minute, meridiem, hour24, second },
|
|
7763
7904
|
});
|
|
7764
7905
|
if (!date || date === null || date === undefined) {
|
|
7765
|
-
console.
|
|
7906
|
+
console.debug('[DateTimePicker] No date provided, clearing all fields and calling onChange(undefined)');
|
|
7766
7907
|
setSelectedDate('');
|
|
7767
7908
|
setHour12(null);
|
|
7768
7909
|
setMinute(null);
|
|
@@ -7800,11 +7941,11 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7800
7941
|
: 0;
|
|
7801
7942
|
// If all time values are null, clear the value
|
|
7802
7943
|
if (h === null && m === null && (showSeconds ? s === null : true)) {
|
|
7803
|
-
console.
|
|
7944
|
+
console.debug('[DateTimePicker] All time values are null, clearing value');
|
|
7804
7945
|
onChange?.(undefined);
|
|
7805
7946
|
return;
|
|
7806
7947
|
}
|
|
7807
|
-
console.
|
|
7948
|
+
console.debug('[DateTimePicker] ISO format - setting time on date:', {
|
|
7808
7949
|
h,
|
|
7809
7950
|
m,
|
|
7810
7951
|
s,
|
|
@@ -7818,7 +7959,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7818
7959
|
}
|
|
7819
7960
|
else {
|
|
7820
7961
|
const data = timeData;
|
|
7821
|
-
console.
|
|
7962
|
+
console.debug('[DateTimePicker] Processing 12-hour format:', {
|
|
7822
7963
|
'data !== undefined': data !== undefined,
|
|
7823
7964
|
'data?.hour': data?.hour,
|
|
7824
7965
|
'data?.minute': data?.minute,
|
|
@@ -7831,14 +7972,14 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7831
7972
|
const h = data !== undefined ? data.hour : hour12;
|
|
7832
7973
|
const m = data !== undefined ? data.minute : minute;
|
|
7833
7974
|
const mer = data !== undefined ? data.meridiem : meridiem;
|
|
7834
|
-
console.
|
|
7975
|
+
console.debug('[DateTimePicker] Resolved time values:', { h, m, mer });
|
|
7835
7976
|
// If all time values are null, clear the value
|
|
7836
7977
|
if (h === null && m === null && mer === null) {
|
|
7837
|
-
console.
|
|
7978
|
+
console.debug('[DateTimePicker] All time values are null, clearing value');
|
|
7838
7979
|
onChange?.(undefined);
|
|
7839
7980
|
return;
|
|
7840
7981
|
}
|
|
7841
|
-
console.
|
|
7982
|
+
console.debug('[DateTimePicker] 12-hour format - converting time:', {
|
|
7842
7983
|
h,
|
|
7843
7984
|
m,
|
|
7844
7985
|
mer,
|
|
@@ -7849,7 +7990,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7849
7990
|
hour24 = 0;
|
|
7850
7991
|
else if (mer === 'pm' && h < 12)
|
|
7851
7992
|
hour24 = h + 12;
|
|
7852
|
-
console.
|
|
7993
|
+
console.debug('[DateTimePicker] Converted to 24-hour:', {
|
|
7853
7994
|
h,
|
|
7854
7995
|
mer,
|
|
7855
7996
|
hour24,
|
|
@@ -7857,7 +7998,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7857
7998
|
newDate.setHours(hour24);
|
|
7858
7999
|
}
|
|
7859
8000
|
else {
|
|
7860
|
-
console.
|
|
8001
|
+
console.debug('[DateTimePicker] Skipping hour update - h or mer is null:', {
|
|
7861
8002
|
h,
|
|
7862
8003
|
mer,
|
|
7863
8004
|
});
|
|
@@ -7866,12 +8007,12 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7866
8007
|
newDate.setMinutes(m);
|
|
7867
8008
|
}
|
|
7868
8009
|
else {
|
|
7869
|
-
console.
|
|
8010
|
+
console.debug('[DateTimePicker] Skipping minute update - m is null');
|
|
7870
8011
|
}
|
|
7871
8012
|
newDate.setSeconds(0);
|
|
7872
8013
|
}
|
|
7873
8014
|
const finalISO = dayjs(newDate).tz(timezone).toISOString();
|
|
7874
|
-
console.
|
|
8015
|
+
console.debug('[DateTimePicker] Final ISO string to emit:', {
|
|
7875
8016
|
newDate: newDate.toISOString(),
|
|
7876
8017
|
timezone,
|
|
7877
8018
|
finalISO,
|
|
@@ -7906,7 +8047,7 @@ function DateTimePicker$1({ value, onChange, format = 'date-time', showSeconds =
|
|
|
7906
8047
|
: undefined;
|
|
7907
8048
|
// Log current state before render
|
|
7908
8049
|
useEffect(() => {
|
|
7909
|
-
console.
|
|
8050
|
+
console.debug('[DateTimePicker] Current state before render:', {
|
|
7910
8051
|
isISO,
|
|
7911
8052
|
hour12,
|
|
7912
8053
|
minute,
|
|
@@ -8166,7 +8307,7 @@ const BooleanViewer = ({ schema, column, prefix, }) => {
|
|
|
8166
8307
|
const value = watch(colLabel);
|
|
8167
8308
|
const formI18n = useFormI18n(column, prefix, schema);
|
|
8168
8309
|
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
8169
|
-
gridRow, children: [jsx(Text, { children: value ?
|
|
8310
|
+
gridRow, children: [jsx(Text, { children: value ? 'True' : 'False' }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
|
|
8170
8311
|
};
|
|
8171
8312
|
|
|
8172
8313
|
const CustomViewer = ({ column, schema, prefix }) => {
|
|
@@ -8205,16 +8346,15 @@ const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
|
|
|
8205
8346
|
const colLabel = formI18n.colLabel;
|
|
8206
8347
|
const watchEnum = watch(colLabel);
|
|
8207
8348
|
const watchEnums = (watch(colLabel) ?? []);
|
|
8349
|
+
const renderDisplayFunction = renderDisplay || defaultRenderDisplay;
|
|
8208
8350
|
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
8209
8351
|
gridRow, children: [isMultiple && (jsx(Flex, { flexFlow: 'wrap', gap: 1, children: watchEnums.map((enumValue) => {
|
|
8210
8352
|
const item = enumValue;
|
|
8211
8353
|
if (item === undefined) {
|
|
8212
8354
|
return jsx(Fragment, { children: "undefined" });
|
|
8213
8355
|
}
|
|
8214
|
-
return (jsx(Tag, { size: "lg", children:
|
|
8215
|
-
|
|
8216
|
-
: formI18n.t(item) }, item));
|
|
8217
|
-
}) })), !isMultiple && jsx(Text, { children: formI18n.t(watchEnum) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
|
|
8356
|
+
return (jsx(Tag, { size: "lg", children: renderDisplayFunction(item) }, item));
|
|
8357
|
+
}) })), !isMultiple && jsx(Text, { children: renderDisplayFunction(watchEnum) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
|
|
8218
8358
|
};
|
|
8219
8359
|
|
|
8220
8360
|
const FileViewer = ({ column, schema, prefix }) => {
|