Viewing File: /home/markqprx/iniasli.pro/client/data-table.tsx

import React, {
  cloneElement,
  ComponentProps,
  ReactElement,
  ReactNode,
  useState,
} from 'react';
import {TableDataItem} from '../ui/tables/types/table-data-item';
import {BackendFilter} from './filters/backend-filter';
import {MessageDescriptor} from '../i18n/message-descriptor';
import {ColumnConfig} from './column-config';
import {useTrans} from '../i18n/use-trans';
import {useBackendFilterUrlParams} from './filters/backend-filter-url-params';
import {
  GetDatatableDataParams,
  useDatatableData,
} from './requests/paginated-resources';
import {DataTableContext} from './page/data-table-context';
import {AnimatePresence, m} from 'framer-motion';
import {ProgressBar} from '../ui/progress/progress-bar';
import {Table, TableProps} from '../ui/tables/table';
import {DataTablePaginationFooter} from './data-table-pagination-footer';
import {DataTableHeader} from './data-table-header';
import {FilterList} from './filters/filter-list/filter-list';
import {SelectedStateDatatableHeader} from '@common/datatable/selected-state-datatable-header';
import clsx from 'clsx';
import {useIsMobileMediaQuery} from '@common/utils/hooks/is-mobile-media-query';
import {BackendFiltersUrlKey} from '@common/datatable/filters/backend-filters-url-key';
import {opacityAnimation} from '@common/ui/animation/opacity-animation';
import {FilterListSkeleton} from '@common/datatable/filters/filter-list/filter-list-skeleton';

export interface DataTableProps<T extends TableDataItem> {
  filters?: BackendFilter[];
  filtersLoading?: boolean;
  columns: ColumnConfig<T>[];
  searchPlaceholder?: MessageDescriptor;
  queryParams?: Record<string, string | number | undefined | null>;
  endpoint: string;
  resourceName?: ReactNode;
  emptyStateMessage: ReactElement<{isFiltering: boolean}>;
  actions?: ReactNode;
  enableSelection?: boolean;
  selectionStyle?: TableProps<T>['selectionStyle'];
  selectedActions?: ReactNode;
  onRowAction?: TableProps<T>['onAction'];
  tableDomProps?: ComponentProps<'table'>;
  children?: ReactNode;
  collapseTableOnMobile?: boolean;
  cellHeight?: string;
}
export function DataTable<T extends TableDataItem>({
  filters,
  filtersLoading,
  columns,
  searchPlaceholder,
  queryParams,
  endpoint,
  actions,
  selectedActions,
  emptyStateMessage,
  tableDomProps,
  onRowAction,
  enableSelection = true,
  selectionStyle = 'checkbox',
  children,
  cellHeight,
  collapseTableOnMobile = true,
}: DataTableProps<T>) {
  const isMobile = useIsMobileMediaQuery();
  const {trans} = useTrans();
  const {encodedFilters} = useBackendFilterUrlParams(filters);
  const [params, setParams] = useState<GetDatatableDataParams>({perPage: 15});
  const [selectedRows, setSelectedRows] = useState<(string | number)[]>([]);
  const query = useDatatableData<T>(
    endpoint,
    {
      ...params,
      ...queryParams,
      [BackendFiltersUrlKey]: encodedFilters,
    },
    undefined,
    () => setSelectedRows([]),
  );

  const isFiltering = !!(params.query || params.filters || encodedFilters);
  const pagination = query.data?.pagination;

  return (
    <DataTableContext.Provider
      value={{
        selectedRows,
        setSelectedRows,
        endpoint,
        params,
        setParams,
        query,
      }}
    >
      {children}
      <AnimatePresence initial={false} mode="wait">
        {selectedRows.length ? (
          <SelectedStateDatatableHeader
            selectedItemsCount={selectedRows.length}
            actions={selectedActions}
            key="selected"
          />
        ) : (
          <DataTableHeader
            searchPlaceholder={searchPlaceholder}
            searchValue={params.query}
            onSearchChange={query => setParams({...params, query})}
            actions={actions}
            filters={filters}
            filtersLoading={filtersLoading}
            key="default"
          />
        )}
      </AnimatePresence>

      {filters && (
        <div className="mb-14">
          <AnimatePresence initial={false} mode="wait">
            {filtersLoading && encodedFilters ? (
              <FilterListSkeleton />
            ) : (
              <m.div key="filter-list" {...opacityAnimation}>
                <FilterList filters={filters} />
              </m.div>
            )}
          </AnimatePresence>
        </div>
      )}

      <div
        className={clsx(
          'relative rounded-panel',
          (!isMobile || !collapseTableOnMobile) && 'border',
        )}
      >
        {query.isFetching && (
          <ProgressBar
            isIndeterminate
            className="absolute left-0 top-0 z-10 w-full"
            aria-label={trans({message: 'Loading'})}
            size="xs"
          />
        )}

        <div className="relative overflow-x-auto md:overflow-hidden">
          <Table
            {...tableDomProps}
            columns={columns}
            data={pagination?.data || []}
            sortDescriptor={params}
            onSortChange={descriptor => {
              setParams({...params, ...descriptor});
            }}
            selectedRows={selectedRows}
            enableSelection={enableSelection}
            selectionStyle={selectionStyle}
            onSelectionChange={setSelectedRows}
            onAction={onRowAction}
            collapseOnMobile={collapseTableOnMobile}
            cellHeight={cellHeight}
          />
        </div>

        {(query.isFetched || query.isPlaceholderData) &&
        !pagination?.data.length ? (
          <div className="pt-50">
            {cloneElement(emptyStateMessage, {
              isFiltering,
            })}
          </div>
        ) : undefined}

        <DataTablePaginationFooter
          query={query}
          onPageChange={page => setParams({...params, page})}
          onPerPageChange={perPage => setParams({...params, perPage})}
        />
      </div>
    </DataTableContext.Provider>
  );
}
Back to Directory File Manager