@bsol-oss/react-datatable5 12.0.0-beta.76 → 12.0.0-beta.77

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
@@ -4869,7 +4869,7 @@ function getText(_ref2) {
4869
4869
  return source.getStringData(textMediaType);
4870
4870
  }
4871
4871
 
4872
- const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }, placeholder = "Drop files here or click to upload", }) => {
4872
+ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }, placeholder = 'Drop files here or click to upload', }) => {
4873
4873
  const ref = React.useRef(null);
4874
4874
  const [isDraggedOver, setIsDraggedOver] = React.useState(false);
4875
4875
  React.useEffect(() => {
@@ -4883,7 +4883,7 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4883
4883
  onDrop: ({ source }) => {
4884
4884
  const files = getFiles({ source });
4885
4885
  const text = getText({ source });
4886
- console.log(files, text, "dfposa");
4886
+ console.log(files, text, 'dfposa');
4887
4887
  onDrop({ files, text });
4888
4888
  },
4889
4889
  });
@@ -4892,9 +4892,9 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4892
4892
  function getColor(isDraggedOver) {
4893
4893
  if (isDraggedOver) {
4894
4894
  return {
4895
- backgroundColor: "blue.400",
4895
+ backgroundColor: 'blue.400',
4896
4896
  _dark: {
4897
- backgroundColor: "blue.400",
4897
+ backgroundColor: 'blue.400',
4898
4898
  },
4899
4899
  };
4900
4900
  }
@@ -4915,7 +4915,7 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4915
4915
  const filesArray = [...event.target.files];
4916
4916
  onDrop({ files: filesArray });
4917
4917
  };
4918
- return (jsxRuntime.jsxs(react.Grid, { ...getColor(isDraggedOver), ref: ref, cursor: "pointer", onClick: handleClick, borderStyle: "dashed", borderColor: "colorPalette.400", alignContent: "center", justifyContent: "center", borderWidth: 1, borderRadius: 4, ...gridProps, children: [children, !!children === false && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(react.Flex, { children: placeholder }), jsxRuntime.jsx(react.Input, { type: "file", multiple: true, style: { display: "none" }, ref: fileInput, onChange: handleChange })] }))] }));
4918
+ return (jsxRuntime.jsxs(react.Grid, { ...getColor(isDraggedOver), ref: ref, cursor: 'pointer', onClick: handleClick, borderStyle: 'dashed', borderColor: 'colorPalette.400', alignContent: 'center', justifyContent: 'center', borderWidth: 1, borderRadius: 4, minH: "120px", ...gridProps, children: [children, !!children === false && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(react.Flex, { children: placeholder }), jsxRuntime.jsx(react.Input, { type: "file", multiple: true, style: { display: 'none' }, ref: fileInput, onChange: handleChange })] }))] }));
4919
4919
  };
4920
4920
 
