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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -112,6 +112,9 @@ interface DataTableProps<TData = unknown> {
112
112
  * Data array for the table.
113
113
  *
114
114
  * It will pass into as the data in `@tanstack/react-table`
115
+ * Do not toggle the data array, it will cause the table to re-render in infinite loop.
116
+ *
117
+ * @default []
115
118
  *
116
119
  */
117
120
  data: TData[];
@@ -386,7 +389,7 @@ interface TableProps extends TableRootProps {
386
389
  canResize?: boolean;
387
390
  children: ReactNode;
388
391
  }
389
- declare const Table: ({ children, emptyComponent, canResize, ...props }: TableProps) => string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null;
392
+ declare const Table: ({ children, emptyComponent, canResize, showLoading, ...props }: TableProps) => string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null;
390
393
 
391
394
  interface TableBodyProps {
392
395
  pinnedBgColor?: {
@@ -467,14 +470,15 @@ declare const TableHeader: ({ canResize, showSelector, isSticky, tableHeaderProp
467
470
 
468
471
  interface DefaultTableProps {
469
472
  showFooter?: boolean;
470
- tableProps?: Omit<TableProps, "children">;
473
+ tableProps?: Omit<TableProps, 'children'>;
471
474
  tableHeaderProps?: TableHeaderProps;
472
475
  tableBodyProps?: TableBodyProps;
473
476
  tableFooterProps?: TableFooterProps;
474
477
  controlProps?: TableControlsProps;
475
- variant?: "" | "greedy";
478
+ variant?: '' | 'greedy';
479
+ isLoading?: boolean;
476
480
  }
477
- declare const DefaultTable: ({ showFooter, tableProps, tableHeaderProps, tableBodyProps, tableFooterProps, controlProps, variant, }: DefaultTableProps) => react_jsx_runtime.JSX.Element;
481
+ declare const DefaultTable: ({ showFooter, tableProps, tableHeaderProps, tableBodyProps, tableFooterProps, controlProps, variant, isLoading, }: DefaultTableProps) => react_jsx_runtime.JSX.Element;
478
482
 
479
483
  interface ReloadButtonProps {
480
484
  variant?: string;
package/dist/index.js CHANGED
@@ -3114,9 +3114,10 @@ const EmptyState = React__namespace.forwardRef(function EmptyState(props, ref) {
3114
3114
  });
3115
3115
 
3116
3116
  const EmptyResult = (jsxRuntime.jsx(EmptyState, { icon: jsxRuntime.jsx(hi.HiColorSwatch, {}), title: "No results found", description: "Try adjusting your search", children: jsxRuntime.jsxs(react.List.Root, { variant: "marker", children: [jsxRuntime.jsx(react.List.Item, { children: "Try removing filters" }), jsxRuntime.jsx(react.List.Item, { children: "Try different keywords" })] }) }));
3117
- const Table = ({ children, emptyComponent = EmptyResult, canResize = true, ...props }) => {
3117
+ const Table = ({ children, emptyComponent = EmptyResult, canResize = true, showLoading = false, ...props }) => {
3118
3118
  const { table } = useDataTableContext();
3119
- if (table.getRowModel().rows.length <= 0) {
3119
+ // Skip empty check when loading to allow skeleton to render
3120
+ if (!showLoading && table.getRowModel().rows.length <= 0) {
3120
3121
  return emptyComponent;
3121
3122
  }
3122
3123
  return (jsxRuntime.jsx(react.Table.Root, { stickyHeader: true, variant: "outline", width: canResize ? table.getCenterTotalSize() : undefined, display: "grid", alignContent: "start", overflowY: "auto", bg: { base: "colorPalette.50", _dark: "colorPalette.950" }, ...props, children: children }));
@@ -3185,6 +3186,65 @@ const TableRowSelector = ({ row, }) => {
3185
3186
  onCheckedChange: row.getToggleSelectedHandler() }) }));
3186
3187
  };
3187
3188
 
3189
+ const TableBodySkeleton = ({ showSelector = false, canResize = true, }) => {
3190
+ "use no memo";
3191
+ const { table } = useDataTableContext();
3192
+ const SELECTION_BOX_WIDTH = 20;
3193
+ const [hoveredRow, setHoveredRow] = React.useState(-1);
3194
+ const handleRowHover = (index) => {
3195
+ setHoveredRow(index);
3196
+ };
3197
+ const getTdProps = (column) => {
3198
+ const tdProps = column.getIsPinned()
3199
+ ? {
3200
+ left: showSelector
3201
+ ? `${column.getStart("left") + SELECTION_BOX_WIDTH + table.getDensityValue() * 2}px`
3202
+ : `${column.getStart("left")}px`,
3203
+ position: "relative",
3204
+ }
3205
+ : {};
3206
+ return tdProps;
3207
+ };
3208
+ const getTrProps = ({ hoveredRow, index, }) => {
3209
+ if (hoveredRow === -1) {
3210
+ return {};
3211
+ }
3212
+ if (hoveredRow === index) {
3213
+ return {
3214
+ opacity: "1",
3215
+ };
3216
+ }
3217
+ return {
3218
+ opacity: "0.8",
3219
+ };
3220
+ };
3221
+ // Get the number of skeleton rows based on current pageSize
3222
+ const pageSize = table.getState().pagination.pageSize;
3223
+ const visibleColumns = table.getVisibleLeafColumns();
3224
+ return (jsxRuntime.jsx(react.Table.Body, { children: Array.from({ length: pageSize }).map((_, rowIndex) => {
3225
+ return (jsxRuntime.jsxs(react.Table.Row, { display: "flex", zIndex: 1, onMouseEnter: () => handleRowHover(rowIndex), onMouseLeave: () => handleRowHover(-1), ...getTrProps({ hoveredRow, index: rowIndex }), children: [showSelector && (jsxRuntime.jsx(TableRowSelectorSkeleton, {})), visibleColumns.map((column, colIndex) => {
3226
+ return (jsxRuntime.jsx(react.Table.Cell, { padding: `${table.getDensityValue()}px`,
3227
+ // styling resize and pinning start
3228
+ flex: `${canResize ? "0" : "1"} 0 ${column.getSize()}px`,
3229
+ // this is to avoid the cell from being too wide
3230
+ minWidth: `0`, color: {
3231
+ base: "colorPalette.900",
3232
+ _dark: "colorPalette.100",
3233
+ },
3234
+ bg: { base: "colorPalette.50", _dark: "colorPalette.950" }, ...getTdProps(column), children: jsxRuntime.jsx(react.Skeleton, { height: "20px", width: "80%" }) }, `chakra-table-skeleton-cell-${rowIndex}-${colIndex}`));
3235
+ })] }, `chakra-table-skeleton-row-${rowIndex}`));
3236
+ }) }));
3237
+ };
3238
+ const TableRowSelectorSkeleton = () => {
3239
+ const { table } = useDataTableContext();
3240
+ const SELECTION_BOX_WIDTH = 20;
3241
+ return (jsxRuntime.jsx(react.Table.Cell, { padding: `${table.getDensityValue()}px`, display: "grid", color: {
3242
+ base: "colorPalette.900",
3243
+ _dark: "colorPalette.100",
3244
+ },
3245
+ bg: { base: "colorPalette.50", _dark: "colorPalette.950" }, justifyItems: "center", alignItems: "center", children: jsxRuntime.jsx(react.Skeleton, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px` }) }));
3246
+ };
3247
+
3188
3248
  const TableFooter = ({ showSelector = false, alwaysShowSelector = true, }) => {
3189
3249
  const table = useDataTableContext().table;
3190
3250
  const SELECTION_BOX_WIDTH = 20;
@@ -3346,11 +3406,12 @@ const TableHeader = ({ canResize = true, showSelector = false, isSticky = true,
3346
3406
  })] }, `chakra-table-headergroup-${headerGroup.id}`))) }));
3347
3407
  };
3348
3408
 
3349
- const DefaultTable = ({ showFooter = false, tableProps = {}, tableHeaderProps = {}, tableBodyProps = {}, tableFooterProps = {}, controlProps = {}, variant = "", }) => {
3350
- if (variant === "greedy") {
3351
- return (jsxRuntime.jsx(TableControls, { ...controlProps, children: jsxRuntime.jsxs(Table, { canResize: false, ...{ ...tableProps }, children: [jsxRuntime.jsx(TableHeader, { canResize: false, ...tableHeaderProps }), jsxRuntime.jsx(TableBody, { canResize: false, ...tableBodyProps }), showFooter && (jsxRuntime.jsx(TableFooter, { canResize: false, ...tableFooterProps }))] }) }));
3409
+ const DefaultTable = ({ showFooter = false, tableProps = {}, tableHeaderProps = {}, tableBodyProps = {}, tableFooterProps = {}, controlProps = {}, variant = '', isLoading = false, }) => {
3410
+ const bodyComponent = isLoading ? (jsxRuntime.jsx(TableBodySkeleton, { showSelector: tableBodyProps.showSelector, canResize: tableBodyProps.canResize })) : (jsxRuntime.jsx(TableBody, { ...tableBodyProps }));
3411
+ if (variant === 'greedy') {
3412
+ return (jsxRuntime.jsx(TableControls, { ...controlProps, children: jsxRuntime.jsxs(Table, { canResize: false, showLoading: isLoading, ...tableProps, children: [jsxRuntime.jsx(TableHeader, { canResize: false, ...tableHeaderProps }), bodyComponent, showFooter && (jsxRuntime.jsx(TableFooter, { canResize: false, ...tableFooterProps }))] }) }));
3352
3413
  }
3353
- return (jsxRuntime.jsx(TableControls, { ...controlProps, children: jsxRuntime.jsxs(Table, { ...tableProps, children: [jsxRuntime.jsx(TableHeader, { ...tableHeaderProps }), jsxRuntime.jsx(TableBody, { ...tableBodyProps }), showFooter && jsxRuntime.jsx(TableFooter, { ...tableFooterProps })] }) }));
3414
+ return (jsxRuntime.jsx(TableControls, { ...controlProps, children: jsxRuntime.jsxs(Table, { showLoading: isLoading, ...tableProps, children: [jsxRuntime.jsx(TableHeader, { ...tableHeaderProps }), bodyComponent, showFooter && jsxRuntime.jsx(TableFooter, { ...tableFooterProps })] }) }));
3354
3415
  };
3355
3416
 
3356
3417
  const TableCardContainer = ({ children, variant = "", gap = "1rem", gridTemplateColumns = "repeat(auto-fit, minmax(20rem, 1fr))", direction = "row", ...props }) => {
@@ -4469,7 +4530,8 @@ function filterArray(array, searchTerm) {
4469
4530
 
4470
4531
  const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLimit = false, }) => {
4471
4532
  const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
4472
- const { translate, enumPickerLabels } = useSchemaContext();
4533
+ const { enumPickerLabels } = useSchemaContext();
4534
+ const formI18n = useFormI18n(column, prefix);
4473
4535
  const { required, variant } = schema;
4474
4536
  const isRequired = required?.some((columnId) => columnId === column);
4475
4537
  const { gridColumn = 'span 12', gridRow = 'span 1', renderDisplay } = schema;
@@ -4477,7 +4539,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4477
4539
  const [limit, setLimit] = React.useState(10);
4478
4540
  const [openSearchResult, setOpenSearchResult] = React.useState();
4479
4541
  const ref = React.useRef(null);
4480
- const colLabel = `${prefix}${column}`;
4542
+ const colLabel = formI18n.colLabel;
4481
4543
  const watchEnum = watch(colLabel);
4482
4544
  const watchEnums = (watch(colLabel) ?? []);
4483
4545
  const dataList = schema.enum ?? [];
@@ -4488,10 +4550,8 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4488
4550
  setLimit(10);
4489
4551
  };
4490
4552
  if (variant === 'radio') {
4491
- return (jsxRuntime.jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
4492
- gridRow, errorText: errors[`${colLabel}`]
4493
- ? translate.t(removeIndex(`${colLabel}.field_required`))
4494
- : 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) => {
4553
+ return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4554
+ 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) => {
4495
4555
  return (jsxRuntime.jsxs(react.RadioGroup.Item, { onClick: () => {
4496
4556
  if (!isMultiple) {
4497
4557
  setOpenSearchResult(false);
@@ -4502,13 +4562,11 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4502
4562
  setValue(colLabel, [...newSet]);
4503
4563
  }, value: item, children: [jsxRuntime.jsx(react.RadioGroup.ItemHiddenInput, {}), jsxRuntime.jsx(react.RadioGroup.ItemIndicator, {}), jsxRuntime.jsx(react.RadioGroup.ItemText, { children: !!renderDisplay === true
4504
4564
  ? renderDisplay(item)
4505
- : translate.t(removeIndex(`${colLabel}.${item}`)) })] }, `${colLabel}-${item}`));
4565
+ : formI18n.t(item) })] }, `${colLabel}-${item}`));
4506
4566
  }) }) }) }));
4507
4567
  }
4508
- return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
4509
- gridRow, errorText: errors[`${colLabel}`]
4510
- ? translate.t(removeIndex(`${colLabel}.field_required`))
4511
- : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxRuntime.jsxs(react.Flex, { flexFlow: 'wrap', gap: 1, children: [watchEnums.map((enumValue) => {
4568
+ return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4569
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxRuntime.jsxs(react.Flex, { flexFlow: 'wrap', gap: 1, children: [watchEnums.map((enumValue) => {
4512
4570
  const item = enumValue;
4513
4571
  if (!!item === false) {
4514
4572
  return jsxRuntime.jsx(jsxRuntime.Fragment, {});
@@ -4517,19 +4575,15 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4517
4575
  setValue(column, watchEnums.filter((id) => id != item));
4518
4576
  }, children: !!renderDisplay === true
4519
4577
  ? renderDisplay(item)
4520
- : translate.t(removeIndex(`${colLabel}.${item}`)) }, item));
4578
+ : formI18n.t(item) }, item));
4521
4579
  }), jsxRuntime.jsx(Tag, { size: "lg", cursor: 'pointer', onClick: () => {
4522
4580
  setOpenSearchResult(true);
4523
- }, children: enumPickerLabels?.addMore ??
4524
- translate.t(removeIndex(`${colLabel}.add_more`)) }, `${colLabel}-add-more-tag`)] })), !isMultiple && (jsxRuntime.jsx(Button, { variant: 'outline', onClick: () => {
4581
+ }, children: enumPickerLabels?.addMore ?? formI18n.t('add_more') }, `${colLabel}-add-more-tag`)] })), !isMultiple && (jsxRuntime.jsx(Button, { variant: 'outline', onClick: () => {
4525
4582
  setOpenSearchResult(true);
4526
- }, justifyContent: 'start', children: !!watchEnum === false
4527
- ? ''
4528
- : translate.t(removeIndex(`${colLabel}.${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 ??
4529
- translate.t(`${colLabel}.type_to_search`), onChange: (event) => {
4583
+ }, 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) => {
4530
4584
  onSearchChange(event);
4531
4585
  setOpenSearchResult(true);
4532
- }, autoComplete: "off", ref: ref }), jsxRuntime.jsx(PopoverTitle, {}), showTotalAndLimit && (jsxRuntime.jsx(react.Text, { children: `${enumPickerLabels?.total ?? translate.t(removeIndex(`${colLabel}.total`))}: ${count}, ${enumPickerLabels?.showing ?? translate.t(removeIndex(`${colLabel}.showing`))} ${limit}` })), jsxRuntime.jsxs(react.Grid, { overflow: 'auto', maxHeight: '20rem', children: [jsxRuntime.jsx(react.Flex, { flexFlow: 'column wrap', children: dataList
4586
+ }, 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
4533
4587
  .filter((item) => {
4534
4588
  const searchTerm = (searchText || '').toLowerCase();
4535
4589
  if (!searchTerm)
@@ -4541,7 +4595,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4541
4595
  // Check if the display value (translation) contains the search text
4542
4596
  const displayValue = !!renderDisplay === true
4543
4597
  ? renderDisplay(item)
4544
- : translate.t(removeIndex(`${colLabel}.${item}`));
4598
+ : formI18n.t(item);
4545
4599
  // Convert to string and check if it includes the search term
4546
4600
  const displayValueString = String(displayValue).toLowerCase();
4547
4601
  const displayValueMatch = displayValueString.includes(searchTerm);
@@ -4561,9 +4615,9 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4561
4615
  setValue(colLabel, [...newSet]);
4562
4616
  }, ...(selected ? { color: 'colorPalette.400/50' } : {}), children: !!renderDisplay === true
4563
4617
  ? renderDisplay(item)
4564
- : translate.t(removeIndex(`${colLabel}.${item}`)) }, `${colLabel}-${item}`));
4618
+ : formI18n.t(item) }, `${colLabel}-${item}`));
4565
4619
  }) }), isDirty && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: dataList.length <= 0 && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: enumPickerLabels?.emptySearchResult ??
4566
- translate.t(removeIndex(`${colLabel}.empty_search_result`)) })) }))] })] }) })] })] }));
4620
+ formI18n.t('empty_search_result') })) }))] })] }) })] })] }));
4567
4621
  };
