@bsol-oss/react-datatable5 12.0.0-beta.77 → 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) {
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
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';
@@ -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) {
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsol-oss/react-datatable5",
3
- "version": "12.0.0-beta.77",
3
+ "version": "12.0.0-beta.78",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",