@bsol-oss/react-datatable5 12.0.0-beta.83 → 12.0.0-beta.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -575,7 +575,7 @@ const PaginationItems = (props) => {
|
|
|
575
575
|
return page.type === "ellipsis" ? (jsxRuntime.jsx(PaginationEllipsis, { index: index, ...props }, index)) : (jsxRuntime.jsx(PaginationItem, { type: "page", value: page.value, ...props }, index));
|
|
576
576
|
}) }));
|
|
577
577
|
};
|
|
578
|
-
|
|
578
|
+
React__namespace.forwardRef(function PaginationPageText(props, ref) {
|
|
579
579
|
const { format = "compact", ...rest } = props;
|
|
580
580
|
const { page, totalPages, pageRange, count } = react.usePaginationContext();
|
|
581
581
|
const content = React__namespace.useMemo(() => {
|
|
@@ -4176,16 +4176,6 @@ const DateRangePicker = ({ column, schema, prefix, }) => {
|
|
|
4176
4176
|
}, monthsToDisplay: 2 })] }) })] }) }));
|
|
4177
4177
|
};
|
|
4178
4178
|
|
|
4179
|
-
function filterArray(array, searchTerm) {
|
|
4180
|
-
// Convert the search term to lower case for case-insensitive comparison
|
|
4181
|
-
const lowerCaseSearchTerm = searchTerm.toLowerCase();
|
|
4182
|
-
// Use the filter method to return an array of matching items
|
|
4183
|
-
return array.filter((item) => {
|
|
4184
|
-
// Convert each item to a string and check if it includes the search term
|
|
4185
|
-
return item.toString().toLowerCase().includes(lowerCaseSearchTerm);
|
|
4186
|
-
});
|
|
4187
|
-
}
|
|
4188
|
-
|
|
4189
4179
|
const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLimit = false, }) => {
|
|
4190
4180
|
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
4191
4181
|
const { enumPickerLabels } = useSchemaContext();
|
|
@@ -4193,89 +4183,74 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4193
4183
|
const { required, variant } = schema;
|
|
4194
4184
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4195
4185
|
const { gridColumn = 'span 12', gridRow = 'span 1', renderDisplay } = schema;
|
|
4196
|
-
const [searchText, setSearchText] = React.useState();
|
|
4197
|
-
const [limit, setLimit] = React.useState(10);
|
|
4198
|
-
const [openSearchResult, setOpenSearchResult] = React.useState();
|
|
4199
|
-
const ref = React.useRef(null);
|
|
4200
4186
|
const colLabel = formI18n.colLabel;
|
|
4201
4187
|
const watchEnum = watch(colLabel);
|
|
4202
4188
|
const watchEnums = (watch(colLabel) ?? []);
|
|
4203
4189
|
const dataList = schema.enum ?? [];
|
|
4204
|
-
|
|
4205
|
-
const
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4190
|
+
// Current value for combobox (array format)
|
|
4191
|
+
const currentValue = isMultiple
|
|
4192
|
+
? watchEnums.filter((val) => val != null && val !== '')
|
|
4193
|
+
: watchEnum
|
|
4194
|
+
? [watchEnum]
|
|
4195
|
+
: [];
|
|
4196
|
+
// Transform enum data for combobox collection
|
|
4197
|
+
const comboboxItems = React.useMemo(() => {
|
|
4198
|
+
return dataList.map((item) => ({
|
|
4199
|
+
label: !!renderDisplay === true
|
|
4200
|
+
? String(renderDisplay(item))
|
|
4201
|
+
: formI18n.t(item),
|
|
4202
|
+
value: item,
|
|
4203
|
+
}));
|
|
4204
|
+
}, [dataList, renderDisplay, formI18n]);
|
|
4205
|
+
// Use filter hook for combobox
|
|
4206
|
+
const { contains } = react.useFilter({ sensitivity: 'base' });
|
|
4207
|
+
// Create collection for combobox
|
|
4208
|
+
const { collection, filter } = react.useListCollection({
|
|
4209
|
+
initialItems: comboboxItems,
|
|
4210
|
+
itemToString: (item) => item.label,
|
|
4211
|
+
itemToValue: (item) => item.value,
|
|
4212
|
+
filter: contains,
|
|
4213
|
+
});
|
|
4214
|
+
// Handle input value change (search)
|
|
4215
|
+
const handleInputValueChange = (details) => {
|
|
4216
|
+
filter(details.inputValue);
|
|
4217
|
+
};
|
|
4218
|
+
// Handle value change
|
|
4219
|
+
const handleValueChange = (details) => {
|
|
4220
|
+
if (isMultiple) {
|
|
4221
|
+
setValue(colLabel, details.value);
|
|
4222
|
+
}
|
|
4223
|
+
else {
|
|
4224
|
+
setValue(colLabel, details.value[0] || '');
|
|
4225
|
+
}
|
|
4209
4226
|
};
|
|
4210
4227
|
if (variant === 'radio') {
|
|
4211
4228
|
return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4212
|
-
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsx(react.RadioGroup.Root, {
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
}
|
|
4219
|
-
const newSet = new Set([...(watchEnums ?? []), item]);
|
|
4220
|
-
setValue(colLabel, [...newSet]);
|
|
4221
|
-
}, value: item, children: [jsxRuntime.jsx(react.RadioGroup.ItemHiddenInput, {}), jsxRuntime.jsx(react.RadioGroup.ItemIndicator, {}), jsxRuntime.jsx(react.RadioGroup.ItemText, { children: !!renderDisplay === true
|
|
4229
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsx(react.RadioGroup.Root, { value: !isMultiple ? watchEnum : undefined, onValueChange: (details) => {
|
|
4230
|
+
if (!isMultiple) {
|
|
4231
|
+
setValue(colLabel, details.value);
|
|
4232
|
+
}
|
|
4233
|
+
}, children: jsxRuntime.jsx(react.HStack, { gap: "6", children: dataList.map((item) => {
|
|
4234
|
+
return (jsxRuntime.jsxs(react.RadioGroup.Item, { value: item, children: [jsxRuntime.jsx(react.RadioGroup.ItemHiddenInput, {}), jsxRuntime.jsx(react.RadioGroup.ItemIndicator, {}), jsxRuntime.jsx(react.RadioGroup.ItemText, { children: !!renderDisplay === true
|
|
4222
4235
|
? renderDisplay(item)
|
|
4223
4236
|
: formI18n.t(item) })] }, `${colLabel}-${item}`));
|
|
4224
4237
|
}) }) }) }));
|
|
4225
4238
|
}
|
|
4226
4239
|
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4227
|
-
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxRuntime.
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
}, justifyContent: 'start', children: !!watchEnum === false ? '' : formI18n.t(watchEnum ?? 'null') })), jsxRuntime.jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: 'bottom-start' }, children: [jsxRuntime.jsx(PopoverTrigger, {}), jsxRuntime.jsx(PopoverContent, { portalled: false, children: jsxRuntime.jsxs(PopoverBody, { display: 'grid', gap: 1, children: [jsxRuntime.jsx(react.Input, { placeholder: enumPickerLabels?.typeToSearch ?? formI18n.t('type_to_search'), onChange: (event) => {
|
|
4242
|
-
onSearchChange(event);
|
|
4243
|
-
setOpenSearchResult(true);
|
|
4244
|
-
}, autoComplete: "off", ref: ref }), jsxRuntime.jsx(PopoverTitle, {}), showTotalAndLimit && (jsxRuntime.jsx(react.Text, { children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${count}, ${enumPickerLabels?.showing ?? formI18n.t('showing')} ${limit}` })), jsxRuntime.jsxs(react.Grid, { overflow: 'auto', maxHeight: '20rem', children: [jsxRuntime.jsx(react.Flex, { flexFlow: 'column wrap', children: dataList
|
|
4245
|
-
.filter((item) => {
|
|
4246
|
-
const searchTerm = (searchText || '').toLowerCase();
|
|
4247
|
-
if (!searchTerm)
|
|
4248
|
-
return true;
|
|
4249
|
-
// Check if the original enum value contains the search text
|
|
4250
|
-
const enumValueMatch = item
|
|
4251
|
-
.toLowerCase()
|
|
4252
|
-
.includes(searchTerm);
|
|
4253
|
-
// Check if the display value (translation) contains the search text
|
|
4254
|
-
const displayValue = !!renderDisplay === true
|
|
4255
|
-
? renderDisplay(item)
|
|
4256
|
-
: formI18n.t(item);
|
|
4257
|
-
// Convert to string and check if it includes the search term
|
|
4258
|
-
const displayValueString = String(displayValue).toLowerCase();
|
|
4259
|
-
const displayValueMatch = displayValueString.includes(searchTerm);
|
|
4260
|
-
return enumValueMatch || displayValueMatch;
|
|
4261
|
-
})
|
|
4262
|
-
.map((item) => {
|
|
4263
|
-
const selected = isMultiple
|
|
4264
|
-
? watchEnums.some((enumValue) => item === enumValue)
|
|
4265
|
-
: watchEnum == item;
|
|
4266
|
-
return (jsxRuntime.jsx(react.Box, { cursor: 'pointer', onClick: () => {
|
|
4267
|
-
if (!isMultiple) {
|
|
4268
|
-
setOpenSearchResult(false);
|
|
4269
|
-
setValue(colLabel, item);
|
|
4270
|
-
return;
|
|
4271
|
-
}
|
|
4272
|
-
const newSet = new Set([...(watchEnums ?? []), item]);
|
|
4273
|
-
setValue(colLabel, [...newSet]);
|
|
4274
|
-
}, ...(selected ? { color: 'colorPalette.400/50' } : {}), children: !!renderDisplay === true
|
|
4275
|
-
? renderDisplay(item)
|
|
4276
|
-
: formI18n.t(item) }, `${colLabel}-${item}`));
|
|
4277
|
-
}) }), isDirty && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: dataList.length <= 0 && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: enumPickerLabels?.emptySearchResult ??
|
|
4278
|
-
formI18n.t('empty_search_result') })) }))] })] }) })] })] }));
|
|
4240
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && currentValue.length > 0 && (jsxRuntime.jsx(react.Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((enumValue) => {
|
|
4241
|
+
if (!enumValue) {
|
|
4242
|
+
return null;
|
|
4243
|
+
}
|
|
4244
|
+
return (jsxRuntime.jsx(Tag, { size: "lg", closable: true, onClick: () => {
|
|
4245
|
+
const newValue = currentValue.filter((val) => val !== enumValue);
|
|
4246
|
+
setValue(colLabel, newValue);
|
|
4247
|
+
}, children: !!renderDisplay === true
|
|
4248
|
+
? renderDisplay(enumValue)
|
|
4249
|
+
: formI18n.t(enumValue) }, enumValue));
|
|
4250
|
+
}) })), jsxRuntime.jsxs(react.Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.Combobox.Input, { placeholder: enumPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxRuntime.jsxs(react.Combobox.IndicatorGroup, { children: [!isMultiple && currentValue.length > 0 && (jsxRuntime.jsx(react.Combobox.ClearTrigger, { onClick: () => {
|
|
4251
|
+
setValue(colLabel, '');
|
|
4252
|
+
} })), jsxRuntime.jsx(react.Combobox.Trigger, {})] })] }), jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [showTotalAndLimit && (jsxRuntime.jsx(react.Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsxRuntime.jsx(react.Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
|
|
4253
|
+
formI18n.t('empty_search_result') })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: item.label }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value))) }))] }) }) })] })] }));
|
|
4279
4254
|
};
|
|
4280
4255
|
|
|
4281
4256
|
function isEnteringWindow(_ref) {
|
|
@@ -4644,13 +4619,13 @@ function formatBytes(bytes) {
|
|
|
4644
4619
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
4645
4620
|
}
|
|
4646
4621
|
|
|
4647
|
-
const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, enabled = true, multiple = false, onFileSelect,
|
|
4622
|
+
const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, enabled = true, multiple = false, onFileSelect, selectedFile: controlledSelectedFile, onSelectedFileChange, }) => {
|
|
4648
4623
|
const [searchTerm, setSearchTerm] = React.useState('');
|
|
4649
|
-
const [
|
|
4624
|
+
const [internalSelectedFile, setInternalSelectedFile] = React.useState(multiple ? [] : undefined);
|
|
4650
4625
|
const [failedImageIds, setFailedImageIds] = React.useState(new Set());
|
|
4651
|
-
// Use controlled or internal state for
|
|
4652
|
-
const
|
|
4653
|
-
const
|
|
4626
|
+
// Use controlled or internal state for selectedFile
|
|
4627
|
+
const selectedFile = controlledSelectedFile ?? internalSelectedFile;
|
|
4628
|
+
const setSelectedFile = onSelectedFileChange ?? setInternalSelectedFile;
|
|
4654
4629
|
const { data: filesData, isLoading, isError, } = reactQuery.useQuery({
|
|
4655
4630
|
queryKey: ['file-picker-library', searchTerm],
|
|
4656
4631
|
queryFn: async () => {
|
|
@@ -4665,23 +4640,23 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
|
|
|
4665
4640
|
const filteredFiles = filterImageOnly
|
|
4666
4641
|
? files.filter((file) => /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name))
|
|
4667
4642
|
: files;
|
|
4668
|
-
const handleFileClick = (
|
|
4643
|
+
const handleFileClick = (file) => {
|
|
4669
4644
|
if (multiple) {
|
|
4670
|
-
const currentSelection = Array.isArray(
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
setSelectedFileId(newSelection);
|
|
4645
|
+
const currentSelection = Array.isArray(selectedFile) ? selectedFile : [];
|
|
4646
|
+
const isAlreadySelected = currentSelection.some((f) => f.id === file.id);
|
|
4647
|
+
const newSelection = isAlreadySelected
|
|
4648
|
+
? currentSelection.filter((f) => f.id !== file.id)
|
|
4649
|
+
: [...currentSelection, file];
|
|
4650
|
+
setSelectedFile(newSelection);
|
|
4677
4651
|
if (onFileSelect) {
|
|
4678
4652
|
onFileSelect(newSelection);
|
|
4679
4653
|
}
|
|
4680
4654
|
}
|
|
4681
4655
|
else {
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4656
|
+
const newFile = selectedFile === file ? undefined : file;
|
|
4657
|
+
setSelectedFile(newFile);
|
|
4658
|
+
if (onFileSelect && newFile) {
|
|
4659
|
+
onFileSelect(newFile);
|
|
4685
4660
|
}
|
|
4686
4661
|
}
|
|
4687
4662
|
};
|
|
@@ -4708,9 +4683,10 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
|
|
|
4708
4683
|
}, children: labels?.loadingFailed ?? 'Failed to load files' }) })), !isLoading && !isError && (jsxRuntime.jsx(react.Box, { maxHeight: "400px", overflowY: "auto", children: filteredFiles.length === 0 ? (jsxRuntime.jsx(react.Box, { textAlign: "center", py: 8, children: jsxRuntime.jsx(react.Text, { color: "fg.muted", children: labels?.noFilesFound ?? 'No files found' }) })) : (jsxRuntime.jsx(react.VStack, { align: "stretch", gap: 2, children: filteredFiles.map((file) => {
|
|
4709
4684
|
const isImage = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name);
|
|
4710
4685
|
const isSelected = multiple
|
|
4711
|
-
? Array.isArray(
|
|
4712
|
-
|
|
4713
|
-
:
|
|
4686
|
+
? Array.isArray(selectedFile) &&
|
|
4687
|
+
selectedFile.some((f) => f.id === file.id)
|
|
4688
|
+
: selectedFile?.id ===
|
|
4689
|
+
file.id;
|
|
4714
4690
|
const imageFailed = failedImageIds.has(file.id);
|
|
4715
4691
|
return (jsxRuntime.jsx(react.Box, { p: 3, border: "2px solid", borderColor: isSelected
|
|
4716
4692
|
? {
|
|
@@ -4722,7 +4698,7 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
|
|
|
4722
4698
|
base: 'colorPalette.50',
|
|
4723
4699
|
_dark: 'colorPalette.900/20',
|
|
4724
4700
|
}
|
|
4725
|
-
: 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => handleFileClick(file
|
|
4701
|
+
: 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => handleFileClick(file), _hover: {
|
|
4726
4702
|
borderColor: isSelected
|
|
4727
4703
|
? {
|
|
4728
4704
|
base: 'colorPalette.600',
|
|
@@ -4747,22 +4723,22 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
|
|
|
4747
4723
|
}) })) }))] }));
|
|
4748
4724
|
};
|
|
4749
4725
|
|
|
4750
|
-
function
|
|
4751
|
-
const [
|
|
4726
|
+
function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, translate, colLabel, }) {
|
|
4727
|
+
const [selectedFile, setSelectedFile] = React.useState(undefined);
|
|
4752
4728
|
const [activeTab, setActiveTab] = React.useState('browse');
|
|
4753
4729
|
const [uploadingFiles, setUploadingFiles] = React.useState(new Set());
|
|
4754
4730
|
const [uploadErrors, setUploadErrors] = React.useState(new Map());
|
|
4755
4731
|
const handleSelect = () => {
|
|
4756
|
-
if (
|
|
4757
|
-
onSelect(
|
|
4732
|
+
if (selectedFile) {
|
|
4733
|
+
onSelect(selectedFile);
|
|
4758
4734
|
onClose();
|
|
4759
|
-
|
|
4735
|
+
setSelectedFile(undefined);
|
|
4760
4736
|
setActiveTab('browse');
|
|
4761
4737
|
}
|
|
4762
4738
|
};
|
|
4763
4739
|
const handleClose = () => {
|
|
4764
4740
|
onClose();
|
|
4765
|
-
|
|
4741
|
+
setSelectedFile(undefined);
|
|
4766
4742
|
setActiveTab('browse');
|
|
4767
4743
|
setUploadingFiles(new Set());
|
|
4768
4744
|
setUploadErrors(new Map());
|
|
@@ -4780,16 +4756,23 @@ function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = fa
|
|
|
4780
4756
|
});
|
|
4781
4757
|
try {
|
|
4782
4758
|
const fileId = await onUploadFile(file);
|
|
4783
|
-
|
|
4759
|
+
// Create a minimal FilePickerMediaFile object from the uploaded file
|
|
4760
|
+
const uploadedFile = {
|
|
4761
|
+
id: fileId,
|
|
4762
|
+
name: file.name,
|
|
4763
|
+
size: file.size,
|
|
4764
|
+
type: file.type,
|
|
4765
|
+
};
|
|
4766
|
+
setSelectedFile(uploadedFile);
|
|
4784
4767
|
setUploadingFiles((prev) => {
|
|
4785
4768
|
const newSet = new Set(prev);
|
|
4786
4769
|
newSet.delete(fileKey);
|
|
4787
4770
|
return newSet;
|
|
4788
4771
|
});
|
|
4789
4772
|
// Auto-select and close in single-select mode
|
|
4790
|
-
onSelect(
|
|
4773
|
+
onSelect(uploadedFile);
|
|
4791
4774
|
onClose();
|
|
4792
|
-
|
|
4775
|
+
setSelectedFile(undefined);
|
|
4793
4776
|
setActiveTab('browse');
|
|
4794
4777
|
}
|
|
4795
4778
|
catch (error) {
|
|
@@ -4813,7 +4796,7 @@ function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = fa
|
|
|
4813
4796
|
translate(removeIndex(`${colLabel}.browse_tab`)) ??
|
|
4814
4797
|
'Browse Library' }), jsxRuntime.jsx(react.Tabs.Trigger, { value: "upload", children: labels?.uploadTab ??
|
|
4815
4798
|
translate(removeIndex(`${colLabel}.upload_tab`)) ??
|
|
4816
|
-
'Upload Files' })] }), jsxRuntime.jsx(react.Tabs.Content, { value: "browse", children: onFetchFiles && (jsxRuntime.jsx(MediaLibraryBrowser, { onFetchFiles: onFetchFiles, filterImageOnly: filterImageOnly, labels: labels, enabled: open && activeTab === 'browse',
|
|
4799
|
+
'Upload Files' })] }), jsxRuntime.jsx(react.Tabs.Content, { value: "browse", children: onFetchFiles && (jsxRuntime.jsx(MediaLibraryBrowser, { onFetchFiles: onFetchFiles, filterImageOnly: filterImageOnly, labels: labels, enabled: open && activeTab === 'browse', selectedFile: selectedFile, onFileSelect: setSelectedFile })) }), jsxRuntime.jsx(react.Tabs.Content, { value: "upload", children: jsxRuntime.jsxs(react.VStack, { align: "stretch", gap: 4, children: [jsxRuntime.jsx(FileDropzone, { onDrop: ({ files }) => handleFileUpload(files), placeholder: labels?.fileDropzone ??
|
|
4817
4800
|
translate(removeIndex(`${colLabel}.fileDropzone`)) ??
|
|
4818
4801
|
'Drop files here or click to upload' }), uploadingFiles.size > 0 && (jsxRuntime.jsx(react.Box, { children: Array.from(uploadingFiles).map((fileKey) => (jsxRuntime.jsx(react.Box, { py: 2, children: jsxRuntime.jsxs(react.HStack, { gap: 2, children: [jsxRuntime.jsx(react.Spinner, { size: "sm", colorPalette: "blue" }), jsxRuntime.jsxs(react.Text, { fontSize: "sm", color: "fg.muted", children: [labels?.uploading ??
|
|
4819
4802
|
translate(removeIndex(`${colLabel}.uploading`)) ??
|
|
@@ -4828,9 +4811,9 @@ function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = fa
|
|
|
4828
4811
|
_dark: 'colorPalette.300',
|
|
4829
4812
|
}, children: [fileKey.split('-')[0], ":", ' ', labels?.uploadFailed ??
|
|
4830
4813
|
translate(removeIndex(`${colLabel}.upload_failed`)) ??
|
|
4831
|
-
'Upload failed', error && ` - ${error}`] }) }, fileKey))) }))] }) })] })) : onFetchFiles ? (jsxRuntime.jsx(MediaLibraryBrowser, { onFetchFiles: onFetchFiles, filterImageOnly: filterImageOnly, labels: labels, enabled: open,
|
|
4814
|
+
'Upload failed', error && ` - ${error}`] }) }, fileKey))) }))] }) })] })) : onFetchFiles ? (jsxRuntime.jsx(MediaLibraryBrowser, { onFetchFiles: onFetchFiles, filterImageOnly: filterImageOnly, labels: labels, enabled: open, selectedFile: selectedFile, onFileSelect: setSelectedFile })) : null }), jsxRuntime.jsx(DialogFooter, { children: jsxRuntime.jsxs(react.HStack, { gap: 3, justify: "end", children: [jsxRuntime.jsx(react.Button, { variant: "outline", onClick: handleClose, borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: labels?.cancel ??
|
|
4832
4815
|
translate(removeIndex(`${colLabel}.cancel`)) ??
|
|
4833
|
-
'Cancel' }), jsxRuntime.jsx(react.Button, { colorPalette: "blue", onClick: handleSelect, disabled: !
|
|
4816
|
+
'Cancel' }), jsxRuntime.jsx(react.Button, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFile, children: labels?.select ??
|
|
4834
4817
|
translate(removeIndex(`${colLabel}.select`)) ??
|
|
4835
4818
|
'Select' })] }) })] }) }));
|
|
4836
4819
|
}
|
|
@@ -4929,17 +4912,70 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
4929
4912
|
const colLabel = formI18n.colLabel;
|
|
4930
4913
|
const [dialogOpen, setDialogOpen] = React.useState(false);
|
|
4931
4914
|
const [failedImageIds, setFailedImageIds] = React.useState(new Set());
|
|
4915
|
+
// Map of file ID to FilePickerMediaFile for display
|
|
4916
|
+
const [fileMap, setFileMap] = React.useState(new Map());
|
|
4932
4917
|
const { onFetchFiles, filterImageOnly = false, enableUpload = false, onUploadFile, } = filePicker || {};
|
|
4918
|
+
// Fetch file details for existing file IDs
|
|
4919
|
+
React.useEffect(() => {
|
|
4920
|
+
if (!onFetchFiles || currentFileIds.length === 0)
|
|
4921
|
+
return;
|
|
4922
|
+
const fetchFileDetails = async () => {
|
|
4923
|
+
setFileMap((prevMap) => {
|
|
4924
|
+
const filesToFetch = currentFileIds.filter((id) => !prevMap.has(id));
|
|
4925
|
+
if (filesToFetch.length === 0)
|
|
4926
|
+
return prevMap;
|
|
4927
|
+
// Fetch all files and filter for the ones we need
|
|
4928
|
+
onFetchFiles('')
|
|
4929
|
+
.then((allFiles) => {
|
|
4930
|
+
setFileMap((currentMap) => {
|
|
4931
|
+
const newFileMap = new Map(currentMap);
|
|
4932
|
+
filesToFetch.forEach((id) => {
|
|
4933
|
+
const file = allFiles.find((f) => f.id === id);
|
|
4934
|
+
if (file) {
|
|
4935
|
+
newFileMap.set(id, file);
|
|
4936
|
+
}
|
|
4937
|
+
});
|
|
4938
|
+
return newFileMap;
|
|
4939
|
+
});
|
|
4940
|
+
})
|
|
4941
|
+
.catch((error) => {
|
|
4942
|
+
console.error('Failed to fetch file details:', error);
|
|
4943
|
+
});
|
|
4944
|
+
return prevMap;
|
|
4945
|
+
});
|
|
4946
|
+
};
|
|
4947
|
+
fetchFileDetails();
|
|
4948
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4949
|
+
}, [currentFileIds.join(','), onFetchFiles]);
|
|
4950
|
+
// Clean up fileMap when files are removed
|
|
4951
|
+
React.useEffect(() => {
|
|
4952
|
+
setFileMap((prevMap) => {
|
|
4953
|
+
const currentIds = new Set(currentFileIds);
|
|
4954
|
+
const newFileMap = new Map();
|
|
4955
|
+
prevMap.forEach((file, id) => {
|
|
4956
|
+
if (currentIds.has(id)) {
|
|
4957
|
+
newFileMap.set(id, file);
|
|
4958
|
+
}
|
|
4959
|
+
});
|
|
4960
|
+
return newFileMap.size !== prevMap.size ? newFileMap : prevMap;
|
|
4961
|
+
});
|
|
4962
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4963
|
+
}, [currentFileIds.join(',')]);
|
|
4933
4964
|
if (!onFetchFiles) {
|
|
4934
4965
|
return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4935
4966
|
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsxRuntime.jsx(react.Text, { color: "fg.muted", children: "Media library browser requires onFetchFiles" }) }));
|
|
4936
4967
|
}
|
|
4937
|
-
const
|
|
4968
|
+
const handleImageError = (fileIdentifier) => {
|
|
4969
|
+
setFailedImageIds((prev) => new Set(prev).add(fileIdentifier));
|
|
4970
|
+
};
|
|
4971
|
+
const handleMediaLibrarySelect = (file) => {
|
|
4972
|
+
// Store the file in the map for display
|
|
4973
|
+
setFileMap((prev) => new Map(prev).set(file.id, file));
|
|
4938
4974
|
if (isSingleSelect) {
|
|
4939
|
-
setValue(colLabel,
|
|
4975
|
+
setValue(colLabel, file.id);
|
|
4940
4976
|
}
|
|
4941
4977
|
else {
|
|
4942
|
-
const newFileIds = [...currentFileIds,
|
|
4978
|
+
const newFileIds = [...currentFileIds, file.id];
|
|
4943
4979
|
setValue(colLabel, newFileIds);
|
|
4944
4980
|
}
|
|
4945
4981
|
};
|
|
@@ -4952,33 +4988,27 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
4952
4988
|
setValue(colLabel, newFileIds);
|
|
4953
4989
|
}
|
|
4954
4990
|
};
|
|
4955
|
-
const isImageId = (fileId) => {
|
|
4956
|
-
return /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(fileId);
|
|
4957
|
-
};
|
|
4958
4991
|
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4959
4992
|
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 ??
|
|
4960
4993
|
formI18n.t('browse_library') ??
|
|
4961
|
-
'Browse from Library' }) }), jsxRuntime.jsx(
|
|
4994
|
+
'Browse from Library' }) }), jsxRuntime.jsx(MediaBrowserDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
|
|
4962
4995
|
formI18n.t('dialog_title') ??
|
|
4963
4996
|
'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, onUploadFile: onUploadFile, enableUpload: enableUpload, labels: filePickerLabels, translate: formI18n.t, colLabel: colLabel }), jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
|
|
4964
|
-
const
|
|
4997
|
+
const file = fileMap.get(fileId);
|
|
4998
|
+
const isImage = file
|
|
4999
|
+
? /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name)
|
|
5000
|
+
: /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(fileId);
|
|
4965
5001
|
const imageFailed = failedImageIds.has(fileId);
|
|
5002
|
+
const displayName = file?.name ?? fileId;
|
|
4966
5003
|
return (jsxRuntime.jsx(react.Card.Root, { variant: 'subtle', colorPalette: "blue", children: jsxRuntime.jsxs(react.Card.Body, { gap: "2", cursor: 'pointer', onClick: () => handleRemove(index), display: 'flex', flexFlow: 'row', alignItems: 'center', padding: '2', border: "2px solid", borderColor: "border.default", borderRadius: "md", _hover: {
|
|
4967
5004
|
borderColor: 'colorPalette.300',
|
|
4968
5005
|
bg: 'bg.muted',
|
|
4969
|
-
}, transition: "all 0.2s", children: [jsxRuntime.jsx(react.Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, marginRight: "2", children: isImage && !imageFailed ? (jsxRuntime.jsx(react.Icon, { as: lu.LuImage, boxSize: 6, color: "fg.muted" })) : (jsxRuntime.jsx(react.Icon, { as: lu.LuFile, boxSize: 6, color: "fg.muted" })) }), jsxRuntime.
|
|
5006
|
+
}, transition: "all 0.2s", children: [jsxRuntime.jsx(react.Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, marginRight: "2", overflow: "hidden", children: isImage && file?.url && !imageFailed ? (jsxRuntime.jsx(react.Image, { src: file.url, alt: displayName, boxSize: "60px", objectFit: "cover", onError: () => handleImageError(fileId) })) : isImage && !imageFailed ? (jsxRuntime.jsx(react.Icon, { as: lu.LuImage, boxSize: 6, color: "fg.muted" })) : (jsxRuntime.jsx(react.Icon, { as: lu.LuFile, boxSize: 6, color: "fg.muted" })) }), jsxRuntime.jsxs(react.VStack, { align: "start", flex: 1, gap: 1, children: [jsxRuntime.jsx(react.Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: displayName }), file?.size && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
|
|
5007
|
+
? `${(file.size / 1024).toFixed(1)} KB`
|
|
5008
|
+
: file.size }))] }), jsxRuntime.jsx(react.Icon, { as: ti.TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, `${fileId}-${index}`));
|
|
4970
5009
|
}) })] }));
|
|
4971
5010
|
};
|
|
4972
5011
|
|
|
4973
|
-
const ToggleTip = React__namespace.forwardRef(function ToggleTip(props, ref) {
|
|
4974
|
-
const { showArrow, children, portalled = true, content, portalRef, ...rest } = props;
|
|
4975
|
-
return (jsxRuntime.jsxs(react.Popover.Root, { ...rest, positioning: { ...rest.positioning, gutter: 4 }, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: children }), jsxRuntime.jsx(react.Portal, { disabled: !portalled, container: portalRef, children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsxs(react.Popover.Content, { width: "auto", px: "2", py: "1", textStyle: "xs", rounded: "sm", ref: ref, children: [showArrow && (jsxRuntime.jsx(react.Popover.Arrow, { children: jsxRuntime.jsx(react.Popover.ArrowTip, {}) })), content] }) }) })] }));
|
|
4976
|
-
});
|
|
4977
|
-
const InfoTip = React__namespace.forwardRef(function InfoTip(props, ref) {
|
|
4978
|
-
const { children, ...rest } = props;
|
|
4979
|
-
return (jsxRuntime.jsx(ToggleTip, { content: children, ...rest, ref: ref, children: jsxRuntime.jsx(react.IconButton, { variant: "ghost", "aria-label": "info", size: "2xs", colorPalette: "colorPalette", children: jsxRuntime.jsx(hi.HiOutlineInformationCircle, {}) }) }));
|
|
4980
|
-
});
|
|
4981
|
-
|
|
4982
5012
|
const getTableData = async ({ serverUrl, in_table, searching = "", where = [], limit = 10, offset = 0, }) => {
|
|
4983
5013
|
if (serverUrl === undefined || serverUrl.length == 0) {
|
|
4984
5014
|
throw new Error("The serverUrl is missing");
|
|
@@ -5015,10 +5045,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5015
5045
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5016
5046
|
const { table, column: column_ref, display_column, customQueryFn, } = foreign_key;
|
|
5017
5047
|
const [searchText, setSearchText] = React.useState('');
|
|
5018
|
-
const [limit
|
|
5019
|
-
const [openSearchResult, setOpenSearchResult] = React.useState();
|
|
5020
|
-
const [page, setPage] = React.useState(0);
|
|
5021
|
-
const ref = React.useRef(null);
|
|
5048
|
+
const [limit] = React.useState(50); // Increased limit for combobox
|
|
5022
5049
|
const colLabel = formI18n.colLabel;
|
|
5023
5050
|
const watchedValue = watch(colLabel);
|
|
5024
5051
|
const watchId = !isMultiple ? watchedValue : undefined;
|
|
@@ -5033,22 +5060,25 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5033
5060
|
: [];
|
|
5034
5061
|
// Use watched values if they exist (including empty string for single select),
|
|
5035
5062
|
// otherwise fall back to initial values from getValues()
|
|
5036
|
-
// This ensures the query can trigger on mount with initial values
|
|
5037
|
-
// For single: use watchId if it's not undefined/null, otherwise use initialId
|
|
5038
|
-
// For multiple: use watchIds if watchedValue is defined, otherwise use initialIds
|
|
5039
5063
|
const currentId = watchId !== undefined && watchId !== null ? watchId : initialId;
|
|
5040
5064
|
const currentIds = watchedValue !== undefined && watchedValue !== null && isMultiple
|
|
5041
5065
|
? watchIds
|
|
5042
5066
|
: initialIds;
|
|
5043
|
-
//
|
|
5067
|
+
// Current value for combobox (array format)
|
|
5068
|
+
const currentValue = isMultiple
|
|
5069
|
+
? currentIds.filter((id) => id != null && id !== '')
|
|
5070
|
+
: currentId
|
|
5071
|
+
? [currentId]
|
|
5072
|
+
: [];
|
|
5073
|
+
// Query for search results (async loading)
|
|
5044
5074
|
const query = reactQuery.useQuery({
|
|
5045
|
-
queryKey: [`idpicker`, { column, searchText, limit
|
|
5075
|
+
queryKey: [`idpicker`, { column, searchText, limit }],
|
|
5046
5076
|
queryFn: async () => {
|
|
5047
5077
|
if (customQueryFn) {
|
|
5048
5078
|
const { data, idMap } = await customQueryFn({
|
|
5049
5079
|
searching: searchText ?? '',
|
|
5050
5080
|
limit: limit,
|
|
5051
|
-
offset:
|
|
5081
|
+
offset: 0,
|
|
5052
5082
|
});
|
|
5053
5083
|
setIdMap((state) => {
|
|
5054
5084
|
return { ...state, ...idMap };
|
|
@@ -5060,7 +5090,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5060
5090
|
searching: searchText ?? '',
|
|
5061
5091
|
in_table: table,
|
|
5062
5092
|
limit: limit,
|
|
5063
|
-
offset:
|
|
5093
|
+
offset: 0,
|
|
5064
5094
|
});
|
|
5065
5095
|
const newMap = Object.fromEntries((data ?? { data: [] }).data.map((item) => {
|
|
5066
5096
|
return [
|
|
@@ -5075,12 +5105,11 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5075
5105
|
});
|
|
5076
5106
|
return data;
|
|
5077
5107
|
},
|
|
5078
|
-
enabled:
|
|
5108
|
+
enabled: true, // Always enabled for combobox
|
|
5079
5109
|
staleTime: 300000,
|
|
5080
5110
|
});
|
|
5081
5111
|
// Query for currently selected items (to display them properly)
|
|
5082
|
-
|
|
5083
|
-
const queryDefault = reactQuery.useQuery({
|
|
5112
|
+
reactQuery.useQuery({
|
|
5084
5113
|
queryKey: [
|
|
5085
5114
|
`idpicker-default`,
|
|
5086
5115
|
{
|
|
@@ -5090,11 +5119,9 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5090
5119
|
},
|
|
5091
5120
|
],
|
|
5092
5121
|
queryFn: async () => {
|
|
5093
|
-
// Use current values (which include initial) for the query
|
|
5094
5122
|
const queryId = currentId;
|
|
5095
5123
|
const queryIds = currentIds;
|
|
5096
5124
|
if (customQueryFn) {
|
|
5097
|
-
// For customQueryFn, pass where clause to fetch specific IDs
|
|
5098
5125
|
const { data, idMap } = await customQueryFn({
|
|
5099
5126
|
searching: '',
|
|
5100
5127
|
limit: isMultiple ? queryIds.length : 1,
|
|
@@ -5109,10 +5136,9 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5109
5136
|
if (!queryId && (!queryIds || queryIds.length === 0)) {
|
|
5110
5137
|
return { data: [] };
|
|
5111
5138
|
}
|
|
5112
|
-
const searchValue = isMultiple ? queryIds.join(',') : queryId;
|
|
5113
5139
|
const data = await getTableData({
|
|
5114
5140
|
serverUrl,
|
|
5115
|
-
searching:
|
|
5141
|
+
searching: '',
|
|
5116
5142
|
in_table: table,
|
|
5117
5143
|
where: [{ id: column_ref, value: isMultiple ? queryIds : queryId }],
|
|
5118
5144
|
limit: isMultiple ? queryIds.length : 1,
|
|
@@ -5135,101 +5161,80 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5135
5161
|
? Array.isArray(currentIds) && currentIds.length > 0
|
|
5136
5162
|
: !!currentId,
|
|
5137
5163
|
});
|
|
5138
|
-
// Effect to trigger initial data fetch when popover opens
|
|
5139
|
-
React.useEffect(() => {
|
|
5140
|
-
if (openSearchResult) {
|
|
5141
|
-
// Reset search text when opening the popover
|
|
5142
|
-
setSearchText('');
|
|
5143
|
-
// Reset page to first page
|
|
5144
|
-
setPage(0);
|
|
5145
|
-
// Fetch initial data
|
|
5146
|
-
query.refetch();
|
|
5147
|
-
}
|
|
5148
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5149
|
-
}, [openSearchResult]);
|
|
5150
|
-
const onSearchChange = async (event) => {
|
|
5151
|
-
setSearchText(event.target.value);
|
|
5152
|
-
setPage(0);
|
|
5153
|
-
query.refetch();
|
|
5154
|
-
};
|
|
5155
|
-
const handleLimitChange = (event) => {
|
|
5156
|
-
const newLimit = Number(event.target.value);
|
|
5157
|
-
setLimit(newLimit);
|
|
5158
|
-
// Reset to first page when changing limit
|
|
5159
|
-
setPage(0);
|
|
5160
|
-
// Trigger a new search with the updated limit
|
|
5161
|
-
query.refetch();
|
|
5162
|
-
};
|
|
5163
5164
|
const { isLoading, isFetching, data, isPending, isError } = query;
|
|
5164
5165
|
const dataList = data?.data ?? [];
|
|
5165
|
-
|
|
5166
|
-
const
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5166
|
+
// Transform data for combobox collection
|
|
5167
|
+
const comboboxItems = React.useMemo(() => {
|
|
5168
|
+
return dataList.map((item) => ({
|
|
5169
|
+
label: !!renderDisplay === true
|
|
5170
|
+
? String(renderDisplay(item))
|
|
5171
|
+
: String(item[display_column] ?? ''),
|
|
5172
|
+
value: String(item[column_ref]),
|
|
5173
|
+
raw: item,
|
|
5174
|
+
}));
|
|
5175
|
+
}, [dataList, display_column, column_ref, renderDisplay]);
|
|
5176
|
+
// Use filter hook for combobox
|
|
5177
|
+
const { contains } = react.useFilter({ sensitivity: 'base' });
|
|
5178
|
+
// Create collection for combobox
|
|
5179
|
+
const { collection, filter, set } = react.useListCollection({
|
|
5180
|
+
initialItems: comboboxItems,
|
|
5181
|
+
itemToString: (item) => item.label,
|
|
5182
|
+
itemToValue: (item) => item.value,
|
|
5183
|
+
filter: contains,
|
|
5184
|
+
});
|
|
5185
|
+
// Handle input value change (search)
|
|
5186
|
+
const handleInputValueChange = (details) => {
|
|
5187
|
+
setSearchText(details.inputValue);
|
|
5188
|
+
// Filter will be applied after data is fetched
|
|
5189
|
+
};
|
|
5190
|
+
// Handle value change
|
|
5191
|
+
const handleValueChange = (details) => {
|
|
5192
|
+
if (isMultiple) {
|
|
5193
|
+
setValue(colLabel, details.value);
|
|
5174
5194
|
}
|
|
5175
|
-
|
|
5176
|
-
|
|
5195
|
+
else {
|
|
5196
|
+
setValue(colLabel, details.value[0] || '');
|
|
5177
5197
|
}
|
|
5178
|
-
return record[display_column];
|
|
5179
5198
|
};
|
|
5199
|
+
// Debounce search to avoid too many API calls and update collection after data loads
|
|
5200
|
+
React.useEffect(() => {
|
|
5201
|
+
const timer = setTimeout(() => {
|
|
5202
|
+
if (searchText !== undefined) {
|
|
5203
|
+
query.refetch();
|
|
5204
|
+
}
|
|
5205
|
+
}, 300);
|
|
5206
|
+
return () => clearTimeout(timer);
|
|
5207
|
+
}, [searchText, query]);
|
|
5208
|
+
// Update collection and filter when data changes
|
|
5209
|
+
React.useEffect(() => {
|
|
5210
|
+
if (dataList.length > 0) {
|
|
5211
|
+
set(comboboxItems);
|
|
5212
|
+
// Apply filter to the collection
|
|
5213
|
+
if (searchText) {
|
|
5214
|
+
filter(searchText);
|
|
5215
|
+
}
|
|
5216
|
+
}
|
|
5217
|
+
}, [dataList, comboboxItems, set, filter, searchText]);
|
|
5180
5218
|
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5181
|
-
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxRuntime.
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
border: '1px solid #ccc',
|
|
5201
|
-
fontSize: '14px',
|
|
5202
|
-
}, children: [jsxRuntime.jsx("option", { value: "5", children: "5" }), jsxRuntime.jsx("option", { value: "10", children: "10" }), jsxRuntime.jsx("option", { value: "20", children: "20" }), jsxRuntime.jsx("option", { value: "30", children: "30" })] }) })] }), jsxRuntime.jsx(react.Grid, { overflowY: 'auto', children: dataList.length > 0 ? (jsxRuntime.jsx(react.Flex, { flexFlow: 'column wrap', gap: 1, children: dataList.map((item) => {
|
|
5203
|
-
const selected = isMultiple
|
|
5204
|
-
? watchIds.some((id) => item[column_ref] === id)
|
|
5205
|
-
: watchId === item[column_ref];
|
|
5206
|
-
return (jsxRuntime.jsx(react.Box, { cursor: 'pointer', onClick: () => {
|
|
5207
|
-
if (!isMultiple) {
|
|
5208
|
-
setOpenSearchResult(false);
|
|
5209
|
-
setValue(colLabel, item[column_ref]);
|
|
5210
|
-
return;
|
|
5211
|
-
}
|
|
5212
|
-
// For multiple selection, don't add if already selected
|
|
5213
|
-
if (selected)
|
|
5214
|
-
return;
|
|
5215
|
-
const newSet = new Set([
|
|
5216
|
-
...(watchIds ?? []),
|
|
5217
|
-
item[column_ref],
|
|
5218
|
-
]);
|
|
5219
|
-
setValue(colLabel, [...newSet]);
|
|
5220
|
-
}, opacity: 0.7, _hover: { opacity: 1 }, ...(selected
|
|
5221
|
-
? {
|
|
5222
|
-
color: 'colorPalette.400/50',
|
|
5223
|
-
fontWeight: 'bold',
|
|
5224
|
-
}
|
|
5225
|
-
: {}), children: !!renderDisplay === true
|
|
5226
|
-
? renderDisplay(item)
|
|
5227
|
-
: item[display_column] }, item[column_ref]));
|
|
5228
|
-
}) })) : (jsxRuntime.jsx(react.Text, { children: searchText
|
|
5229
|
-
? idPickerLabels?.emptySearchResult ??
|
|
5230
|
-
formI18n.t('empty_search_result')
|
|
5231
|
-
: idPickerLabels?.initialResults ??
|
|
5232
|
-
formI18n.t('initial_results') })) }), jsxRuntime.jsx(PaginationRoot, { justifySelf: 'center', count: count, pageSize: limit, defaultPage: 1, page: page + 1, onPageChange: (e) => setPage(e.page - 1), children: jsxRuntime.jsxs(react.HStack, { gap: "4", children: [jsxRuntime.jsx(PaginationPrevTrigger, {}), count > 0 && jsxRuntime.jsx(PaginationPageText, {}), jsxRuntime.jsx(PaginationNextTrigger, {})] }) })] }))] }) })] })] }));
|
|
5219
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && currentValue.length > 0 && (jsxRuntime.jsx(react.Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((id) => {
|
|
5220
|
+
const item = idMap[id];
|
|
5221
|
+
if (item === undefined) {
|
|
5222
|
+
return (jsxRuntime.jsx(react.Text, { fontSize: "sm", children: idPickerLabels?.undefined ?? formI18n.t('undefined') }, id));
|
|
5223
|
+
}
|
|
5224
|
+
return (jsxRuntime.jsx(Tag, { closable: true, onClick: () => {
|
|
5225
|
+
const newValue = currentValue.filter((itemId) => itemId !== id);
|
|
5226
|
+
setValue(colLabel, newValue);
|
|
5227
|
+
}, children: !!renderDisplay === true
|
|
5228
|
+
? renderDisplay(item)
|
|
5229
|
+
: item[display_column] }, id));
|
|
5230
|
+
}) })), jsxRuntime.jsxs(react.Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.Combobox.Input, { placeholder: idPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxRuntime.jsxs(react.Combobox.IndicatorGroup, { children: [(isFetching || isLoading || isPending) && (jsxRuntime.jsx(react.Spinner, { size: "xs" })), isError && (jsxRuntime.jsx(react.Icon, { color: "fg.error", children: jsxRuntime.jsx(bi.BiError, {}) })), !isMultiple && currentValue.length > 0 && (jsxRuntime.jsx(react.Combobox.ClearTrigger, { onClick: () => {
|
|
5231
|
+
setValue(colLabel, '');
|
|
5232
|
+
} })), jsxRuntime.jsx(react.Combobox.Trigger, {})] })] }), jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsx(react.Combobox.Content, { children: isFetching || isLoading || isPending ? (jsxRuntime.jsxs(react.HStack, { p: 2, justify: "center", children: [jsxRuntime.jsx(react.Spinner, { size: "xs" }), jsxRuntime.jsx(react.Text, { fontSize: "sm", children: idPickerLabels?.loading ?? formI18n.t('loading') })] })) : isError ? (jsxRuntime.jsx(react.Text, { p: 2, color: "fg.error", fontSize: "sm", children: idPickerLabels?.loadingFailed ??
|
|
5233
|
+
formI18n.t('loading_failed') })) : collection.items.length === 0 ? (jsxRuntime.jsx(react.Combobox.Empty, { children: searchText
|
|
5234
|
+
? idPickerLabels?.emptySearchResult ??
|
|
5235
|
+
formI18n.t('empty_search_result')
|
|
5236
|
+
: idPickerLabels?.initialResults ??
|
|
5237
|
+
formI18n.t('initial_results') })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: item.label }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value))) })) }) }) })] })] }));
|
|
5233
5238
|
};
|
|
5234
5239
|
|
|
5235
5240
|
const NumberInputRoot = React__namespace.forwardRef(function NumberInput(props, ref) {
|