4568
4622
 
4569
4623
  function isEnteringWindow(_ref) {
@@ -4869,7 +4923,7 @@ function getText(_ref2) {
4869
4923
  return source.getStringData(textMediaType);
4870
4924
  }
4871
4925
 
4872
- const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }, placeholder = "Drop files here or click to upload", }) => {
4926
+ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }, placeholder = 'Drop files here or click to upload', }) => {
4873
4927
  const ref = React.useRef(null);
4874
4928
  const [isDraggedOver, setIsDraggedOver] = React.useState(false);
4875
4929
  React.useEffect(() => {
@@ -4883,7 +4937,7 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4883
4937
  onDrop: ({ source }) => {
4884
4938
  const files = getFiles({ source });
4885
4939
  const text = getText({ source });
4886
- console.log(files, text, "dfposa");
4940
+ console.log(files, text, 'dfposa');
4887
4941
  onDrop({ files, text });
4888
4942
  },
4889
4943
  });
@@ -4892,9 +4946,9 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4892
4946
  function getColor(isDraggedOver) {
4893
4947
  if (isDraggedOver) {
4894
4948
  return {
4895
- backgroundColor: "blue.400",
4949
+ backgroundColor: 'blue.400',
4896
4950
  _dark: {
4897
- backgroundColor: "blue.400",
4951
+ backgroundColor: 'blue.400',
4898
4952
  },
4899
4953
  };
4900
4954
  }
@@ -4915,7 +4969,7 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4915
4969
  const filesArray = [...event.target.files];
4916
4970
  onDrop({ files: filesArray });
4917
4971
  };
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 })] }))] }));
4972
+ 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
4973
  };
