@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
- const PaginationPageText = React__namespace.forwardRef(function PaginationPageText(props, ref) {
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
- const count = schema.enum?.length ?? 0;
4205
- const isDirty = (searchText?.length ?? 0) > 0;
4206
- const onSearchChange = async (event) => {
4207
- setSearchText(event.target.value);
4208
- setLimit(10);
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, { defaultValue: "1", children: jsxRuntime.jsx(react.HStack, { gap: "6", children: filterArray(dataList, searchText ?? '').map((item) => {
4213
- return (jsxRuntime.jsxs(react.RadioGroup.Item, { onClick: () => {
4214
- if (!isMultiple) {
4215
- setOpenSearchResult(false);
4216
- setValue(colLabel, item);
4217
- return;
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.jsxs(react.Flex, { flexFlow: 'wrap', gap: 1, children: [watchEnums.map((enumValue) => {
4228
- const item = enumValue;
4229
- if (!!item === false) {
4230
- return jsxRuntime.jsx(jsxRuntime.Fragment, {});
4231
- }
4232
- return (jsxRuntime.jsx(Tag, { size: "lg", closable: true, onClick: () => {
4233
- setValue(column, watchEnums.filter((id) => id != item));
4234
- }, children: !!renderDisplay === true
4235
- ? renderDisplay(item)
4236
- : formI18n.t(item) }, item));
4237
- }), jsxRuntime.jsx(Tag, { size: "lg", cursor: 'pointer', onClick: () => {
4238
- setOpenSearchResult(true);
4239
- }, children: enumPickerLabels?.addMore ?? formI18n.t('add_more') }, `${colLabel}-add-more-tag`)] })), !isMultiple && (jsxRuntime.jsx(Button, { variant: 'outline', onClick: () => {
4240
- setOpenSearchResult(true);
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, selectedFileId: controlledSelectedFileId, onSelectedFileIdChange, }) => {
4622
+ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, enabled = true, multiple = false, onFileSelect, selectedFile: controlledSelectedFile, onSelectedFileChange, }) => {
4648
4623
  const [searchTerm, setSearchTerm] = React.useState('');
4649
- const [internalSelectedFileId, setInternalSelectedFileId] = React.useState(multiple ? [] : '');
4624
+ const [internalSelectedFile, setInternalSelectedFile] = React.useState(multiple ? [] : undefined);
4650
4625
  const [failedImageIds, setFailedImageIds] = React.useState(new Set());
4651
- // Use controlled or internal state for selectedFileId
4652
- const selectedFileId = controlledSelectedFileId ?? internalSelectedFileId;
4653
- const setSelectedFileId = onSelectedFileIdChange ?? setInternalSelectedFileId;
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 = (fileId) => {
4643
+ const handleFileClick = (file) => {
4669
4644
  if (multiple) {
4670
- const currentSelection = Array.isArray(selectedFileId)
4671
- ? selectedFileId
4672
- : [];
4673
- const newSelection = currentSelection.includes(fileId)
4674
- ? currentSelection.filter((id) => id !== fileId)
4675
- : [...currentSelection, fileId];
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
- setSelectedFileId(fileId);
4683
- if (onFileSelect) {
4684
- onFileSelect(fileId);
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(selectedFileId) &&
4712
- selectedFileId.includes(file.id)
4713
- : selectedFileId === file.id;
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.id), _hover: {
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 FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, translate, colLabel, }) {
4751
- const [selectedFileId, setSelectedFileId] = React.useState('');
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 (selectedFileId) {
4757
- onSelect(selectedFileId);
4732
+ if (selectedFile) {
4733
+ onSelect(selectedFile);
4758
4734
  onClose();
4759
- setSelectedFileId('');
4735
+ setSelectedFile(undefined);
4760
4736
  setActiveTab('browse');
4761
4737
  }
4762
4738
  };
4763
4739
  const handleClose = () => {
4764
4740
  onClose();
4765
- setSelectedFileId('');
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
- setSelectedFileId(fileId);
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(fileId);
4773
+ onSelect(uploadedFile);
4791
4774
  onClose();
4792
- setSelectedFileId('');
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', selectedFileId: selectedFileId, onSelectedFileIdChange: setSelectedFileId })) }), 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 ??
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, selectedFileId: selectedFileId, onSelectedFileIdChange: setSelectedFileId })) : 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 ??
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: !selectedFileId, children: labels?.select ??
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 handleMediaLibrarySelect = (fileId) => {
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, fileId);
4975
+ setValue(colLabel, file.id);
4940
4976
  }
4941
4977
  else {
4942
- const newFileIds = [...currentFileIds, fileId];
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(FilePickerDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
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 isImage = isImageId(fileId);
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.jsx(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: fileId }) }), jsxRuntime.jsx(react.Icon, { as: ti.TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, `${fileId}-${index}`));
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, setLimit] = React.useState(10);
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
- // Query for search results
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, page }],
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: page * limit,
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: page * limit,
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: openSearchResult === true,
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
- // Use currentId/currentIds in queryKey so it includes initial values and updates when watched values change
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: searchValue,
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
- const count = data?.count ?? 0;
5166
- const getPickedValue = () => {
5167
- if (Object.keys(idMap).length <= 0) {
5168
- return '';
5169
- }
5170
- // Use currentId which includes initial values
5171
- const record = idMap[currentId];
5172
- if (record === undefined) {
5173
- return '';
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
- if (!!renderDisplay === true) {
5176
- return renderDisplay(record);
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.jsxs(react.Flex, { flexFlow: 'wrap', gap: 1, children: [watchIds.map((id) => {
5182
- const item = idMap[id];
5183
- if (item === undefined) {
5184
- return (jsxRuntime.jsx(react.Text, { children: idPickerLabels?.undefined ?? formI18n.t('undefined') }, id));
5185
- }
5186
- return (jsxRuntime.jsx(Tag, { closable: true, onClick: () => {
5187
- setValue(colLabel, watchIds.filter((itemId) => itemId !== item[column_ref]));
5188
- }, children: !!renderDisplay === true
5189
- ? renderDisplay(item)
5190
- : item[display_column] }, id));
5191
- }), jsxRuntime.jsx(Tag, { cursor: 'pointer', onClick: () => {
5192
- setOpenSearchResult(true);
5193
- }, children: idPickerLabels?.addMore ?? formI18n.t('add_more') })] })), !isMultiple && (jsxRuntime.jsx(Button, { variant: 'outline', onClick: () => {
5194
- setOpenSearchResult(true);
5195
- }, justifyContent: 'start', children: queryDefault.isLoading ? jsxRuntime.jsx(react.Spinner, { size: "sm" }) : getPickedValue() })), jsxRuntime.jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: 'bottom-start', strategy: 'fixed' }, children: [jsxRuntime.jsx(PopoverTrigger, {}), jsxRuntime.jsx(PopoverContent, { portalled: false, children: jsxRuntime.jsxs(PopoverBody, { display: 'grid', gap: 1, children: [jsxRuntime.jsx(react.Input, { placeholder: idPickerLabels?.typeToSearch ?? formI18n.t('type_to_search'), onChange: onSearchChange, autoComplete: "off", ref: ref, value: searchText }), jsxRuntime.jsx(PopoverTitle, {}), openSearchResult && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(isFetching || isLoading || isPending) && jsxRuntime.jsx(react.Spinner, {}), isError && (jsxRuntime.jsx(react.Icon, { color: 'red.400', children: jsxRuntime.jsx(bi.BiError, {}) })), jsxRuntime.jsxs(react.Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxRuntime.jsxs(react.Flex, { alignItems: "center", gap: "2", children: [jsxRuntime.jsx(InfoTip, { children: `${idPickerLabels?.total ?? formI18n.t('total')} ${count}, ${idPickerLabels?.showing ?? formI18n.t('showing')} ${limit} ${idPickerLabels?.perPage ?? formI18n.t('per_page', { defaultValue: 'per page' })}` }), jsxRuntime.jsxs(react.Text, { fontSize: "sm", fontWeight: "bold", children: [count, jsxRuntime.jsxs(react.Text, { as: "span", fontSize: "xs", ml: "1", color: "gray.500", children: ["/", ' ', count > 0
5196
- ? `${page * limit + 1}-${Math.min((page + 1) * limit, count)}`
5197
- : '0'] })] })] }), jsxRuntime.jsx(react.Box, { children: jsxRuntime.jsxs("select", { value: limit, onChange: handleLimitChange, style: {
5198
- padding: '4px 8px',
5199
- borderRadius: '4px',
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) {