@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.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, Tag as Tag$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, CheckboxCard as CheckboxCard$1, Tooltip as Tooltip$1, Group, InputElement, Icon, EmptyState as EmptyState$2, VStack, List, Table as Table$1, Checkbox as Checkbox$1, Card, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Image, Alert, Field as Field$1, Popover, Tabs, NumberInput, Show, RadioCard, CheckboxGroup, Center, Heading, Skeleton } from '@chakra-ui/react';
2
+ import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, Tag as Tag$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, CheckboxCard as CheckboxCard$1, Tooltip as Tooltip$1, Group, InputElement, Icon, EmptyState as EmptyState$2, VStack, List, Table as Table$1, Checkbox as Checkbox$1, Card, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Image, Alert, Field as Field$1, Popover, useFilter, useListCollection, Combobox, Tabs, NumberInput, Show, RadioCard, CheckboxGroup, Center, Heading, Skeleton } from '@chakra-ui/react';
3
3
  import { AiOutlineColumnWidth } from 'react-icons/ai';
4
4
  import * as React from 'react';
5
- import React__default, { createContext, useContext, useState, useEffect, useRef, forwardRef } from 'react';
5
+ import React__default, { createContext, useContext, useState, useEffect, useRef, useMemo, forwardRef } from 'react';
6
6
  import { LuX, LuCheck, LuChevronRight, LuSearch, LuImage, LuFile } from 'react-icons/lu';
7
7
  import { MdOutlineSort, MdFilterAlt, MdSearch, MdOutlineChecklist, MdClear, MdOutlineViewColumn, MdFilterListAlt, MdPushPin, MdCancel, MdDateRange } from 'react-icons/md';
8
8
  import { FaUpDown, FaGripLinesVertical, FaTrash } from 'react-icons/fa6';
@@ -21,7 +21,7 @@ import { useQueryClient, useQuery } from '@tanstack/react-query';
21
21
  import { IoReload } from 'react-icons/io5';
22
22
  import { useDebounce } from '@uidotdev/usehooks';
23
23
  import { BsExclamationCircleFill, BsClock } from 'react-icons/bs';
24
- import { HiColorSwatch, HiOutlineInformationCircle } from 'react-icons/hi';
24
+ import { HiColorSwatch } from 'react-icons/hi';
25
25
  import { flexRender, createColumnHelper, makeStateUpdater, functionalUpdate, useReactTable, getCoreRowModel, getFilteredRowModel, getSortedRowModel, getPaginationRowModel } from '@tanstack/react-table';
26
26
  import { GrAscend, GrDescend } from 'react-icons/gr';
27
27
  import { useTranslation } from 'react-i18next';
@@ -555,7 +555,7 @@ const PaginationItems = (props) => {
555
555
  return page.type === "ellipsis" ? (jsx(PaginationEllipsis, { index: index, ...props }, index)) : (jsx(PaginationItem, { type: "page", value: page.value, ...props }, index));
556
556
  }) }));
557
557
  };
558
- const PaginationPageText = React.forwardRef(function PaginationPageText(props, ref) {
558
+ React.forwardRef(function PaginationPageText(props, ref) {
559
559
  const { format = "compact", ...rest } = props;
560
560
  const { page, totalPages, pageRange, count } = usePaginationContext();
561
561
  const content = React.useMemo(() => {
@@ -4156,16 +4156,6 @@ const DateRangePicker = ({ column, schema, prefix, }) => {
4156
4156
  }, monthsToDisplay: 2 })] }) })] }) }));
4157
4157
  };
4158
4158
 