4921
4921
  /**
@@ -4935,6 +4935,7 @@ function formatBytes(bytes) {
4935
4935
  function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, labels, translate, colLabel, }) {
4936
4936
  const [searchTerm, setSearchTerm] = React.useState('');
4937
4937
  const [selectedFileId, setSelectedFileId] = React.useState('');
4938
+ const [failedImageIds, setFailedImageIds] = React.useState(new Set());
4938
4939
  const { data: filesData, isLoading, isError, } = reactQuery.useQuery({
4939
4940
  queryKey: ['file-picker-library', searchTerm],
4940
4941
  queryFn: async () => {
@@ -4961,31 +4962,72 @@ function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = fa
4961
4962
  onClose();
4962
4963
  setSelectedFileId('');
4963
4964
  setSearchTerm('');
4965
+ setFailedImageIds(new Set());
4966
+ };
4967
+ const handleImageError = (fileId) => {
4968
+ setFailedImageIds((prev) => new Set(prev).add(fileId));
4964
4969
  };
4965
4970
  if (!onFetchFiles)
4966
4971
  return null;
4967
4972
  return (jsxRuntime.jsx(DialogRoot, { open: open, onOpenChange: (e) => !e.open && handleClose(), children: jsxRuntime.jsxs(DialogContent, { maxWidth: "800px", maxHeight: "90vh", children: [jsxRuntime.jsxs(DialogHeader, { children: [jsxRuntime.jsx(DialogTitle, { fontSize: "lg", fontWeight: "bold", children: title }), jsxRuntime.jsx(DialogCloseTrigger, {})] }), jsxRuntime.jsx(DialogBody, { children: jsxRuntime.jsxs(react.VStack, { align: "stretch", gap: 4, children: [jsxRuntime.jsxs(react.Box, { position: "relative", children: [jsxRuntime.jsx(react.Input, { placeholder: labels?.searchPlaceholder ??
4968
4973
  translate(removeIndex(`${colLabel}.search_placeholder`)) ??
4969
- 'Search files...', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), bg: "bg.panel", border: "1px solid", borderColor: "border.default", _focus: {
4970
- borderColor: 'blue.500',
4971
- boxShadow: '0 0 0 1px var(--chakra-colors-blue-500)',
4974
+ 'Search files...', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), bg: "bg.panel", border: "1px solid", borderColor: "border.default", colorPalette: "blue", _focus: {
4975
+ borderColor: 'colorPalette.500',
4976
+ _dark: {
4977
+ borderColor: 'colorPalette.400',
4978
+ },
4979
+ boxShadow: {
4980
+ base: '0 0 0 1px var(--chakra-colors-blue-500)',
4981
+ _dark: '0 0 0 1px var(--chakra-colors-blue-400)',
4982
+ },
4972
4983
  }, pl: 10 }), jsxRuntime.jsx(react.Icon, { as: lu.LuSearch, position: "absolute", left: 3, top: "50%", transform: "translateY(-50%)", color: "fg.muted", boxSize: 4 })] }), isLoading && (jsxRuntime.jsxs(react.Box, { textAlign: "center", py: 8, children: [jsxRuntime.jsx(react.Spinner, { size: "lg", colorPalette: "blue" }), jsxRuntime.jsx(react.Text, { mt: 4, color: "fg.muted", children: labels?.loading ??
4973
4984
  translate(removeIndex(`${colLabel}.loading`)) ??
4974
- 'Loading files...' })] })), isError && (jsxRuntime.jsx(react.Box, { bg: "red.50", _dark: { bg: 'red.900/20' }, border: "1px solid", borderColor: "red.200", borderRadius: "md", p: 4, children: jsxRuntime.jsx(react.Text, { color: "red.600", _dark: { color: 'red.300' }, children: labels?.loadingFailed ??
4985
+ 'Loading files...' })] })), isError && (jsxRuntime.jsx(react.Box, { bg: { base: 'colorPalette.50', _dark: 'colorPalette.900/20' }, border: "1px solid", borderColor: {
4986
+ base: 'colorPalette.200',
4987
+ _dark: 'colorPalette.800',
4988
+ }, colorPalette: "red", borderRadius: "md", p: 4, children: jsxRuntime.jsx(react.Text, { color: {
4989
+ base: 'colorPalette.600',
4990
+ _dark: 'colorPalette.300',
4991
+ }, children: labels?.loadingFailed ??
4975
4992
  translate(removeIndex(`${colLabel}.error.loading_failed`)) ??
4976
4993
  '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 ??
4977
4994
  translate(removeIndex(`${colLabel}.no_files_found`)) ??
4978
4995
  'No files found' }) })) : (jsxRuntime.jsx(react.VStack, { align: "stretch", gap: 2, children: filteredFiles.map((file) => {
4979
4996
  const isImage = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name);
4980
4997
  const isSelected = selectedFileId === file.id;
4981
- return (jsxRuntime.jsx(react.Box, { p: 3, border: "2px solid", borderColor: isSelected ? 'blue.500' : 'border.default', borderRadius: "md", bg: isSelected ? 'blue.50' : 'bg.panel', _dark: {
4982
- bg: isSelected ? 'blue.900/20' : 'bg.panel',
4983
- }, cursor: "pointer", onClick: () => setSelectedFileId(file.id), _hover: {
4984
- borderColor: isSelected ? 'blue.600' : 'blue.300',
4985
- bg: isSelected ? 'blue.100' : 'bg.muted',
4986
- }, transition: "all 0.2s", children: jsxRuntime.jsxs(react.HStack, { gap: 3, children: [jsxRuntime.jsx(react.Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, children: isImage && file.url ? (jsxRuntime.jsx(react.Image, { src: file.url, alt: file.name, boxSize: "60px", objectFit: "cover", borderRadius: "md" })) : (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: file.name }), jsxRuntime.jsxs(react.HStack, { gap: 2, children: [file.size && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
4998
+ const imageFailed = failedImageIds.has(file.id);
4999
+ return (jsxRuntime.jsx(react.Box, { p: 3, border: "2px solid", borderColor: isSelected
5000
+ ? {
5001
+ base: 'colorPalette.500',
5002
+ _dark: 'colorPalette.400',
5003
+ }
5004
+ : 'border.default', borderRadius: "md", bg: isSelected
5005
+ ? {
5006
+ base: 'colorPalette.50',
5007
+ _dark: 'colorPalette.900/20',
5008
+ }
5009
+ : 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => setSelectedFileId(file.id), _hover: {
5010
+ borderColor: isSelected
5011
+ ? {
5012
+ base: 'colorPalette.600',
5013
+ _dark: 'colorPalette.400',
5014
+ }
5015
+ : {
5016
+ base: 'colorPalette.300',
5017
+ _dark: 'colorPalette.400',
5018
+ },
5019
+ bg: isSelected
5020
+ ? {
5021
+ base: 'colorPalette.100',
5022
+ _dark: 'colorPalette.800/30',
5023
+ }
5024
+ : 'bg.muted',
5025
+ }, transition: "all 0.2s", children: jsxRuntime.jsxs(react.HStack, { gap: 3, children: [jsxRuntime.jsx(react.Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, children: isImage && file.url && !imageFailed ? (jsxRuntime.jsx(react.Image, { src: file.url, alt: file.name, boxSize: "60px", objectFit: "cover", borderRadius: "md", onError: () => handleImageError(file.id) })) : isImage && (imageFailed || !file.url) ? (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: file.name }), jsxRuntime.jsxs(react.HStack, { gap: 2, children: [file.size && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
4987
5026
  ? formatBytes(file.size)
4988
- : file.size }) })), file.comment && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [file.size && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: "\u2022" })), jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.comment })] }))] })] }), isSelected && (jsxRuntime.jsx(react.Box, { width: "24px", height: "24px", borderRadius: "full", bg: "blue.500", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsxRuntime.jsx(react.Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
5027
+ : file.size }) })), file.comment && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [file.size && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: "\u2022" })), jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.comment })] }))] })] }), isSelected && (jsxRuntime.jsx(react.Box, { width: "24px", height: "24px", borderRadius: "full", bg: {
5028
+ base: 'colorPalette.500',
5029
+ _dark: 'colorPalette.400',
5030
+ }, colorPalette: "blue", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsxRuntime.jsx(react.Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
4989
5031
  }) })) }))] }) }), 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 ??
4990
5032
  translate(removeIndex(`${colLabel}.cancel`)) ??
4991
5033
  'Cancel' }), jsxRuntime.jsx(react.Button, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFileId, children: labels?.select ??
@@ -5004,8 +5046,12 @@ const FilePicker = ({ column, schema, prefix }) => {
5004
5046
  : [];
5005
5047
  const colLabel = formI18n.colLabel;
5006
5048
  const [dialogOpen, setDialogOpen] = React.useState(false);
5049
+ const [failedImageIds, setFailedImageIds] = React.useState(new Set());
5007
5050
  const { onFetchFiles, enableMediaLibrary = false, filterImageOnly = false, } = filePicker || {};
5008
5051
  const showMediaLibrary = enableMediaLibrary && !!onFetchFiles;
5052
+ const handleImageError = (fileIdentifier) => {
5053
+ setFailedImageIds((prev) => new Set(prev).add(fileIdentifier));
5054
+ };
5009
5055
  const handleMediaLibrarySelect = (fileId) => {
5010
5056
  const newFiles = [...currentFiles, fileId];
5011
5057
  setValue(colLabel, newFiles);
@@ -5069,10 +5115,11 @@ const FilePicker = ({ column, schema, prefix }) => {
5069
5115
  const fileSize = getFileSize(file);
5070
5116
  const isImage = isImageFile(file);
5071
5117
  const imageUrl = getImageUrl(file);
5072
- return (jsxRuntime.jsx(react.Card.Root, { variant: 'subtle', 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: {
5073
- borderColor: 'blue.300',
5118
+ const imageFailed = failedImageIds.has(fileIdentifier);
5119
+ 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: {
5120
+ borderColor: 'colorPalette.300',
5074
5121
  bg: 'bg.muted',
5075
- }, 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 && imageUrl ? (jsxRuntime.jsx(react.Image, { src: imageUrl, alt: fileName, boxSize: "60px", objectFit: "cover", borderRadius: "md" })) : (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: fileName }), fileSize !== undefined && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: formatBytes(fileSize) }))] }), jsxRuntime.jsx(react.Icon, { as: ti.TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, fileIdentifier));
5122
+ }, 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 && imageUrl && !imageFailed ? (jsxRuntime.jsx(react.Image, { src: imageUrl, alt: fileName, boxSize: "60px", objectFit: "cover", borderRadius: "md", onError: () => handleImageError(fileIdentifier) })) : isImage && (imageFailed || !imageUrl) ? (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: fileName }), fileSize !== undefined && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: formatBytes(fileSize) }))] }), jsxRuntime.jsx(react.Icon, { as: ti.TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, fileIdentifier));
5076
5123
  }) })] }));
5077
5124
  };
5078
5125
 
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal,
3
3
  import { AiOutlineColumnWidth } from 'react-icons/ai';
4
4
  import * as React from 'react';
5
5
  import React__default, { createContext, useContext, useState, useEffect, useRef, forwardRef } from 'react';
6
- import { LuX, LuCheck, LuChevronRight, LuFile, LuSearch } from 'react-icons/lu';
6
+ import { LuX, LuCheck, LuChevronRight, LuImage, LuFile, LuSearch } from 'react-icons/lu';
7
7
  import { MdOutlineSort, MdFilterAlt, MdSearch, MdOutlineViewColumn, MdFilterListAlt, MdPushPin, MdCancel, MdClear, MdOutlineChecklist, MdDateRange } from 'react-icons/md';
8
8
  import { FaUpDown, FaGripLinesVertical, FaTrash } from 'react-icons/fa6';
9
9
  import { BiDownArrow, BiUpArrow, BiError } from 'react-icons/bi';
@@ -4849,7 +4849,7 @@ function getText(_ref2) {
4849
4849
  return source.getStringData(textMediaType);
4850
4850
  }
4851
4851
 
4852
- const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }, placeholder = "Drop files here or click to upload", }) => {
4852
+ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }, placeholder = 'Drop files here or click to upload', }) => {
4853
4853
  const ref = useRef(null);
4854
4854
  const [isDraggedOver, setIsDraggedOver] = useState(false);
4855
4855
  useEffect(() => {
@@ -4863,7 +4863,7 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4863
4863
  onDrop: ({ source }) => {
4864
4864
  const files = getFiles({ source });
4865
4865
  const text = getText({ source });
4866
- console.log(files, text, "dfposa");
4866
+ console.log(files, text, 'dfposa');
4867
4867
  onDrop({ files, text });
4868
4868
  },
4869
4869
  });
@@ -4872,9 +4872,9 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4872
4872
  function getColor(isDraggedOver) {
4873
4873
  if (isDraggedOver) {
4874
4874
  return {
4875
- backgroundColor: "blue.400",
4875
+ backgroundColor: 'blue.400',
4876
4876
  _dark: {
4877
- backgroundColor: "blue.400",
4877
+ backgroundColor: 'blue.400',
4878
4878
  },
4879
4879
  };
4880
4880
  }
@@ -4895,7 +4895,7 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4895
4895
  const filesArray = [...event.target.files];
4896
4896
  onDrop({ files: filesArray });
4897
4897
  };
4898
- return (jsxs(Grid, { ...getColor(isDraggedOver), ref: ref, cursor: "pointer", onClick: handleClick, borderStyle: "dashed", borderColor: "colorPalette.400", alignContent: "center", justifyContent: "center", borderWidth: 1, borderRadius: 4, ...gridProps, children: [children, !!children === false && (jsxs(Fragment, { children: [jsx(Flex, { children: placeholder }), jsx(Input, { type: "file", multiple: true, style: { display: "none" }, ref: fileInput, onChange: handleChange })] }))] }));
4898
+ return (jsxs(Grid, { ...getColor(isDraggedOver), ref: ref, cursor: 'pointer', onClick: handleClick, borderStyle: 'dashed', borderColor: 'colorPalette.400', alignContent: 'center', justifyContent: 'center', borderWidth: 1, borderRadius: 4, minH: "120px", ...gridProps, children: [children, !!children === false && (jsxs(Fragment, { children: [jsx(Flex, { children: placeholder }), jsx(Input, { type: "file", multiple: true, style: { display: 'none' }, ref: fileInput, onChange: handleChange })] }))] }));
4899
4899
  };
4900
4900
 
4901
4901
  /**
@@ -4915,6 +4915,7 @@ function formatBytes(bytes) {
4915
4915
  function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, labels, translate, colLabel, }) {
4916
4916
  const [searchTerm, setSearchTerm] = useState('');
4917
4917
  const [selectedFileId, setSelectedFileId] = useState('');
4918
+ const [failedImageIds, setFailedImageIds] = useState(new Set());
4918
4919
  const { data: filesData, isLoading, isError, } = useQuery({
4919
4920
  queryKey: ['file-picker-library', searchTerm],
4920
4921
  queryFn: async () => {
@@ -4941,31 +4942,72 @@ function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = fa
4941
4942
  onClose();
4942
4943
  setSelectedFileId('');
4943
4944
  setSearchTerm('');
4945
+ setFailedImageIds(new Set());
4946
+ };
4947
+ const handleImageError = (fileId) => {
4948
+ setFailedImageIds((prev) => new Set(prev).add(fileId));
4944
4949
  };
4945
4950
  if (!onFetchFiles)
4946
4951
  return null;
4947
4952
  return (jsx(DialogRoot, { open: open, onOpenChange: (e) => !e.open && handleClose(), children: jsxs(DialogContent, { maxWidth: "800px", maxHeight: "90vh", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { fontSize: "lg", fontWeight: "bold", children: title }), jsx(DialogCloseTrigger, {})] }), jsx(DialogBody, { children: jsxs(VStack, { align: "stretch", gap: 4, children: [jsxs(Box, { position: "relative", children: [jsx(Input, { placeholder: labels?.searchPlaceholder ??
4948
4953
  translate(removeIndex(`${colLabel}.search_placeholder`)) ??
4949
- 'Search files...', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), bg: "bg.panel", border: "1px solid", borderColor: "border.default", _focus: {
4950
- borderColor: 'blue.500',
4951
- boxShadow: '0 0 0 1px var(--chakra-colors-blue-500)',
4954
+ 'Search files...', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), bg: "bg.panel", border: "1px solid", borderColor: "border.default", colorPalette: "blue", _focus: {
4955
+ borderColor: 'colorPalette.500',
4956
+ _dark: {
4957
+ borderColor: 'colorPalette.400',
4958
+ },
4959
+ boxShadow: {
4960
+ base: '0 0 0 1px var(--chakra-colors-blue-500)',
4961
+ _dark: '0 0 0 1px var(--chakra-colors-blue-400)',
4962
+ },
4952
4963
  }, pl: 10 }), jsx(Icon, { as: LuSearch, position: "absolute", left: 3, top: "50%", transform: "translateY(-50%)", color: "fg.muted", boxSize: 4 })] }), isLoading && (jsxs(Box, { textAlign: "center", py: 8, children: [jsx(Spinner, { size: "lg", colorPalette: "blue" }), jsx(Text, { mt: 4, color: "fg.muted", children: labels?.loading ??
4953
4964
  translate(removeIndex(`${colLabel}.loading`)) ??
4954
- 'Loading files...' })] })), isError && (jsx(Box, { bg: "red.50", _dark: { bg: 'red.900/20' }, border: "1px solid", borderColor: "red.200", borderRadius: "md", p: 4, children: jsx(Text, { color: "red.600", _dark: { color: 'red.300' }, children: labels?.loadingFailed ??
4965
+ 'Loading files...' })] })), isError && (jsx(Box, { bg: { base: 'colorPalette.50', _dark: 'colorPalette.900/20' }, border: "1px solid", borderColor: {
4966
+ base: 'colorPalette.200',
4967
+ _dark: 'colorPalette.800',
4968
+ }, colorPalette: "red", borderRadius: "md", p: 4, children: jsx(Text, { color: {
4969
+ base: 'colorPalette.600',
4970
+ _dark: 'colorPalette.300',
4971
+ }, children: labels?.loadingFailed ??
4955
4972
  translate(removeIndex(`${colLabel}.error.loading_failed`)) ??
4956
4973
  '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 ??
4957
4974
  translate(removeIndex(`${colLabel}.no_files_found`)) ??
4958
4975
  'No files found' }) })) : (jsx(VStack, { align: "stretch", gap: 2, children: filteredFiles.map((file) => {
4959
4976
  const isImage = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name);
4960
4977
  const isSelected = selectedFileId === file.id;
4961
- return (jsx(Box, { p: 3, border: "2px solid", borderColor: isSelected ? 'blue.500' : 'border.default', borderRadius: "md", bg: isSelected ? 'blue.50' : 'bg.panel', _dark: {
4962
- bg: isSelected ? 'blue.900/20' : 'bg.panel',
4963
- }, cursor: "pointer", onClick: () => setSelectedFileId(file.id), _hover: {
4964
- borderColor: isSelected ? 'blue.600' : 'blue.300',
4965
- bg: isSelected ? 'blue.100' : 'bg.muted',
4966
- }, transition: "all 0.2s", children: jsxs(HStack, { gap: 3, children: [jsx(Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, children: isImage && file.url ? (jsx(Image, { src: file.url, alt: file.name, boxSize: "60px", objectFit: "cover", borderRadius: "md" })) : (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: file.name }), jsxs(HStack, { gap: 2, children: [file.size && (jsx(Fragment, { children: jsx(Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
4978
+ const imageFailed = failedImageIds.has(file.id);
4979
+ return (jsx(Box, { p: 3, border: "2px solid", borderColor: isSelected
4980
+ ? {
4981
+ base: 'colorPalette.500',
4982
+ _dark: 'colorPalette.400',
4983
+ }
4984
+ : 'border.default', borderRadius: "md", bg: isSelected
4985
+ ? {
4986
+ base: 'colorPalette.50',
4987
+ _dark: 'colorPalette.900/20',
4988
+ }
4989
+ : 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => setSelectedFileId(file.id), _hover: {
4990
+ borderColor: isSelected
4991
+ ? {
4992
+ base: 'colorPalette.600',
4993
+ _dark: 'colorPalette.400',
4994
+ }
4995
+ : {
4996
+ base: 'colorPalette.300',
4997
+ _dark: 'colorPalette.400',
4998
+ },
4999
+ bg: isSelected
5000
+ ? {
5001
+ base: 'colorPalette.100',
5002
+ _dark: 'colorPalette.800/30',
5003
+ }
5004
+ : 'bg.muted',
5005
+ }, transition: "all 0.2s", children: jsxs(HStack, { gap: 3, children: [jsx(Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, children: isImage && file.url && !imageFailed ? (jsx(Image, { src: file.url, alt: file.name, boxSize: "60px", objectFit: "cover", borderRadius: "md", onError: () => handleImageError(file.id) })) : isImage && (imageFailed || !file.url) ? (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: file.name }), jsxs(HStack, { gap: 2, children: [file.size && (jsx(Fragment, { children: jsx(Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
4967
5006
  ? formatBytes(file.size)
4968
- : file.size }) })), file.comment && (jsxs(Fragment, { children: [file.size && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: "\u2022" })), jsx(Text, { fontSize: "xs", color: "fg.muted", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.comment })] }))] })] }), isSelected && (jsx(Box, { width: "24px", height: "24px", borderRadius: "full", bg: "blue.500", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsx(Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
5007
+ : file.size }) })), file.comment && (jsxs(Fragment, { children: [file.size && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: "\u2022" })), jsx(Text, { fontSize: "xs", color: "fg.muted", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.comment })] }))] })] }), isSelected && (jsx(Box, { width: "24px", height: "24px", borderRadius: "full", bg: {
5008
+ base: 'colorPalette.500',
5009
+ _dark: 'colorPalette.400',
5010
+ }, colorPalette: "blue", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsx(Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
4969
5011
  }) })) }))] }) }), 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 ??
4970
5012
  translate(removeIndex(`${colLabel}.cancel`)) ??
4971
5013
  'Cancel' }), jsx(Button$1, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFileId, children: labels?.select ??
@@ -4984,8 +5026,12 @@ const FilePicker = ({ column, schema, prefix }) => {
4984
5026
  : [];
4985
5027
  const colLabel = formI18n.colLabel;
4986
5028
  const [dialogOpen, setDialogOpen] = useState(false);
5029
+ const [failedImageIds, setFailedImageIds] = useState(new Set());
4987
5030
  const { onFetchFiles, enableMediaLibrary = false, filterImageOnly = false, } = filePicker || {};
4988
5031
  const showMediaLibrary = enableMediaLibrary && !!onFetchFiles;
5032
+ const handleImageError = (fileIdentifier) => {
5033
+ setFailedImageIds((prev) => new Set(prev).add(fileIdentifier));
5034
+ };
4989
5035
  const handleMediaLibrarySelect = (fileId) => {
4990
5036
  const newFiles = [...currentFiles, fileId];
4991
5037
  setValue(colLabel, newFiles);
@@ -5049,10 +5095,11 @@ const FilePicker = ({ column, schema, prefix }) => {
5049
5095
  const fileSize = getFileSize(file);
5050
5096
  const isImage = isImageFile(file);
5051
5097
  const imageUrl = getImageUrl(file);
5052
- return (jsx(Card.Root, { variant: 'subtle', 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: {
5053
- borderColor: 'blue.300',
5098
+ const imageFailed = failedImageIds.has(fileIdentifier);
5099
+ 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: {
5100
+ borderColor: 'colorPalette.300',
5054
5101
  bg: 'bg.muted',
5055
- }, 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 && imageUrl ? (jsx(Image, { src: imageUrl, alt: fileName, boxSize: "60px", objectFit: "cover", borderRadius: "md" })) : (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: fileName }), fileSize !== undefined && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: formatBytes(fileSize) }))] }), jsx(Icon, { as: TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, fileIdentifier));
5102
+ }, 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 && imageUrl && !imageFailed ? (jsx(Image, { src: imageUrl, alt: fileName, boxSize: "60px", objectFit: "cover", borderRadius: "md", onError: () => handleImageError(fileIdentifier) })) : isImage && (imageFailed || !imageUrl) ? (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: fileName }), fileSize !== undefined && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: formatBytes(fileSize) }))] }), jsx(Icon, { as: TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, fileIdentifier));
5056
5103
  }) })] }));
5057
5104
  };
5058
5105
 
@@ -1,5 +1,5 @@
1
- import { GridProps } from "@chakra-ui/react";
2
- import { ReactNode } from "react";
1
+ import { GridProps } from '@chakra-ui/react';
2
+ import { ReactNode } from 'react';
3
3
  export interface FileDropzoneProps {
4
4
  children?: ReactNode;
5
5
  onDrop?: ({ files, text }: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsol-oss/react-datatable5",
3
- "version": "12.0.0-beta.76",
3
+ "version": "12.0.0-beta.77",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",