4920
4974
 
4921
4975
  /**
@@ -4935,6 +4989,7 @@ function formatBytes(bytes) {
4935
4989
  function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, labels, translate, colLabel, }) {
4936
4990
  const [searchTerm, setSearchTerm] = React.useState('');
4937
4991
  const [selectedFileId, setSelectedFileId] = React.useState('');
4992
+ const [failedImageIds, setFailedImageIds] = React.useState(new Set());
4938
4993
  const { data: filesData, isLoading, isError, } = reactQuery.useQuery({
4939
4994
  queryKey: ['file-picker-library', searchTerm],
4940
4995
  queryFn: async () => {
@@ -4961,31 +5016,72 @@ function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = fa
4961
5016
  onClose();
4962
5017
  setSelectedFileId('');
4963
5018
  setSearchTerm('');
5019
+ setFailedImageIds(new Set());
5020
+ };
5021
+ const handleImageError = (fileId) => {
5022
+ setFailedImageIds((prev) => new Set(prev).add(fileId));
4964
5023
  };
4965
5024
  if (!onFetchFiles)
4966
5025
  return null;
4967
5026
  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
5027
  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)',
5028
+ 'Search files...', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), bg: "bg.panel", border: "1px solid", borderColor: "border.default", colorPalette: "blue", _focus: {
5029
+ borderColor: 'colorPalette.500',
5030
+ _dark: {
5031
+ borderColor: 'colorPalette.400',
5032
+ },
5033
+ boxShadow: {
5034
+ base: '0 0 0 1px var(--chakra-colors-blue-500)',
5035
+ _dark: '0 0 0 1px var(--chakra-colors-blue-400)',
5036
+ },
4972
5037
  }, 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
5038
  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 ??
5039
+ 'Loading files...' })] })), isError && (jsxRuntime.jsx(react.Box, { bg: { base: 'colorPalette.50', _dark: 'colorPalette.900/20' }, border: "1px solid", borderColor: {
5040
+ base: 'colorPalette.200',
5041
+ _dark: 'colorPalette.800',
5042
+ }, colorPalette: "red", borderRadius: "md", p: 4, children: jsxRuntime.jsx(react.Text, { color: {
5043
+ base: 'colorPalette.600',
5044
+ _dark: 'colorPalette.300',
5045
+ }, children: labels?.loadingFailed ??
4975
5046
  translate(removeIndex(`${colLabel}.error.loading_failed`)) ??
4976
5047
  '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
5048
  translate(removeIndex(`${colLabel}.no_files_found`)) ??
4978
5049
  'No files found' }) })) : (jsxRuntime.jsx(react.VStack, { align: "stretch", gap: 2, children: filteredFiles.map((file) => {
4979
5050
  const isImage = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name);
4980
5051
  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'
5052
+ const imageFailed = failedImageIds.has(file.id);
5053
+ return (jsxRuntime.jsx(react.Box, { p: 3, border: "2px solid", borderColor: isSelected
5054
+ ? {
5055
+ base: 'colorPalette.500',
5056
+ _dark: 'colorPalette.400',
5057
+ }
5058
+ : 'border.default', borderRadius: "md", bg: isSelected
5059
+ ? {
5060
+ base: 'colorPalette.50',
5061
+ _dark: 'colorPalette.900/20',
5062
+ }
5063
+ : 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => setSelectedFileId(file.id), _hover: {
5064
+ borderColor: isSelected
5065
+ ? {
5066
+ base: 'colorPalette.600',
5067
+ _dark: 'colorPalette.400',
5068
+ }
5069
+ : {
5070
+ base: 'colorPalette.300',
5071
+ _dark: 'colorPalette.400',
5072
+ },
5073
+ bg: isSelected
5074
+ ? {
5075
+ base: 'colorPalette.100',
5076
+ _dark: 'colorPalette.800/30',
5077
+ }
5078
+ : 'bg.muted',
5079
+ }, 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
5080
  ? 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));
5081
+ : 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: {
5082
+ base: 'colorPalette.500',
5083
+ _dark: 'colorPalette.400',
5084
+ }, 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
5085
  }) })) }))] }) }), 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
5086
  translate(removeIndex(`${colLabel}.cancel`)) ??
4991
5087
  'Cancel' }), jsxRuntime.jsx(react.Button, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFileId, children: labels?.select ??
@@ -5004,8 +5100,12 @@ const FilePicker = ({ column, schema, prefix }) => {
5004
5100
  : [];
5005
5101
  const colLabel = formI18n.colLabel;
5006
5102
  const [dialogOpen, setDialogOpen] = React.useState(false);
5103
+ const [failedImageIds, setFailedImageIds] = React.useState(new Set());
5007
5104
  const { onFetchFiles, enableMediaLibrary = false, filterImageOnly = false, } = filePicker || {};
5008
5105
  const showMediaLibrary = enableMediaLibrary && !!onFetchFiles;
5106
+ const handleImageError = (fileIdentifier) => {
5107
+ setFailedImageIds((prev) => new Set(prev).add(fileIdentifier));
5108
+ };
5009
5109
  const handleMediaLibrarySelect = (fileId) => {
5010
5110
  const newFiles = [...currentFiles, fileId];
5011
5111
  setValue(colLabel, newFiles);
@@ -5069,10 +5169,11 @@ const FilePicker = ({ column, schema, prefix }) => {
5069
5169
  const fileSize = getFileSize(file);
5070
5170
  const isImage = isImageFile(file);
5071
5171
  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',
5172
+ const imageFailed = failedImageIds.has(fileIdentifier);
5173
+ 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: {
5174
+ borderColor: 'colorPalette.300',
5074
5175
  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));
5176
+ }, 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
5177
  }) })] }));
5077
5178
  };
5078
5179
 
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
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, Image, EmptyState as EmptyState$2, VStack, Alert, Card, Group, InputElement, Tooltip as Tooltip$1, Icon, List, Table as Table$1, Checkbox as Checkbox$1, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Field as Field$1, Popover, NumberInput, Show, RadioCard, CheckboxGroup, Center, Heading } 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, Image, EmptyState as EmptyState$2, VStack, Alert, Card, Group, InputElement, Tooltip as Tooltip$1, Icon, List, Table as Table$1, Checkbox as Checkbox$1, Skeleton, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Field as Field$1, Popover, NumberInput, Show, RadioCard, CheckboxGroup, Center, Heading } from '@chakra-ui/react';
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';
@@ -3094,9 +3094,10 @@ const EmptyState = React.forwardRef(function EmptyState(props, ref) {
3094
3094
  });
3095
3095
 
3096
3096
  const EmptyResult = (jsx(EmptyState, { icon: jsx(HiColorSwatch, {}), title: "No results found", description: "Try adjusting your search", children: jsxs(List.Root, { variant: "marker", children: [jsx(List.Item, { children: "Try removing filters" }), jsx(List.Item, { children: "Try different keywords" })] }) }));
3097
- const Table = ({ children, emptyComponent = EmptyResult, canResize = true, ...props }) => {
3097
+ const Table = ({ children, emptyComponent = EmptyResult, canResize = true, showLoading = false, ...props }) => {
3098
3098
  const { table } = useDataTableContext();
3099
- if (table.getRowModel().rows.length <= 0) {
3099
+ // Skip empty check when loading to allow skeleton to render
3100
+ if (!showLoading && table.getRowModel().rows.length <= 0) {
3100
3101
  return emptyComponent;
3101
3102
  }
3102
3103
  return (jsx(Table$1.Root, { stickyHeader: true, variant: "outline", width: canResize ? table.getCenterTotalSize() : undefined, display: "grid", alignContent: "start", overflowY: "auto", bg: { base: "colorPalette.50", _dark: "colorPalette.950" }, ...props, children: children }));
@@ -3165,6 +3166,65 @@ const TableRowSelector = ({ row, }) => {
3165
3166
  onCheckedChange: row.getToggleSelectedHandler() }) }));
3166
3167
  };
3167
3168
 
3169
+ const TableBodySkeleton = ({ showSelector = false, canResize = true, }) => {
3170
+ "use no memo";
3171
+ const { table } = useDataTableContext();
3172
+ const SELECTION_BOX_WIDTH = 20;
3173
+ const [hoveredRow, setHoveredRow] = useState(-1);
3174
+ const handleRowHover = (index) => {
3175
+ setHoveredRow(index);
3176
+ };
3177
+ const getTdProps = (column) => {
3178
+ const tdProps = column.getIsPinned()
3179
+ ? {
3180
+ left: showSelector
3181
+ ? `${column.getStart("left") + SELECTION_BOX_WIDTH + table.getDensityValue() * 2}px`
3182
+ : `${column.getStart("left")}px`,
3183
+ position: "relative",
3184
+ }
3185
+ : {};
3186
+ return tdProps;
3187
+ };
3188
+ const getTrProps = ({ hoveredRow, index, }) => {
3189
+ if (hoveredRow === -1) {
3190
+ return {};
3191
+ }
3192
+ if (hoveredRow === index) {
3193
+ return {
3194
+ opacity: "1",
3195
+ };
3196
+ }
3197
+ return {
3198
+ opacity: "0.8",
3199
+ };
3200
+ };
3201
+ // Get the number of skeleton rows based on current pageSize
3202
+ const pageSize = table.getState().pagination.pageSize;
3203
+ const visibleColumns = table.getVisibleLeafColumns();
3204
+ return (jsx(Table$1.Body, { children: Array.from({ length: pageSize }).map((_, rowIndex) => {
3205
+ return (jsxs(Table$1.Row, { display: "flex", zIndex: 1, onMouseEnter: () => handleRowHover(rowIndex), onMouseLeave: () => handleRowHover(-1), ...getTrProps({ hoveredRow, index: rowIndex }), children: [showSelector && (jsx(TableRowSelectorSkeleton, {})), visibleColumns.map((column, colIndex) => {
3206
+ return (jsx(Table$1.Cell, { padding: `${table.getDensityValue()}px`,
3207
+ // styling resize and pinning start
3208
+ flex: `${canResize ? "0" : "1"} 0 ${column.getSize()}px`,
3209
+ // this is to avoid the cell from being too wide
3210
+ minWidth: `0`, color: {
3211
+ base: "colorPalette.900",
3212
+ _dark: "colorPalette.100",
3213
+ },
3214
+ bg: { base: "colorPalette.50", _dark: "colorPalette.950" }, ...getTdProps(column), children: jsx(Skeleton, { height: "20px", width: "80%" }) }, `chakra-table-skeleton-cell-${rowIndex}-${colIndex}`));
3215
+ })] }, `chakra-table-skeleton-row-${rowIndex}`));
3216
+ }) }));
3217
+ };
3218
+ const TableRowSelectorSkeleton = () => {
3219
+ const { table } = useDataTableContext();
3220
+ const SELECTION_BOX_WIDTH = 20;
3221
+ return (jsx(Table$1.Cell, { padding: `${table.getDensityValue()}px`, display: "grid", color: {
3222
+ base: "colorPalette.900",
3223
+ _dark: "colorPalette.100",
3224
+ },
3225
+ bg: { base: "colorPalette.50", _dark: "colorPalette.950" }, justifyItems: "center", alignItems: "center", children: jsx(Skeleton, { width: `${SELECTION_BOX_WIDTH}px`, height: `${SELECTION_BOX_WIDTH}px` }) }));
3226
+ };
3227
+
3168
3228
  const TableFooter = ({ showSelector = false, alwaysShowSelector = true, }) => {
3169
3229
  const table = useDataTableContext().table;
3170
3230
  const SELECTION_BOX_WIDTH = 20;
@@ -3326,11 +3386,12 @@ const TableHeader = ({ canResize = true, showSelector = false, isSticky = true,
3326
3386
  })] }, `chakra-table-headergroup-${headerGroup.id}`))) }));
3327
3387
  };
3328
3388
 
3329
- const DefaultTable = ({ showFooter = false, tableProps = {}, tableHeaderProps = {}, tableBodyProps = {}, tableFooterProps = {}, controlProps = {}, variant = "", }) => {
3330
- if (variant === "greedy") {
3331
- return (jsx(TableControls, { ...controlProps, children: jsxs(Table, { canResize: false, ...{ ...tableProps }, children: [jsx(TableHeader, { canResize: false, ...tableHeaderProps }), jsx(TableBody, { canResize: false, ...tableBodyProps }), showFooter && (jsx(TableFooter, { canResize: false, ...tableFooterProps }))] }) }));
3389
+ const DefaultTable = ({ showFooter = false, tableProps = {}, tableHeaderProps = {}, tableBodyProps = {}, tableFooterProps = {}, controlProps = {}, variant = '', isLoading = false, }) => {
3390
+ const bodyComponent = isLoading ? (jsx(TableBodySkeleton, { showSelector: tableBodyProps.showSelector, canResize: tableBodyProps.canResize })) : (jsx(TableBody, { ...tableBodyProps }));
3391
+ if (variant === 'greedy') {
3392
+ return (jsx(TableControls, { ...controlProps, children: jsxs(Table, { canResize: false, showLoading: isLoading, ...tableProps, children: [jsx(TableHeader, { canResize: false, ...tableHeaderProps }), bodyComponent, showFooter && (jsx(TableFooter, { canResize: false, ...tableFooterProps }))] }) }));
3332
3393
  }
3333
- return (jsx(TableControls, { ...controlProps, children: jsxs(Table, { ...tableProps, children: [jsx(TableHeader, { ...tableHeaderProps }), jsx(TableBody, { ...tableBodyProps }), showFooter && jsx(TableFooter, { ...tableFooterProps })] }) }));
3394
+ return (jsx(TableControls, { ...controlProps, children: jsxs(Table, { showLoading: isLoading, ...tableProps, children: [jsx(TableHeader, { ...tableHeaderProps }), bodyComponent, showFooter && jsx(TableFooter, { ...tableFooterProps })] }) }));
3334
3395
  };
3335
3396
 
3336
3397
  const TableCardContainer = ({ children, variant = "", gap = "1rem", gridTemplateColumns = "repeat(auto-fit, minmax(20rem, 1fr))", direction = "row", ...props }) => {
@@ -4449,7 +4510,8 @@ function filterArray(array, searchTerm) {
4449
4510
 
4450
4511
  const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLimit = false, }) => {
4451
4512
  const { watch, formState: { errors }, setValue, } = useFormContext();
4452
- const { translate, enumPickerLabels } = useSchemaContext();
4513
+ const { enumPickerLabels } = useSchemaContext();
4514
+ const formI18n = useFormI18n(column, prefix);
4453
4515
  const { required, variant } = schema;
4454
4516
  const isRequired = required?.some((columnId) => columnId === column);
4455
4517
  const { gridColumn = 'span 12', gridRow = 'span 1', renderDisplay } = schema;
@@ -4457,7 +4519,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4457
4519
  const [limit, setLimit] = useState(10);
4458
4520
  const [openSearchResult, setOpenSearchResult] = useState();
4459
4521
  const ref = useRef(null);
4460
- const colLabel = `${prefix}${column}`;
4522
+ const colLabel = formI18n.colLabel;
4461
4523
  const watchEnum = watch(colLabel);
4462
4524
  const watchEnums = (watch(colLabel) ?? []);
4463
4525
  const dataList = schema.enum ?? [];
@@ -4468,10 +4530,8 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4468
4530
  setLimit(10);
4469
4531
  };
4470
4532
  if (variant === 'radio') {
4471
- return (jsx(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
4472
- gridRow, errorText: errors[`${colLabel}`]
4473
- ? translate.t(removeIndex(`${colLabel}.field_required`))
4474
- : undefined, invalid: !!errors[colLabel], children: jsx(RadioGroup$1.Root, { defaultValue: "1", children: jsx(HStack, { gap: "6", children: filterArray(dataList, searchText ?? '').map((item) => {
4533
+ return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4534
+ 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) => {
4475
4535
  return (jsxs(RadioGroup$1.Item, { onClick: () => {
4476
4536
  if (!isMultiple) {
4477
4537
  setOpenSearchResult(false);
@@ -4482,13 +4542,11 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4482
4542
  setValue(colLabel, [...newSet]);
4483
4543
  }, value: item, children: [jsx(RadioGroup$1.ItemHiddenInput, {}), jsx(RadioGroup$1.ItemIndicator, {}), jsx(RadioGroup$1.ItemText, { children: !!renderDisplay === true
4484
4544
  ? renderDisplay(item)
4485
- : translate.t(removeIndex(`${colLabel}.${item}`)) })] }, `${colLabel}-${item}`));
4545
+ : formI18n.t(item) })] }, `${colLabel}-${item}`));
4486
4546
  }) }) }) }));
4487
4547
  }
4488
- return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: 'stretch', gridColumn,
4489
- gridRow, errorText: errors[`${colLabel}`]
4490
- ? translate.t(removeIndex(`${colLabel}.field_required`))
4491
- : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxs(Flex, { flexFlow: 'wrap', gap: 1, children: [watchEnums.map((enumValue) => {
4548
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4549
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && (jsxs(Flex, { flexFlow: 'wrap', gap: 1, children: [watchEnums.map((enumValue) => {
4492
4550
  const item = enumValue;
4493
4551
  if (!!item === false) {
4494
4552
  return jsx(Fragment, {});
@@ -4497,19 +4555,15 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4497
4555
  setValue(column, watchEnums.filter((id) => id != item));
4498
4556
  }, children: !!renderDisplay === true
4499
4557
  ? renderDisplay(item)
4500
- : translate.t(removeIndex(`${colLabel}.${item}`)) }, item));
4558
+ : formI18n.t(item) }, item));
4501
4559
  }), jsx(Tag, { size: "lg", cursor: 'pointer', onClick: () => {
4502
4560
  setOpenSearchResult(true);
4503
- }, children: enumPickerLabels?.addMore ??
4504
- translate.t(removeIndex(`${colLabel}.add_more`)) }, `${colLabel}-add-more-tag`)] })), !isMultiple && (jsx(Button, { variant: 'outline', onClick: () => {
4561
+ }, children: enumPickerLabels?.addMore ?? formI18n.t('add_more') }, `${colLabel}-add-more-tag`)] })), !isMultiple && (jsx(Button, { variant: 'outline', onClick: () => {
4505
4562
  setOpenSearchResult(true);
4506
- }, justifyContent: 'start', children: !!watchEnum === false
4507
- ? ''
4508
- : translate.t(removeIndex(`${colLabel}.${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 ??
4509
- translate.t(`${colLabel}.type_to_search`), onChange: (event) => {
4563
+ }, 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) => {
4510
4564
  onSearchChange(event);
4511
4565
  setOpenSearchResult(true);
4512
- }, autoComplete: "off", ref: ref }), jsx(PopoverTitle, {}), showTotalAndLimit && (jsx(Text, { children: `${enumPickerLabels?.total ?? translate.t(removeIndex(`${colLabel}.total`))}: ${count}, ${enumPickerLabels?.showing ?? translate.t(removeIndex(`${colLabel}.showing`))} ${limit}` })), jsxs(Grid, { overflow: 'auto', maxHeight: '20rem', children: [jsx(Flex, { flexFlow: 'column wrap', children: dataList
4566
+ }, 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
4513
4567
  .filter((item) => {
4514
4568
  const searchTerm = (searchText || '').toLowerCase();
4515
4569
  if (!searchTerm)
@@ -4521,7 +4575,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4521
4575
  // Check if the display value (translation) contains the search text
4522
4576
  const displayValue = !!renderDisplay === true
4523
4577
  ? renderDisplay(item)
4524
- : translate.t(removeIndex(`${colLabel}.${item}`));
4578
+ : formI18n.t(item);
4525
4579
  // Convert to string and check if it includes the search term
4526
4580
  const displayValueString = String(displayValue).toLowerCase();
4527
4581
  const displayValueMatch = displayValueString.includes(searchTerm);
@@ -4541,9 +4595,9 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4541
4595
  setValue(colLabel, [...newSet]);
4542
4596
  }, ...(selected ? { color: 'colorPalette.400/50' } : {}), children: !!renderDisplay === true
4543
4597
  ? renderDisplay(item)
4544
- : translate.t(removeIndex(`${colLabel}.${item}`)) }, `${colLabel}-${item}`));
4598
+ : formI18n.t(item) }, `${colLabel}-${item}`));
4545
4599
  }) }), isDirty && (jsx(Fragment, { children: dataList.length <= 0 && (jsx(Fragment, { children: enumPickerLabels?.emptySearchResult ??
4546
- translate.t(removeIndex(`${colLabel}.empty_search_result`)) })) }))] })] }) })] })] }));
4600
+ formI18n.t('empty_search_result') })) }))] })] }) })] })] }));
4547
4601
  };
4548
4602
 
4549
4603
  function isEnteringWindow(_ref) {
@@ -4849,7 +4903,7 @@ function getText(_ref2) {
4849
4903
  return source.getStringData(textMediaType);
4850
4904
  }
4851
4905
 
4852
- const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }, placeholder = "Drop files here or click to upload", }) => {
4906
+ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }, placeholder = 'Drop files here or click to upload', }) => {
4853
4907
  const ref = useRef(null);
4854
4908
  const [isDraggedOver, setIsDraggedOver] = useState(false);
4855
4909
  useEffect(() => {
@@ -4863,7 +4917,7 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4863
4917
  onDrop: ({ source }) => {
4864
4918
  const files = getFiles({ source });
4865
4919
  const text = getText({ source });
4866
- console.log(files, text, "dfposa");
4920
+ console.log(files, text, 'dfposa');
4867
4921
  onDrop({ files, text });
4868
4922
  },
4869
4923
  });
@@ -4872,9 +4926,9 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4872
4926
  function getColor(isDraggedOver) {
4873
4927
  if (isDraggedOver) {
4874
4928
  return {
4875
- backgroundColor: "blue.400",
4929
+ backgroundColor: 'blue.400',
4876
4930
  _dark: {
4877
- backgroundColor: "blue.400",
4931
+ backgroundColor: 'blue.400',
4878
4932
  },
4879
4933
  };
4880
4934
  }
@@ -4895,7 +4949,7 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
4895
4949
  const filesArray = [...event.target.files];
4896
4950
  onDrop({ files: filesArray });
4897
4951
  };
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 })] }))] }));
4952
+ 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
4953
  };
4900
4954
 
4901
4955
  /**
@@ -4915,6 +4969,7 @@ function formatBytes(bytes) {
4915
4969
  function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, labels, translate, colLabel, }) {
4916
4970
  const [searchTerm, setSearchTerm] = useState('');
4917
4971
  const [selectedFileId, setSelectedFileId] = useState('');
4972
+ const [failedImageIds, setFailedImageIds] = useState(new Set());
4918
4973
  const { data: filesData, isLoading, isError, } = useQuery({
4919
4974
  queryKey: ['file-picker-library', searchTerm],
4920
4975
  queryFn: async () => {
@@ -4941,31 +4996,72 @@ function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = fa
4941
4996
  onClose();
4942
4997
  setSelectedFileId('');
4943
4998
  setSearchTerm('');
4999
+ setFailedImageIds(new Set());
5000
+ };
5001
+ const handleImageError = (fileId) => {
5002
+ setFailedImageIds((prev) => new Set(prev).add(fileId));
4944
5003
  };
4945
5004
  if (!onFetchFiles)
4946
5005
  return null;
4947
5006
  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
5007
  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)',
5008
+ 'Search files...', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), bg: "bg.panel", border: "1px solid", borderColor: "border.default", colorPalette: "blue", _focus: {
5009
+ borderColor: 'colorPalette.500',
5010
+ _dark: {
5011
+ borderColor: 'colorPalette.400',
5012
+ },
5013
+ boxShadow: {
5014
+ base: '0 0 0 1px var(--chakra-colors-blue-500)',
5015
+ _dark: '0 0 0 1px var(--chakra-colors-blue-400)',
5016
+ },
4952
5017
  }, 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
5018
  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 ??
5019
+ 'Loading files...' })] })), isError && (jsx(Box, { bg: { base: 'colorPalette.50', _dark: 'colorPalette.900/20' }, border: "1px solid", borderColor: {
5020
+ base: 'colorPalette.200',
5021
+ _dark: 'colorPalette.800',
5022
+ }, colorPalette: "red", borderRadius: "md", p: 4, children: jsx(Text, { color: {
5023
+ base: 'colorPalette.600',
5024
+ _dark: 'colorPalette.300',
5025
+ }, children: labels?.loadingFailed ??
4955
5026
  translate(removeIndex(`${colLabel}.error.loading_failed`)) ??
4956
5027
  '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
5028
  translate(removeIndex(`${colLabel}.no_files_found`)) ??
4958
5029
  'No files found' }) })) : (jsx(VStack, { align: "stretch", gap: 2, children: filteredFiles.map((file) => {
4959
5030
  const isImage = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name);
4960
5031
  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'
5032
+ const imageFailed = failedImageIds.has(file.id);
5033
+ return (jsx(Box, { p: 3, border: "2px solid", borderColor: isSelected
5034
+ ? {
5035
+ base: 'colorPalette.500',
5036
+ _dark: 'colorPalette.400',
5037
+ }
5038
+ : 'border.default', borderRadius: "md", bg: isSelected
5039
+ ? {
5040
+ base: 'colorPalette.50',
5041
+ _dark: 'colorPalette.900/20',
5042
+ }
5043
+ : 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => setSelectedFileId(file.id), _hover: {
5044
+ borderColor: isSelected
5045
+ ? {
5046
+ base: 'colorPalette.600',
5047
+ _dark: 'colorPalette.400',
5048
+ }
5049
+ : {
5050
+ base: 'colorPalette.300',
5051
+ _dark: 'colorPalette.400',
5052
+ },
5053
+ bg: isSelected
5054
+ ? {
5055
+ base: 'colorPalette.100',
5056
+ _dark: 'colorPalette.800/30',
5057
+ }
5058
+ : 'bg.muted',
5059
+ }, 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
5060
  ? 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));
5061
+ : 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: {
5062
+ base: 'colorPalette.500',
5063
+ _dark: 'colorPalette.400',
5064
+ }, colorPalette: "blue", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsx(Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
4969
5065
  }) })) }))] }) }), 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
5066
  translate(removeIndex(`${colLabel}.cancel`)) ??
4971
5067
  'Cancel' }), jsx(Button$1, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFileId, children: labels?.select ??
@@ -4984,8 +5080,12 @@ const FilePicker = ({ column, schema, prefix }) => {
4984
5080
  : [];
4985
5081
  const colLabel = formI18n.colLabel;
4986
5082
  const [dialogOpen, setDialogOpen] = useState(false);
5083
+ const [failedImageIds, setFailedImageIds] = useState(new Set());
4987
5084
  const { onFetchFiles, enableMediaLibrary = false, filterImageOnly = false, } = filePicker || {};
4988
5085
  const showMediaLibrary = enableMediaLibrary && !!onFetchFiles;
5086
+ const handleImageError = (fileIdentifier) => {
5087
+ setFailedImageIds((prev) => new Set(prev).add(fileIdentifier));
5088
+ };
4989
5089
  const handleMediaLibrarySelect = (fileId) => {
4990
5090
  const newFiles = [...currentFiles, fileId];
4991
5091
  setValue(colLabel, newFiles);
@@ -5049,10 +5149,11 @@ const FilePicker = ({ column, schema, prefix }) => {
5049
5149
  const fileSize = getFileSize(file);
5050
5150
  const isImage = isImageFile(file);
5051
5151
  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',
5152
+ const imageFailed = failedImageIds.has(fileIdentifier);
5153
+ 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: {
5154
+ borderColor: 'colorPalette.300',
5054
5155
  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));
5156
+ }, 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
5157
  }) })] }));
5057
5158
  };
5058
5159
 
@@ -18,6 +18,9 @@ export interface DataTableProps<TData = unknown> {
18
18
  * Data array for the table.
19
19
  *
20
20
  * It will pass into as the data in `@tanstack/react-table`
21
+ * Do not toggle the data array, it will cause the table to re-render in infinite loop.
22
+ *
23
+ * @default []
21
24
  *
22
25
  */