4159
- function filterArray(array, searchTerm) {
4160
- // Convert the search term to lower case for case-insensitive comparison
4161
- const lowerCaseSearchTerm = searchTerm.toLowerCase();
4162
- // Use the filter method to return an array of matching items
4163
- return array.filter((item) => {
4164
- // Convert each item to a string and check if it includes the search term
4165
- return item.toString().toLowerCase().includes(lowerCaseSearchTerm);
4166
- });
4167
- }
4168
-
4169
4159
  const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLimit = false, }) => {
4170
4160
  const { watch, formState: { errors }, setValue, } = useFormContext();
4171
4161
  const { enumPickerLabels } = useSchemaContext();
@@ -4173,89 +4163,74 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4173
4163
  const { required, variant } = schema;
4174
4164
  const isRequired = required?.some((columnId) => columnId === column);
4175
4165
  const { gridColumn = 'span 12', gridRow = 'span 1', renderDisplay } = schema;
4176
- const [searchText, setSearchText] = useState();
4177
- const [limit, setLimit] = useState(10);
4178
- const [openSearchResult, setOpenSearchResult] = useState();
4179
- const ref = useRef(null);
4180
4166
  const colLabel = formI18n.colLabel;
4181
4167
  const watchEnum = watch(colLabel);
4182
4168
  const watchEnums = (watch(colLabel) ?? []);
4183
4169
  const dataList = schema.enum ?? [];
4184
- const count = schema.enum?.length ?? 0;
4185
- const isDirty = (searchText?.length ?? 0) > 0;
4186
- const onSearchChange = async (event) => {
4187
- setSearchText(event.target.value);
4188
- setLimit(10);
4170
+ // Current value for combobox (array format)
4171
+ const currentValue = isMultiple
4172
+ ? watchEnums.filter((val) => val != null && val !== '')
4173
+ : watchEnum
4174
+ ? [watchEnum]
4175
+ : [];
4176
+ // Transform enum data for combobox collection
4177
+ const comboboxItems = useMemo(() => {
4178
+ return dataList.map((item) => ({
4179
+ label: !!renderDisplay === true
4180
+ ? String(renderDisplay(item))
4181
+ : formI18n.t(item),
4182
+ value: item,
4183
+ }));
4184
+ }, [dataList, renderDisplay, formI18n]);
4185
+ // Use filter hook for combobox
4186
+ const { contains } = useFilter({ sensitivity: 'base' });
4187
+ // Create collection for combobox
4188
+ const { collection, filter } = useListCollection({
4189
+ initialItems: comboboxItems,
4190
+ itemToString: (item) => item.label,
4191
+ itemToValue: (item) => item.value,
4192
+ filter: contains,
4193
+ });
4194
+ // Handle input value change (search)
4195
+ const handleInputValueChange = (details) => {
4196
+ filter(details.inputValue);
4197
+ };
4198
+ // Handle value change
4199
+ const handleValueChange = (details) => {
4200
+ if (isMultiple) {
4201
+ setValue(colLabel, details.value);
4202
+ }
4203
+ else {
4204
+ setValue(colLabel, details.value[0] || '');
4205
+ }
4189
4206
  };
4190
4207
  if (variant === 'radio') {
4191
4208
  return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4192
- gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsx(RadioGroup$1.Root, { defaultValue: "1", children: jsx(HStack, { gap: "6", children: filterArray(dataList, searchText ?? '').map((item) => {
4193
- return (jsxs(RadioGroup$1.Item, { onClick: () => {
4194
- if (!isMultiple) {
4195
- setOpenSearchResult(false);
4196
- setValue(colLabel, item);
4197
- return;
4198
- }
4199
- const newSet = new Set([...(watchEnums ?? []), item]);
4200
- setValue(colLabel, [...newSet]);
4201
- }, value: item, children: [jsx(RadioGroup$1.ItemHiddenInput, {}), jsx(RadioGroup$1.ItemIndicator, {}), jsx(RadioGroup$1.ItemText, { children: !!renderDisplay === true
4209
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsx(RadioGroup$1.Root, { value: !isMultiple ? watchEnum : undefined, onValueChange: (details) => {
4210
+ if (!isMultiple) {
4211
+ setValue(colLabel, details.value);
4212
+ }
4213
+ }, children: jsx(HStack, { gap: "6", children: dataList.map((item) => {
4214
+ return (jsxs(RadioGroup$1.Item, { value: item, children: [jsx(RadioGroup$1.ItemHiddenInput, {}), jsx(RadioGroup$1.ItemIndicator, {}), jsx(RadioGroup$1.ItemText, { children: !!renderDisplay === true
4202
4215
  ? renderDisplay(item)
4203
4216
  : formI18n.t(item) })] }, `${colLabel}-${item}`));
4204
4217
  }) }) }) }));
4205
4218
  }
4206
4219
  return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4207
- gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxs(Flex, { flexFlow: 'wrap', gap: 1, children: [watchEnums.map((enumValue) => {
4208
- const item = enumValue;
4209
- if (!!item === false) {
4210
- return jsx(Fragment, {});
4211
- }
4212
- return (jsx(Tag, { size: "lg", closable: true, onClick: () => {
4213
- setValue(column, watchEnums.filter((id) => id != item));
4214
- }, children: !!renderDisplay === true
4215
- ? renderDisplay(item)
4216
- : formI18n.t(item) }, item));
4217
- }), jsx(Tag, { size: "lg", cursor: 'pointer', onClick: () => {
4218
- setOpenSearchResult(true);
4219
- }, children: enumPickerLabels?.addMore ?? formI18n.t('add_more') }, `${colLabel}-add-more-tag`)] })), !isMultiple && (jsx(Button, { variant: 'outline', onClick: () => {
4220
- setOpenSearchResult(true);
4221
- }, justifyContent: 'start', children: !!watchEnum === false ? '' : formI18n.t(watchEnum ?? 'null') })), jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: 'bottom-start' }, children: [jsx(PopoverTrigger, {}), jsx(PopoverContent, { portalled: false, children: jsxs(PopoverBody, { display: 'grid', gap: 1, children: [jsx(Input, { placeholder: enumPickerLabels?.typeToSearch ?? formI18n.t('type_to_search'), onChange: (event) => {
4222
- onSearchChange(event);
4223
- setOpenSearchResult(true);
4224
- }, autoComplete: "off", ref: ref }), jsx(PopoverTitle, {}), showTotalAndLimit && (jsx(Text, { children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${count}, ${enumPickerLabels?.showing ?? formI18n.t('showing')} ${limit}` })), jsxs(Grid, { overflow: 'auto', maxHeight: '20rem', children: [jsx(Flex, { flexFlow: 'column wrap', children: dataList
4225
- .filter((item) => {
4226
- const searchTerm = (searchText || '').toLowerCase();
4227
- if (!searchTerm)
4228
- return true;
4229
- // Check if the original enum value contains the search text
4230
- const enumValueMatch = item
4231
- .toLowerCase()
4232
- .includes(searchTerm);
4233
- // Check if the display value (translation) contains the search text
4234
- const displayValue = !!renderDisplay === true
4235
- ? renderDisplay(item)
4236
- : formI18n.t(item);
4237
- // Convert to string and check if it includes the search term
4238
- const displayValueString = String(displayValue).toLowerCase();
4239
- const displayValueMatch = displayValueString.includes(searchTerm);
4240
- return enumValueMatch || displayValueMatch;
4241
- })
4242
- .map((item) => {
4243
- const selected = isMultiple
4244
- ? watchEnums.some((enumValue) => item === enumValue)
4245
- : watchEnum == item;
4246
- return (jsx(Box, { cursor: 'pointer', onClick: () => {
4247
- if (!isMultiple) {
4248
- setOpenSearchResult(false);
4249
- setValue(colLabel, item);
4250
- return;
4251
- }
4252
- const newSet = new Set([...(watchEnums ?? []), item]);
4253
- setValue(colLabel, [...newSet]);
4254
- }, ...(selected ? { color: 'colorPalette.400/50' } : {}), children: !!renderDisplay === true
4255
- ? renderDisplay(item)
4256
- : formI18n.t(item) }, `${colLabel}-${item}`));
4257
- }) }), isDirty && (jsx(Fragment, { children: dataList.length <= 0 && (jsx(Fragment, { children: enumPickerLabels?.emptySearchResult ??
4258
- formI18n.t('empty_search_result') })) }))] })] }) })] })] }));
4220
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && currentValue.length > 0 && (jsx(Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((enumValue) => {
4221
+ if (!enumValue) {
4222
+ return null;
4223
+ }
4224
+ return (jsx(Tag, { size: "lg", closable: true, onClick: () => {
4225
+ const newValue = currentValue.filter((val) => val !== enumValue);
4226
+ setValue(colLabel, newValue);
4227
+ }, children: !!renderDisplay === true
4228
+ ? renderDisplay(enumValue)
4229
+ : formI18n.t(enumValue) }, enumValue));
4230
+ }) })), jsxs(Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", children: [jsxs(Combobox.Control, { children: [jsx(Combobox.Input, { placeholder: enumPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxs(Combobox.IndicatorGroup, { children: [!isMultiple && currentValue.length > 0 && (jsx(Combobox.ClearTrigger, { onClick: () => {
4231
+ setValue(colLabel, '');
4232
+ } })), jsx(Combobox.Trigger, {})] })] }), jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsx(Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
4233
+ formI18n.t('empty_search_result') })) : (jsx(Fragment, { children: collection.items.map((item) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value))) }))] }) }) })] })] }));
4259
4234
  };
4260
4235
 
4261
4236
  function isEnteringWindow(_ref) {
@@ -4624,13 +4599,13 @@ function formatBytes(bytes) {
4624
4599
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
4625
4600
  }
4626
4601
 
4627
- const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, enabled = true, multiple = false, onFileSelect, selectedFileId: controlledSelectedFileId, onSelectedFileIdChange, }) => {
4602
+ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, enabled = true, multiple = false, onFileSelect, selectedFile: controlledSelectedFile, onSelectedFileChange, }) => {
4628
4603
  const [searchTerm, setSearchTerm] = useState('');
4629
- const [internalSelectedFileId, setInternalSelectedFileId] = useState(multiple ? [] : '');
4604
+ const [internalSelectedFile, setInternalSelectedFile] = useState(multiple ? [] : undefined);
4630
4605
  const [failedImageIds, setFailedImageIds] = useState(new Set());
4631
- // Use controlled or internal state for selectedFileId
4632
- const selectedFileId = controlledSelectedFileId ?? internalSelectedFileId;
4633
- const setSelectedFileId = onSelectedFileIdChange ?? setInternalSelectedFileId;
4606
+ // Use controlled or internal state for selectedFile
4607
+ const selectedFile = controlledSelectedFile ?? internalSelectedFile;
4608
+ const setSelectedFile = onSelectedFileChange ?? setInternalSelectedFile;
4634
4609
  const { data: filesData, isLoading, isError, } = useQuery({
4635
4610
  queryKey: ['file-picker-library', searchTerm],
4636
4611
  queryFn: async () => {
@@ -4645,23 +4620,23 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
4645
4620
  const filteredFiles = filterImageOnly
4646
4621
  ? files.filter((file) => /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name))
4647
4622
  : files;
4648
- const handleFileClick = (fileId) => {
4623
+ const handleFileClick = (file) => {
4649
4624
  if (multiple) {
4650
- const currentSelection = Array.isArray(selectedFileId)
4651
- ? selectedFileId
4652
- : [];
4653
- const newSelection = currentSelection.includes(fileId)
4654
- ? currentSelection.filter((id) => id !== fileId)
4655
- : [...currentSelection, fileId];
4656
- setSelectedFileId(newSelection);
4625
+ const currentSelection = Array.isArray(selectedFile) ? selectedFile : [];
4626
+ const isAlreadySelected = currentSelection.some((f) => f.id === file.id);
4627
+ const newSelection = isAlreadySelected
4628
+ ? currentSelection.filter((f) => f.id !== file.id)
4629
+ : [...currentSelection, file];
4630
+ setSelectedFile(newSelection);
4657
4631
  if (onFileSelect) {
4658
4632
  onFileSelect(newSelection);
4659
4633
  }
4660
4634
  }
4661
4635
  else {
4662
- setSelectedFileId(fileId);
4663
- if (onFileSelect) {
4664
- onFileSelect(fileId);
4636
+ const newFile = selectedFile === file ? undefined : file;
4637
+ setSelectedFile(newFile);
4638
+ if (onFileSelect && newFile) {
4639
+ onFileSelect(newFile);
4665
4640
  }
4666
4641
  }
4667
4642
  };
@@ -4688,9 +4663,10 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
4688
4663
  }, children: labels?.loadingFailed ?? 'Failed to load files' }) })), !isLoading && !isError && (jsx(Box, { maxHeight: "400px", overflowY: "auto", children: filteredFiles.length === 0 ? (jsx(Box, { textAlign: "center", py: 8, children: jsx(Text, { color: "fg.muted", children: labels?.noFilesFound ?? 'No files found' }) })) : (jsx(VStack, { align: "stretch", gap: 2, children: filteredFiles.map((file) => {
4689
4664
  const isImage = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name);
4690
4665
  const isSelected = multiple
4691
- ? Array.isArray(selectedFileId) &&
4692
- selectedFileId.includes(file.id)
4693
- : selectedFileId === file.id;
4666
+ ? Array.isArray(selectedFile) &&
4667
+ selectedFile.some((f) => f.id === file.id)
4668
+ : selectedFile?.id ===
4669
+ file.id;
4694
4670
  const imageFailed = failedImageIds.has(file.id);
4695
4671
  return (jsx(Box, { p: 3, border: "2px solid", borderColor: isSelected
4696
4672
  ? {
@@ -4702,7 +4678,7 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
4702
4678
  base: 'colorPalette.50',
4703
4679
  _dark: 'colorPalette.900/20',
4704
4680
  }
4705
- : 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => handleFileClick(file.id), _hover: {
4681
+ : 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => handleFileClick(file), _hover: {
4706
4682
  borderColor: isSelected
4707
4683
  ? {
4708
4684
  base: 'colorPalette.600',
@@ -4727,22 +4703,22 @@ const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, en
4727
4703
  }) })) }))] }));
4728
4704
  };
4729
4705
 
4730
- function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, translate, colLabel, }) {
4731
- const [selectedFileId, setSelectedFileId] = useState('');
4706
+ function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, translate, colLabel, }) {
4707
+ const [selectedFile, setSelectedFile] = useState(undefined);
4732
4708
  const [activeTab, setActiveTab] = useState('browse');
4733
4709
  const [uploadingFiles, setUploadingFiles] = useState(new Set());
4734
4710
  const [uploadErrors, setUploadErrors] = useState(new Map());
4735
4711
  const handleSelect = () => {
4736
- if (selectedFileId) {
4737
- onSelect(selectedFileId);
4712
+ if (selectedFile) {
4713
+ onSelect(selectedFile);
4738
4714
  onClose();
4739
- setSelectedFileId('');
4715
+ setSelectedFile(undefined);
4740
4716
  setActiveTab('browse');
4741
4717
  }
4742
4718
  };
4743
4719
  const handleClose = () => {
4744
4720
  onClose();
4745
- setSelectedFileId('');
4721
+ setSelectedFile(undefined);
4746
4722
  setActiveTab('browse');
4747
4723
  setUploadingFiles(new Set());
4748
4724
  setUploadErrors(new Map());
@@ -4760,16 +4736,23 @@ function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = fa
4760
4736
  });
4761
4737
  try {
4762
4738
  const fileId = await onUploadFile(file);
4763
- setSelectedFileId(fileId);
4739
+ // Create a minimal FilePickerMediaFile object from the uploaded file
4740
+ const uploadedFile = {
4741
+ id: fileId,
4742
+ name: file.name,
4743
+ size: file.size,
4744
+ type: file.type,
4745
+ };
4746
+ setSelectedFile(uploadedFile);
4764
4747
  setUploadingFiles((prev) => {
4765
4748
  const newSet = new Set(prev);
4766
4749
  newSet.delete(fileKey);
4767
4750
  return newSet;
4768
4751
  });
4769
4752
  // Auto-select and close in single-select mode
4770
- onSelect(fileId);
4753
+ onSelect(uploadedFile);
4771
4754
  onClose();
4772
- setSelectedFileId('');
4755
+ setSelectedFile(undefined);
4773
4756
  setActiveTab('browse');
4774
4757
  }
4775
4758
  catch (error) {
@@ -4793,7 +4776,7 @@ function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = fa
4793
4776
  translate(removeIndex(`${colLabel}.browse_tab`)) ??
4794
4777
  'Browse Library' }), jsx(Tabs.Trigger, { value: "upload", children: labels?.uploadTab ??
4795
4778
  translate(removeIndex(`${colLabel}.upload_tab`)) ??
4796
- 'Upload Files' })] }), jsx(Tabs.Content, { value: "browse", children: onFetchFiles && (jsx(MediaLibraryBrowser, { onFetchFiles: onFetchFiles, filterImageOnly: filterImageOnly, labels: labels, enabled: open && activeTab === 'browse', selectedFileId: selectedFileId, onSelectedFileIdChange: setSelectedFileId })) }), jsx(Tabs.Content, { value: "upload", children: jsxs(VStack, { align: "stretch", gap: 4, children: [jsx(FileDropzone, { onDrop: ({ files }) => handleFileUpload(files), placeholder: labels?.fileDropzone ??
4779
+ 'Upload Files' })] }), jsx(Tabs.Content, { value: "browse", children: onFetchFiles && (jsx(MediaLibraryBrowser, { onFetchFiles: onFetchFiles, filterImageOnly: filterImageOnly, labels: labels, enabled: open && activeTab === 'browse', selectedFile: selectedFile, onFileSelect: setSelectedFile })) }), jsx(Tabs.Content, { value: "upload", children: jsxs(VStack, { align: "stretch", gap: 4, children: [jsx(FileDropzone, { onDrop: ({ files }) => handleFileUpload(files), placeholder: labels?.fileDropzone ??
4797
4780
  translate(removeIndex(`${colLabel}.fileDropzone`)) ??
4798
4781
  'Drop files here or click to upload' }), uploadingFiles.size > 0 && (jsx(Box, { children: Array.from(uploadingFiles).map((fileKey) => (jsx(Box, { py: 2, children: jsxs(HStack, { gap: 2, children: [jsx(Spinner, { size: "sm", colorPalette: "blue" }), jsxs(Text, { fontSize: "sm", color: "fg.muted", children: [labels?.uploading ??
4799
4782
  translate(removeIndex(`${colLabel}.uploading`)) ??
@@ -4808,9 +4791,9 @@ function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = fa
4808
4791
  _dark: 'colorPalette.300',
4809
4792
  }, children: [fileKey.split('-')[0], ":", ' ', labels?.uploadFailed ??
4810
4793
  translate(removeIndex(`${colLabel}.upload_failed`)) ??
4811
- 'Upload failed', error && ` - ${error}`] }) }, fileKey))) }))] }) })] })) : onFetchFiles ? (jsx(MediaLibraryBrowser, { onFetchFiles: onFetchFiles, filterImageOnly: filterImageOnly, labels: labels, enabled: open, selectedFileId: selectedFileId, onSelectedFileIdChange: setSelectedFileId })) : null }), jsx(DialogFooter, { children: jsxs(HStack, { gap: 3, justify: "end", children: [jsx(Button$1, { variant: "outline", onClick: handleClose, borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: labels?.cancel ??
4794
+ 'Upload failed', error && ` - ${error}`] }) }, fileKey))) }))] }) })] })) : onFetchFiles ? (jsx(MediaLibraryBrowser, { onFetchFiles: onFetchFiles, filterImageOnly: filterImageOnly, labels: labels, enabled: open, selectedFile: selectedFile, onFileSelect: setSelectedFile })) : null }), jsx(DialogFooter, { children: jsxs(HStack, { gap: 3, justify: "end", children: [jsx(Button$1, { variant: "outline", onClick: handleClose, borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: labels?.cancel ??
4812
4795
  translate(removeIndex(`${colLabel}.cancel`)) ??
4813
- 'Cancel' }), jsx(Button$1, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFileId, children: labels?.select ??
4796
+ 'Cancel' }), jsx(Button$1, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFile, children: labels?.select ??
4814
4797
  translate(removeIndex(`${colLabel}.select`)) ??
4815
4798
  'Select' })] }) })] }) }));
4816
4799
  }
@@ -4909,17 +4892,70 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
4909
4892
  const colLabel = formI18n.colLabel;
4910
4893
  const [dialogOpen, setDialogOpen] = useState(false);
4911
4894
  const [failedImageIds, setFailedImageIds] = useState(new Set());
4895
+ // Map of file ID to FilePickerMediaFile for display
4896
+ const [fileMap, setFileMap] = useState(new Map());
4912
4897
  const { onFetchFiles, filterImageOnly = false, enableUpload = false, onUploadFile, } = filePicker || {};
4898
+ // Fetch file details for existing file IDs
4899
+ useEffect(() => {
4900
+ if (!onFetchFiles || currentFileIds.length === 0)
4901
+ return;
4902
+ const fetchFileDetails = async () => {
4903
+ setFileMap((prevMap) => {
4904
+ const filesToFetch = currentFileIds.filter((id) => !prevMap.has(id));
4905
+ if (filesToFetch.length === 0)
4906
+ return prevMap;
4907
+ // Fetch all files and filter for the ones we need
4908
+ onFetchFiles('')
4909
+ .then((allFiles) => {
4910
+ setFileMap((currentMap) => {
4911
+ const newFileMap = new Map(currentMap);
4912
+ filesToFetch.forEach((id) => {
4913
+ const file = allFiles.find((f) => f.id === id);
4914
+ if (file) {
4915
+ newFileMap.set(id, file);
4916
+ }
4917
+ });
4918
+ return newFileMap;
4919
+ });
4920
+ })
4921
+ .catch((error) => {
4922
+ console.error('Failed to fetch file details:', error);
4923
+ });
4924
+ return prevMap;
4925
+ });
4926
+ };
4927
+ fetchFileDetails();
4928
+ // eslint-disable-next-line react-hooks/exhaustive-deps
4929
+ }, [currentFileIds.join(','), onFetchFiles]);
4930
+ // Clean up fileMap when files are removed
4931
+ useEffect(() => {
4932
+ setFileMap((prevMap) => {
4933
+ const currentIds = new Set(currentFileIds);
4934
+ const newFileMap = new Map();
4935
+ prevMap.forEach((file, id) => {
4936
+ if (currentIds.has(id)) {
4937
+ newFileMap.set(id, file);
4938
+ }
4939
+ });
4940
+ return newFileMap.size !== prevMap.size ? newFileMap : prevMap;
4941
+ });
4942
+ // eslint-disable-next-line react-hooks/exhaustive-deps
4943
+ }, [currentFileIds.join(',')]);
4913
4944
  if (!onFetchFiles) {
4914
4945
  return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4915
4946
  gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: jsx(Text, { color: "fg.muted", children: "Media library browser requires onFetchFiles" }) }));
4916
4947
  }
4917
- const handleMediaLibrarySelect = (fileId) => {
4948
+ const handleImageError = (fileIdentifier) => {
4949
+ setFailedImageIds((prev) => new Set(prev).add(fileIdentifier));
4950
+ };
4951
+ const handleMediaLibrarySelect = (file) => {
4952
+ // Store the file in the map for display
4953
+ setFileMap((prev) => new Map(prev).set(file.id, file));
4918
4954
  if (isSingleSelect) {
4919
- setValue(colLabel, fileId);
4955
+ setValue(colLabel, file.id);
4920
4956
  }
4921
4957
  else {
4922
- const newFileIds = [...currentFileIds, fileId];
4958
+ const newFileIds = [...currentFileIds, file.id];
4923
4959
  setValue(colLabel, newFileIds);
4924
4960
  }
4925
4961
  };
@@ -4932,33 +4968,27 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
4932
4968
  setValue(colLabel, newFileIds);
4933
4969
  }
4934
4970
  };
4935
- const isImageId = (fileId) => {
4936
- return /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(fileId);
4937
- };
4938
4971
  return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4939
4972
  gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [jsx(VStack, { align: "stretch", gap: 2, children: jsx(Button$1, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ??
4940
4973
  formI18n.t('browse_library') ??
4941
- 'Browse from Library' }) }), jsx(FilePickerDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
4974
+ 'Browse from Library' }) }), jsx(MediaBrowserDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
4942
4975
  formI18n.t('dialog_title') ??
4943
4976
  'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, onUploadFile: onUploadFile, enableUpload: enableUpload, labels: filePickerLabels, translate: formI18n.t, colLabel: colLabel }), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
4944
- const isImage = isImageId(fileId);
4977
+ const file = fileMap.get(fileId);
4978
+ const isImage = file
4979
+ ? /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name)
4980
+ : /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(fileId);
4945
4981
  const imageFailed = failedImageIds.has(fileId);
4982
+ const displayName = file?.name ?? fileId;
4946
4983
  return (jsx(Card.Root, { variant: 'subtle', colorPalette: "blue", children: jsxs(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: {
4947
4984
  borderColor: 'colorPalette.300',
4948
4985
  bg: 'bg.muted',
4949
- }, transition: "all 0.2s", children: [jsx(Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, marginRight: "2", children: isImage && !imageFailed ? (jsx(Icon, { as: LuImage, boxSize: 6, color: "fg.muted" })) : (jsx(Icon, { as: LuFile, boxSize: 6, color: "fg.muted" })) }), jsx(VStack, { align: "start", flex: 1, gap: 1, children: jsx(Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: fileId }) }), jsx(Icon, { as: TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, `${fileId}-${index}`));
4986
+ }, transition: "all 0.2s", children: [jsx(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 ? (jsx(Image, { src: file.url, alt: displayName, boxSize: "60px", objectFit: "cover", onError: () => handleImageError(fileId) })) : isImage && !imageFailed ? (jsx(Icon, { as: LuImage, boxSize: 6, color: "fg.muted" })) : (jsx(Icon, { as: LuFile, boxSize: 6, color: "fg.muted" })) }), jsxs(VStack, { align: "start", flex: 1, gap: 1, children: [jsx(Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: displayName }), file?.size && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
4987
+ ? `${(file.size / 1024).toFixed(1)} KB`
4988
+ : file.size }))] }), jsx(Icon, { as: TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, `${fileId}-${index}`));
4950
4989
  }) })] }));
4951
4990
  };
4952
4991
 
4953
- const ToggleTip = React.forwardRef(function ToggleTip(props, ref) {
4954
- const { showArrow, children, portalled = true, content, portalRef, ...rest } = props;
4955
- return (jsxs(Popover.Root, { ...rest, positioning: { ...rest.positioning, gutter: 4 }, children: [jsx(Popover.Trigger, { asChild: true, children: children }), jsx(Portal, { disabled: !portalled, container: portalRef, children: jsx(Popover.Positioner, { children: jsxs(Popover.Content, { width: "auto", px: "2", py: "1", textStyle: "xs", rounded: "sm", ref: ref, children: [showArrow && (jsx(Popover.Arrow, { children: jsx(Popover.ArrowTip, {}) })), content] }) }) })] }));
4956
- });
4957
- const InfoTip = React.forwardRef(function InfoTip(props, ref) {
4958
- const { children, ...rest } = props;
4959
- return (jsx(ToggleTip, { content: children, ...rest, ref: ref, children: jsx(IconButton, { variant: "ghost", "aria-label": "info", size: "2xs", colorPalette: "colorPalette", children: jsx(HiOutlineInformationCircle, {}) }) }));
4960
- });
4961
-
4962
4992
  const getTableData = async ({ serverUrl, in_table, searching = "", where = [], limit = 10, offset = 0, }) => {
4963
4993
  if (serverUrl === undefined || serverUrl.length == 0) {
4964
4994
  throw new Error("The serverUrl is missing");
@@ -4995,10 +5025,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4995
5025
  const isRequired = required?.some((columnId) => columnId === column);
4996
5026
  const { table, column: column_ref, display_column, customQueryFn, } = foreign_key;
4997
5027
  const [searchText, setSearchText] = useState('');
4998
- const [limit, setLimit] = useState(10);
4999
- const [openSearchResult, setOpenSearchResult] = useState();
5000
- const [page, setPage] = useState(0);
5001
- const ref = useRef(null);
5028
+ const [limit] = useState(50); // Increased limit for combobox
5002
5029
  const colLabel = formI18n.colLabel;
5003
5030
  const watchedValue = watch(colLabel);
5004
5031
  const watchId = !isMultiple ? watchedValue : undefined;
@@ -5013,22 +5040,25 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
5013
5040
  : [];
5014
5041
  // Use watched values if they exist (including empty string for single select),
5015
5042
  // otherwise fall back to initial values from getValues()
5016
- // This ensures the query can trigger on mount with initial values
5017
- // For single: use watchId if it's not undefined/null, otherwise use initialId
5018
- // For multiple: use watchIds if watchedValue is defined, otherwise use initialIds
5019
5043
  const currentId = watchId !== undefined && watchId !== null ? watchId : initialId;
5020
5044
  const currentIds = watchedValue !== undefined && watchedValue !== null && isMultiple
5021
5045
  ? watchIds
5022
5046
  : initialIds;
5023
- // Query for search results
5047
+ // Current value for combobox (array format)
5048
+ const currentValue = isMultiple
5049
+ ? currentIds.filter((id) => id != null && id !== '')
5050
+ : currentId
5051
+ ? [currentId]
5052
+ : [];
5053
+ // Query for search results (async loading)
5024
5054
  const query = useQuery({
5025
- queryKey: [`idpicker`, { column, searchText, limit, page }],
5055
+ queryKey: [`idpicker`, { column, searchText, limit }],
5026
5056
  queryFn: async () => {
5027
5057
  if (customQueryFn) {
5028
5058
  const { data, idMap } = await customQueryFn({
5029
5059
  searching: searchText ?? '',
5030
5060
  limit: limit,
5031
- offset: page * limit,
5061
+ offset: 0,
5032
5062
  });
5033
5063
  setIdMap((state) => {
5034
5064
  return { ...state, ...idMap };
@@ -5040,7 +5070,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
5040
5070
  searching: searchText ?? '',
5041
5071
  in_table: table,
5042
5072
  limit: limit,
5043
- offset: page * limit,
5073
+ offset: 0,
5044
5074
  });
5045
5075
  const newMap = Object.fromEntries((data ?? { data: [] }).data.map((item) => {
5046
5076
  return [
@@ -5055,12 +5085,11 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
5055
5085
  });
5056
5086
  return data;
5057
5087
  },
5058
- enabled: openSearchResult === true,
5088
+ enabled: true, // Always enabled for combobox
5059
5089
  staleTime: 300000,
5060
5090
  });
5061
5091
  // Query for currently selected items (to display them properly)
5062
- // Use currentId/currentIds in queryKey so it includes initial values and updates when watched values change
5063
- const queryDefault = useQuery({
5092
+ useQuery({
5064
5093
  queryKey: [
5065
5094
  `idpicker-default`,
5066
5095
  {
@@ -5070,11 +5099,9 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
5070
5099
  },
5071
5100
  ],
5072
5101
  queryFn: async () => {
5073
- // Use current values (which include initial) for the query
5074
5102
  const queryId = currentId;
5075
5103
  const queryIds = currentIds;
5076
5104
  if (customQueryFn) {
5077
- // For customQueryFn, pass where clause to fetch specific IDs
5078
5105
  const { data, idMap } = await customQueryFn({
5079
5106
  searching: '',
5080
5107
  limit: isMultiple ? queryIds.length : 1,
@@ -5089,10 +5116,9 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
5089
5116
  if (!queryId && (!queryIds || queryIds.length === 0)) {
5090
5117
  return { data: [] };
5091
5118
  }
5092
- const searchValue = isMultiple ? queryIds.join(',') : queryId;
5093
5119
  const data = await getTableData({
5094
5120
  serverUrl,
5095
- searching: searchValue,
5121
+ searching: '',
5096
5122
  in_table: table,
5097
5123
  where: [{ id: column_ref, value: isMultiple ? queryIds : queryId }],
5098
5124
  limit: isMultiple ? queryIds.length : 1,
@@ -5115,101 +5141,80 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
5115
5141
  ? Array.isArray(currentIds) && currentIds.length > 0
5116
5142
  : !!currentId,
5117
5143
  });
5118
- // Effect to trigger initial data fetch when popover opens
5119
- useEffect(() => {
5120
- if (openSearchResult) {
5121
- // Reset search text when opening the popover
5122
- setSearchText('');
5123
- // Reset page to first page
5124
- setPage(0);
5125
- // Fetch initial data
5126
- query.refetch();
5127
- }
5128
- // eslint-disable-next-line react-hooks/exhaustive-deps
5129
- }, [openSearchResult]);
5130
- const onSearchChange = async (event) => {
5131
- setSearchText(event.target.value);
5132
- setPage(0);
5133
- query.refetch();
5134
- };
5135
- const handleLimitChange = (event) => {
5136
- const newLimit = Number(event.target.value);
5137
- setLimit(newLimit);
5138
- // Reset to first page when changing limit
5139
- setPage(0);
5140
- // Trigger a new search with the updated limit
5141
- query.refetch();
5142
- };
5143
5144
  const { isLoading, isFetching, data, isPending, isError } = query;
5144
5145
  const dataList = data?.data ?? [];
5145
- const count = data?.count ?? 0;
5146
- const getPickedValue = () => {
5147
- if (Object.keys(idMap).length <= 0) {
5148
- return '';
5149
- }
5150
- // Use currentId which includes initial values
5151
- const record = idMap[currentId];
5152
- if (record === undefined) {
5153
- return '';
5146
+ // Transform data for combobox collection
5147
+ const comboboxItems = useMemo(() => {
5148
+ return dataList.map((item) => ({
5149
+ label: !!renderDisplay === true
5150
+ ? String(renderDisplay(item))
5151
+ : String(item[display_column] ?? ''),
5152
+ value: String(item[column_ref]),
5153
+ raw: item,
5154
+ }));
5155
+ }, [dataList, display_column, column_ref, renderDisplay]);
5156
+ // Use filter hook for combobox
5157
+ const { contains } = useFilter({ sensitivity: 'base' });
5158
+ // Create collection for combobox
5159
+ const { collection, filter, set } = useListCollection({
5160
+ initialItems: comboboxItems,
5161
+ itemToString: (item) => item.label,
5162
+ itemToValue: (item) => item.value,
5163
+ filter: contains,
5164
+ });
5165
+ // Handle input value change (search)
5166
+ const handleInputValueChange = (details) => {
5167
+ setSearchText(details.inputValue);
5168
+ // Filter will be applied after data is fetched
5169
+ };
5170
+ // Handle value change
5171
+ const handleValueChange = (details) => {
5172
+ if (isMultiple) {
5173
+ setValue(colLabel, details.value);
5154
5174
  }
5155
- if (!!renderDisplay === true) {
5156
- return renderDisplay(record);
5175
+ else {
5176
+ setValue(colLabel, details.value[0] || '');
5157
5177
  }
5158
- return record[display_column];
5159
5178
  };
5179
+ // Debounce search to avoid too many API calls and update collection after data loads
5180
+ useEffect(() => {
5181
+ const timer = setTimeout(() => {
5182
+ if (searchText !== undefined) {
5183
+ query.refetch();
5184
+ }
5185
+ }, 300);
5186
+ return () => clearTimeout(timer);
5187
+ }, [searchText, query]);
5188
+ // Update collection and filter when data changes
5189
+ useEffect(() => {
5190
+ if (dataList.length > 0) {
5191
+ set(comboboxItems);
5192
+ // Apply filter to the collection
5193
+ if (searchText) {
5194
+ filter(searchText);
5195
+ }
5196
+ }
5197
+ }, [dataList, comboboxItems, set, filter, searchText]);
5160
5198
  return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5161
- gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxs(Flex, { flexFlow: 'wrap', gap: 1, children: [watchIds.map((id) => {
5162
- const item = idMap[id];
5163
- if (item === undefined) {
5164
- return (jsx(Text, { children: idPickerLabels?.undefined ?? formI18n.t('undefined') }, id));
5165
- }
5166
- return (jsx(Tag, { closable: true, onClick: () => {
5167
- setValue(colLabel, watchIds.filter((itemId) => itemId !== item[column_ref]));
5168
- }, children: !!renderDisplay === true
5169
- ? renderDisplay(item)
5170
- : item[display_column] }, id));
5171
- }), jsx(Tag, { cursor: 'pointer', onClick: () => {
5172
- setOpenSearchResult(true);
5173
- }, children: idPickerLabels?.addMore ?? formI18n.t('add_more') })] })), !isMultiple && (jsx(Button, { variant: 'outline', onClick: () => {
5174
- setOpenSearchResult(true);
5175
- }, justifyContent: 'start', children: queryDefault.isLoading ? jsx(Spinner, { size: "sm" }) : getPickedValue() })), jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: 'bottom-start', strategy: 'fixed' }, children: [jsx(PopoverTrigger, {}), jsx(PopoverContent, { portalled: false, children: jsxs(PopoverBody, { display: 'grid', gap: 1, children: [jsx(Input, { placeholder: idPickerLabels?.typeToSearch ?? formI18n.t('type_to_search'), onChange: onSearchChange, autoComplete: "off", ref: ref, value: searchText }), jsx(PopoverTitle, {}), openSearchResult && (jsxs(Fragment, { children: [(isFetching || isLoading || isPending) && jsx(Spinner, {}), isError && (jsx(Icon, { color: 'red.400', children: jsx(BiError, {}) })), jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxs(Flex, { alignItems: "center", gap: "2", children: [jsx(InfoTip, { children: `${idPickerLabels?.total ?? formI18n.t('total')} ${count}, ${idPickerLabels?.showing ?? formI18n.t('showing')} ${limit} ${idPickerLabels?.perPage ?? formI18n.t('per_page', { defaultValue: 'per page' })}` }), jsxs(Text, { fontSize: "sm", fontWeight: "bold", children: [count, jsxs(Text, { as: "span", fontSize: "xs", ml: "1", color: "gray.500", children: ["/", ' ', count > 0
5176
- ? `${page * limit + 1}-${Math.min((page + 1) * limit, count)}`
5177
- : '0'] })] })] }), jsx(Box, { children: jsxs("select", { value: limit, onChange: handleLimitChange, style: {
5178
- padding: '4px 8px',
5179
- borderRadius: '4px',
5180
- border: '1px solid #ccc',
5181
- fontSize: '14px',
5182
- }, children: [jsx("option", { value: "5", children: "5" }), jsx("option", { value: "10", children: "10" }), jsx("option", { value: "20", children: "20" }), jsx("option", { value: "30", children: "30" })] }) })] }), jsx(Grid, { overflowY: 'auto', children: dataList.length > 0 ? (jsx(Flex, { flexFlow: 'column wrap', gap: 1, children: dataList.map((item) => {
5183
- const selected = isMultiple
5184
- ? watchIds.some((id) => item[column_ref] === id)
5185
- : watchId === item[column_ref];
5186
- return (jsx(Box, { cursor: 'pointer', onClick: () => {
5187
- if (!isMultiple) {
5188
- setOpenSearchResult(false);
5189
- setValue(colLabel, item[column_ref]);
5190
- return;
5191
- }
5192
- // For multiple selection, don't add if already selected
5193
- if (selected)
5194
- return;
5195
- const newSet = new Set([
5196
- ...(watchIds ?? []),
5197
- item[column_ref],
5198
- ]);
5199
- setValue(colLabel, [...newSet]);
5200
- }, opacity: 0.7, _hover: { opacity: 1 }, ...(selected
5201
- ? {
5202
- color: 'colorPalette.400/50',
5203
- fontWeight: 'bold',
5204
- }
5205
- : {}), children: !!renderDisplay === true
5206
- ? renderDisplay(item)
5207
- : item[display_column] }, item[column_ref]));
5208
- }) })) : (jsx(Text, { children: searchText
5209
- ? idPickerLabels?.emptySearchResult ??
5210
- formI18n.t('empty_search_result')
5211
- : idPickerLabels?.initialResults ??
5212
- formI18n.t('initial_results') })) }), jsx(PaginationRoot, { justifySelf: 'center', count: count, pageSize: limit, defaultPage: 1, page: page + 1, onPageChange: (e) => setPage(e.page - 1), children: jsxs(HStack, { gap: "4", children: [jsx(PaginationPrevTrigger, {}), count > 0 && jsx(PaginationPageText, {}), jsx(PaginationNextTrigger, {})] }) })] }))] }) })] })] }));
5199
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && currentValue.length > 0 && (jsx(Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((id) => {
5200
+ const item = idMap[id];
5201
+ if (item === undefined) {
5202
+ return (jsx(Text, { fontSize: "sm", children: idPickerLabels?.undefined ?? formI18n.t('undefined') }, id));
5203
+ }
5204
+ return (jsx(Tag, { closable: true, onClick: () => {
5205
+ const newValue = currentValue.filter((itemId) => itemId !== id);
5206
+ setValue(colLabel, newValue);
5207
+ }, children: !!renderDisplay === true
5208
+ ? renderDisplay(item)
5209
+ : item[display_column] }, id));
5210
+ }) })), jsxs(Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", children: [jsxs(Combobox.Control, { children: [jsx(Combobox.Input, { placeholder: idPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxs(Combobox.IndicatorGroup, { children: [(isFetching || isLoading || isPending) && (jsx(Spinner, { size: "xs" })), isError && (jsx(Icon, { color: "fg.error", children: jsx(BiError, {}) })), !isMultiple && currentValue.length > 0 && (jsx(Combobox.ClearTrigger, { onClick: () => {
5211
+ setValue(colLabel, '');
5212
+ } })), jsx(Combobox.Trigger, {})] })] }), jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isFetching || isLoading || isPending ? (jsxs(HStack, { p: 2, justify: "center", children: [jsx(Spinner, { size: "xs" }), jsx(Text, { fontSize: "sm", children: idPickerLabels?.loading ?? formI18n.t('loading') })] })) : isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: idPickerLabels?.loadingFailed ??
5213
+ formI18n.t('loading_failed') })) : collection.items.length === 0 ? (jsx(Combobox.Empty, { children: searchText
5214
+ ? idPickerLabels?.emptySearchResult ??
5215
+ formI18n.t('empty_search_result')
5216
+ : idPickerLabels?.initialResults ??
5217
+ formI18n.t('initial_results') })) : (jsx(Fragment, { children: collection.items.map((item) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value))) })) }) }) })] })] }));
5213
5218
  };
5214
5219
 
5215
5220
  const NumberInputRoot = React.forwardRef(function NumberInput$1(props, ref) {