import { Table, TableColumn as WSRTableColumn } from '@wix/design-system'; import React, { ReactElement } from 'react'; import { observer } from 'mobx-react-lite'; import { FiltersMap, KeyedItem } from '@wix/bex-core'; import { CollectionCard } from '../CollectionCard'; import { TableColumn } from '../../model'; import { TableState } from '../../state'; import { useTableCollectionSyncProps } from './useTableCollectionSyncProps'; import type { TableDragAndDropType } from '../TableDragAndDrop'; import { CollectionTableWSRTable, CollectionTableWSRTableBaseProps, } from './CollectionTableWSRTable'; import { CollectionTableContent } from './CollectionTableContent'; import { PageInfiniteScrollLoader } from '../PageInfiniteScrollLoader'; import { useActionCellColumn } from '../ActionCell'; import { BulkActionModalRenderProp, BulkActionToolbarRenderProp, SingleCollectionBulkActionModal, SingleCollectionBulkActionToolbar, } from '../BulkAction'; import { CollectionTableActionCellProps } from './CollectionTableBaseCommonProps'; import { type DataExtensionElement, type DataExtensionType, } from '../DataExtension'; import type { FieldsSourceProvider } from '../fieldsSourceProvider'; import { useCreateWSRColumnBase } from '../../hooks/useCreateWSRColumnBase'; import type { TagsElement } from '../Tags'; import { TablePlaceholderStates } from '../TablePlaceholderStates/TablePlaceholderStates'; import { useScrollableContentContext } from '../../providers'; import { st, classes } from './CollectionTable.st.css.js'; import { useIsMobile } from '../../hooks/useIsMobile'; import { useCollectionTableResize } from '../../hooks/useCollectionTableResize'; import { SearchableCollectionContent } from '../SearchableCollectionContent/SearchableCollectionContent'; export interface CollectionTableBaseProps extends Omit, 'resizable'>, CollectionTableActionCellProps { /** * Defines the columns of the table. Accepts an array of [`TableColumn`](./?path=/story/common-types--tablecolumn) objects. * @overrideType [TableColumn](./?path=/story/common-types--tablecolumn)[] * @external */ columns: TableColumn[]; /** * Whether to show a checkbox column for selecting items. * @default false * @external */ showSelection?: boolean; /** * A callback method to be called on row click. Signature: `onRowClick(rowData, rowNum)`. To enable hover effect you should set this prop. * @external */ onRowClick?: (item: T, index: number) => void; /** * A [``](./?path=/story/features-export--exportto) react element to be added to the toolbar. */ exportButton?: ReactElement; /** * Adds functionality to allow visitors to reorder items manually. * For more information, see the [Drag and Drop Overview](./?path=/story/features-sort-drag-and-drop--overview). * @overrideType [TableDragAndDrop](./?path=/story/features-sort-drag-and-drop--tabledraganddrop) * @external */ dragAndDrop?: TableDragAndDropType; /** * Data extension implementation: `import { DataExtension } from '@wix/patterns';` */ dataExtension?: DataExtensionType | DataExtensionElement; /** * Attaches a field source to the table — the way to add a source's fields, * filters, and field management. Build it with `tableSchemaSource(...)` for a * schema source (also spread its `collectionConfig` into the collection hook), * or `dataExtensionTableSource(...)` for DataExtension, which extends the * consumer's own collection. */ /** A fields source (e.g. `dataExtensionTableSource(...)`) whose fields become * columns and whose field management wires into the table. Schema sources * attach via the `@wix/patterns/schema` collection hooks instead. */ fieldsSource?: FieldsSourceProvider; tags?: TagsElement; forceResize?: boolean; /** * A function that receives the item of the row and returns a string for tooltip content. * @external */ checkboxTooltipContent?: (rowData: KeyedItem) => string | undefined; /** * Render a custom toolbar when more than 1 item is selected.
* The function accepts the following parameters * * `selectedValues`: The items that were checked via the selection column * * `uncheckedValues`: The items that were unchecked via the selection column * * `allSelected`: Indicates whether the "Select All" checkbox is checked * * `openModal`: Opens the modal that contains the component from `bulkActionModal` render prop * * `clearSelection`: Clears all existing selection, call this after the action on the selected items was done * * `total`: Total items in the server (taken from [`fetchData`](./?path=/story/common-hooks--usecollection) return value). * * `openConfirmModal`: Opens a `` to confirm an action about to be applied on the selected items. * * `query`: ComputedQuery - Representation of the current paging, sorting & filtering applied. Usually for using together with bulk action such as `updateAll` and `deleteAll` * * > When passed, enables `showSelection` by default. * @external */ bulkActionToolbar?: BulkActionToolbarRenderProp; /** * When true, wraps every column header with the AddField hover popover. * The "+" button is revealed when the consumer hovers a column header and * triggers either the schema's `addField` or the data-extension's custom * fields modal. Off by default. * @external */ showAddFieldButton?: boolean; /** * Opens a modal from the [`MultiBulkActionToolbar`](./?path=/story/features-actions-bulk-actions--multibulkactiontoolbar). * Supported parameters: * + `selectedValues`: Selected item values. * + `uncheckedValues`: Unselected item values. * + `isSelectAll`: [bool] Whether the **Select All** option is selected. * + `closeModal`: Closes the modal. * + `query`: [object] Instance of [ComputedQuery](./?path=/story/common-types--computedquery) that represents the query that resulted in an empty state. Representation of the current paging, sorting and filtering applied. Usually for using together with bulk action such as `updateAll` and `deleteAll`. * @external */ bulkActionModal?: BulkActionModalRenderProp; } export interface CollectionTableProps extends CollectionTableBaseProps { state: TableState; keyedItems?: KeyedItem[]; isUsingExpandCollapse?: boolean; } function _CollectionTable( props: CollectionTableProps, ) { const { state: tableState, filters, title, tabs, multiLevelSorting: multiLevelSortingProp, dataExtension, tags, views, extraToolbar, bulkActionToolbar, exportButton, exportModal, importModal, bulkActionModal, sticky: stickyProp, actionCell, actionCellWidth, actionCellProps, columns: columnsProp, onRowClick, onSortClick, showSelection: showSelectionProp, search, renderError, errorState = renderError && (((err, { isOnline, retry }) => renderError?.({ err, isOnline, retry, })) as CollectionTableProps['errorState']), emptyState, noResultsState, children, onSelectedItems, cardProps, minCardHeight, dataHook, topNotification, tableGridSwitchButton, useShowLoaderWhenEmpty, useNewInfiniteScrollLoader, dragAndDrop, dragAndDropBulkSubmit, dragAndDropSubmit, dragAndDropCancel, dragAndDropReorderModeToolbar, horizontalScroll: horizontalScrollProp, stickyColumns, stickySelectionColumn, hideBulkSelectionCheckbox, maxSelection, showTitleBar = !tableState.toolbar.container.isPanelLayout, BodyElementType, isApplyColumnWidthStyle, rowVerticalPadding, keyedItems, resizeProps, forceResize, showAddFieldButton, ...rest } = props; const state = tableState.toolbar; const horizontalScroll = horizontalScrollProp ?? state.container.internalExperiments?.enabled( 'specs.cairo.HorizontalScrollAsDefault', ); const { scrollableContentRef } = useScrollableContentContext(); const sticky = stickyProp ?? scrollableContentRef?.current != null; const customColumns = props.customColumns; const isMobile = useIsMobile(); const showSelection = isMobile ? bulkActionToolbar != null : showSelectionProp ?? bulkActionToolbar != null; const isVirtualized = !!BodyElementType; useTableCollectionSyncProps({ ...props, dataExtension, showSelection, customColumns, tags, isUsingTableVirtualization: isVirtualized, }); const { collection, internalScroll } = state; const { selectedOrderedColumnsOrAll } = state; const { bulkSelect, query } = collection; const { isResizable, tableResizeProps } = useCollectionTableResize({ columns: columnsProp, userResizeProps: resizeProps, horizontalScroll, columnSizesState: state.columnSizesState, isVirtualized, forceResize, }); const createWSRColumnBase = useCreateWSRColumnBase({ horizontalScroll, showFieldTypeIcons: state.showFieldTypeIcons, showAddFieldButton, }); const transformedColumns: WSRTableColumn>[] = selectedOrderedColumnsOrAll.map((column, index, { length }) => { const { render, id, sortable, infoTooltipProps, title, width, style, ...restColumn } = column; const columnBase = createWSRColumnBase(column, index, length); const resizeProps = isResizable ? { id: column.id } : undefined; return { ...restColumn, ...columnBase, resizeProps, ...(isApplyColumnWidthStyle ? undefined : { style, width }), sortDescending: sortable && id ? query.sort.getIsSortDescending(id) : undefined, render: (keyedItem: KeyedItem, rowNum: number) => { const renderedContent = render(keyedItem.item, rowNum); return column.searchable ? ( {renderedContent} ) : ( renderedContent ); }, }; }); const actionCellColumn = useActionCellColumn({ actionCellProps: { ...(actionCellProps ?? {}), stickyActionCell: horizontalScroll || actionCellProps?.stickyActionCell, }, actionCellWidth, collection, sticky, actionCell, }); const columnsWithActions: WSRTableColumn>[] = actionCellColumn ? [...transformedColumns, actionCellColumn] : transformedColumns; const internalOnSortClick = (col: TableColumn, index: number) => { onSortClick?.(col, index); if (col.id != null) { state.collection.sort( { id: col.id, sortMode: col.sortMode }, { clearResult: true, multiple: multiLevelSortingProp !== undefined, }, ); } }; const { st: tableRowSt, classes: tableRowClasses } = Table.dataTableRowStyle; let table = ( topNotification({ query: collection.result.originQuery }) : topNotification } customColumns={customColumns} rowVerticalPadding={isMobile ? 'medium' : rowVerticalPadding} dragAndDropReorderModeToolbar={dragAndDropReorderModeToolbar} selectionToolbar={ bulkActionToolbar && ( ) } dynamicRowClass={() => tableRowSt(tableRowClasses.root, { skin: 'light', }) } horizontalScroll={horizontalScroll} resizable={isResizable} resizeProps={tableResizeProps} minCardHeight={minCardHeight} sticky={sticky} showTitleBar={showTitleBar} state={tableState} columns={columnsWithActions} onSortClick={internalOnSortClick} showSelection={showSelection} onSelectionChanged={(_selectedIds, _change) => { state.onSelectionChanged(_selectedIds, _change); onSelectedItems?.(bulkSelect.allSelected, bulkSelect.selectedValues); rest.onSelectionChanged?.(_selectedIds, _change); }} selectionDisabled={(row) => state.bulk.isRowCheckboxDisabled(row)} onRowClick={onRowClick && (({ item }, index) => onRowClick(item, index))} isApplyColumnWidthStyle={isApplyColumnWidthStyle} keyedItems={keyedItems} > { const { result: { originQuery, hasAvailableItems }, } = collection; return noResultsState({ hasAvailableItems, query: originQuery, }); } : noResultsState } /> {useNewInfiniteScrollLoader || internalScroll ? ( ) : undefined} {children} ); if (dragAndDrop) { const { TableDragAndDropContext } = dragAndDrop; table = ( {table} ); } return ( {exportModal ?? state.featuredComponents.exportModal.value} {importModal ?? state.featuredComponents.importModal.value} {bulkActionModal && ( )} {table} ); } export const CollectionTable = observer(_CollectionTable);