23
26
  data: TData[];
@@ -1,15 +1,16 @@
1
- import { TableControlsProps } from "./controls/TableControls";
2
- import { TableProps } from "./display/Table";
3
- import { TableBodyProps } from "./display/TableBody";
4
- import { TableFooterProps } from "./display/TableFooter";
5
- import { TableHeaderProps } from "./display/TableHeader";
1
+ import { TableControlsProps } from './controls/TableControls';
2
+ import { TableProps } from './display/Table';
3
+ import { TableBodyProps } from './display/TableBody';
4
+ import { TableFooterProps } from './display/TableFooter';
5
+ import { TableHeaderProps } from './display/TableHeader';
6
6
  export interface DefaultTableProps {
7
7
  showFooter?: boolean;
8
- tableProps?: Omit<TableProps, "children">;
8
+ tableProps?: Omit<TableProps, 'children'>;
9
9
  tableHeaderProps?: TableHeaderProps;
10
10
  tableBodyProps?: TableBodyProps;
11
11
  tableFooterProps?: TableFooterProps;
12
12
  controlProps?: TableControlsProps;
13
- variant?: "" | "greedy";
13
+ variant?: '' | 'greedy';
14
+ isLoading?: boolean;
14
15
  }
15
- export declare const DefaultTable: ({ showFooter, tableProps, tableHeaderProps, tableBodyProps, tableFooterProps, controlProps, variant, }: DefaultTableProps) => import("react/jsx-runtime").JSX.Element;
16
+ export declare const DefaultTable: ({ showFooter, tableProps, tableHeaderProps, tableBodyProps, tableFooterProps, controlProps, variant, isLoading, }: DefaultTableProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { DefaultTableProps } from './DefaultTable';
2
+ export interface DefaultTableServerProps extends DefaultTableProps {
3
+ /**
4
+ * Optional isLoading prop to override auto-detected loading state.
5
+ * If not provided, will automatically detect from DataTableServerContext.
6
+ */
7
+ isLoading?: boolean;
8
+ }
9
+ /**
10
+ * DefaultTableServer is a wrapper around DefaultTable that automatically
11
+ * detects server-side loading state from DataTableServerContext.
12
+ *
13
+ * Use this component when working with DataTableServer to automatically
14
+ * show skeleton loading state during data fetching.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * <DataTableServer columns={columns} {...datatableServer}>
19
+ * <DefaultTableServer />
20
+ * </DataTableServer>
21
+ * ```
22
+ */
23
+ export declare const DefaultTableServer: ({ isLoading: isLoadingOverride, ...props }: DefaultTableServerProps) => import("react/jsx-runtime").JSX.Element;
@@ -7,4 +7,4 @@ export interface TableProps extends TableRootProps {
7
7
  canResize?: boolean;
8
8
  children: ReactNode;
9
9
  }
10
- export declare const Table: ({ children, emptyComponent, canResize, ...props }: TableProps) => string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null;
10
+ export declare const Table: ({ children, emptyComponent, canResize, showLoading, ...props }: TableProps) => string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,5 @@
1
+ export interface TableBodySkeletonProps {
2
+ showSelector?: boolean;
3
+ canResize?: boolean;
4
+ }
5
+ export declare const TableBodySkeleton: ({ showSelector, canResize, }: TableBodySkeletonProps) => import("react/jsx-runtime").JSX.Element;
@@ -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.78",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",