diff --git a/package-lock.json b/package-lock.json index e59b7b849ebd92..899f10f8b52b5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15265,37 +15265,6 @@ "resolved": "https://registry.npmjs.org/@tannin/postfix/-/postfix-1.1.0.tgz", "integrity": "sha512-oocsqY7g0cR+Gur5jRQLSrX2OtpMLMse1I10JQBm8CdGMrDkh1Mg2gjsiquMHRtBs4Qwu5wgEp5GgIYHk4SNPw==" }, - "node_modules/@tanstack/react-table": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.10.3.tgz", - "integrity": "sha512-Qya1cJ+91arAlW7IRDWksRDnYw28O446jJ/ljkRSc663EaftJoBCAU10M+VV1K6MpCBLrXq1BD5IQc1zj/ZEjA==", - "dependencies": { - "@tanstack/table-core": "8.10.3" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" - } - }, - "node_modules/@tanstack/table-core": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.10.3.tgz", - "integrity": "sha512-hJ55YfJlWbfzRROfcyA/kC1aZr/shsLA8XNAwN8jXylhYWGLnPmiJJISrUfj4dMMWRiFi0xBlnlC7MLH+zSrcw==", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, "node_modules/@testing-library/dom": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", @@ -55059,7 +55028,6 @@ "license": "GPL-2.0-or-later", "dependencies": { "@babel/runtime": "^7.16.0", - "@tanstack/react-table": "^8.10.3", "@wordpress/a11y": "file:../a11y", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", @@ -67561,19 +67529,6 @@ "resolved": "https://registry.npmjs.org/@tannin/postfix/-/postfix-1.1.0.tgz", "integrity": "sha512-oocsqY7g0cR+Gur5jRQLSrX2OtpMLMse1I10JQBm8CdGMrDkh1Mg2gjsiquMHRtBs4Qwu5wgEp5GgIYHk4SNPw==" }, - "@tanstack/react-table": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.10.3.tgz", - "integrity": "sha512-Qya1cJ+91arAlW7IRDWksRDnYw28O446jJ/ljkRSc663EaftJoBCAU10M+VV1K6MpCBLrXq1BD5IQc1zj/ZEjA==", - "requires": { - "@tanstack/table-core": "8.10.3" - } - }, - "@tanstack/table-core": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.10.3.tgz", - "integrity": "sha512-hJ55YfJlWbfzRROfcyA/kC1aZr/shsLA8XNAwN8jXylhYWGLnPmiJJISrUfj4dMMWRiFi0xBlnlC7MLH+zSrcw==" - }, "@testing-library/dom": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", @@ -70418,7 +70373,6 @@ "version": "file:packages/dataviews", "requires": { "@babel/runtime": "^7.16.0", - "@tanstack/react-table": "^8.10.3", "@wordpress/a11y": "file:../a11y", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index 3d15ea435ab4f8..1872480d759c37 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -28,7 +28,6 @@ "sideEffects": false, "dependencies": { "@babel/runtime": "^7.16.0", - "@tanstack/react-table": "^8.10.3", "@wordpress/a11y": "file:../a11y", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", diff --git a/packages/dataviews/src/view-table.js b/packages/dataviews/src/view-table.js index 209b9e443dc2a2..e34d99008657bc 100644 --- a/packages/dataviews/src/view-table.js +++ b/packages/dataviews/src/view-table.js @@ -1,15 +1,3 @@ -/** - * External dependencies - */ -import { - getCoreRowModel, - getFilteredRowModel, - getSortedRowModel, - getPaginationRowModel, - useReactTable, - flexRender, -} from '@tanstack/react-table'; - /** * WordPress dependencies */ @@ -30,7 +18,7 @@ import { Icon, privateApis as componentsPrivateApis, } from '@wordpress/components'; -import { useMemo, Children, Fragment } from '@wordpress/element'; +import { Children, Fragment } from '@wordpress/element'; /** * Internal dependencies @@ -48,32 +36,23 @@ const { DropdownSubMenuTriggerV2: DropdownSubMenuTrigger, } = unlock( componentsPrivateApis ); -const EMPTY_OBJECT = {}; const sortingItemsInfo = { asc: { icon: arrowUp, label: __( 'Sort ascending' ) }, desc: { icon: arrowDown, label: __( 'Sort descending' ) }, }; const sortIcons = { asc: chevronUp, desc: chevronDown }; -function HeaderMenu( { dataView, header } ) { - if ( header.isPlaceholder ) { - return null; - } - const text = flexRender( - header.column.columnDef.header, - header.getContext() - ); - const isSortable = !! header.column.getCanSort(); - const isHidable = !! header.column.getCanHide(); +function HeaderMenu( { field, view, onChangeView } ) { + const isSortable = field.enableSorting !== false; + const isHidable = field.enableHiding !== false; if ( ! isSortable && ! isHidable ) { - return text; + return field.header; } - const sortedDirection = header.column.getIsSorted(); - + const isSorted = view.sort?.field === field.id; let filter, filterInView; const otherFilters = []; - if ( header.column.columnDef.type === ENUMERATION_TYPE ) { - let columnOperators = header.column.columnDef.filterBy?.operators; + if ( field.type === ENUMERATION_TYPE ) { + let columnOperators = field.filterBy?.operators; if ( ! columnOperators || ! Array.isArray( columnOperators ) ) { columnOperators = [ OPERATOR_IN, OPERATOR_NOT_IN ]; } @@ -82,9 +61,9 @@ function HeaderMenu( { dataView, header } ) { ); if ( operators.length >= 0 ) { filter = { - field: header.column.columnDef.id, + field: field.id, operators, - elements: header.column.columnDef.elements || [], + elements: field.elements || [], }; filterInView = { field: filter.field, @@ -96,31 +75,25 @@ function HeaderMenu( { dataView, header } ) { const isFilterable = !! filter; if ( isFilterable ) { - const columnFilters = dataView.getState().columnFilters; + const columnFilters = view.filters; columnFilters.forEach( ( columnFilter ) => { - const [ field, operator ] = - Object.keys( columnFilter )[ 0 ].split( ':' ); - const value = Object.values( columnFilter )[ 0 ]; - if ( field === filter.field ) { + if ( columnFilter.field === filter.field ) { filterInView = { - field, - operator, - value, + ...columnFilter, }; } else { otherFilters.push( columnFilter ); } } ); } - return ( @@ -130,47 +103,61 @@ function HeaderMenu( { dataView, header } ) { { isSortable && ( { Object.entries( sortingItemsInfo ).map( - ( [ direction, info ] ) => ( - } - suffix={ - sortedDirection === direction && ( - - ) - } - onSelect={ ( event ) => { - event.preventDefault(); - if ( sortedDirection === direction ) { - dataView.resetSorting(); - } else { - dataView.setSorting( [ - { - id: header.column.id, - desc: direction === 'desc', - }, - ] ); + ( [ direction, info ] ) => { + const isActive = + isSorted && + view.sort.direction === direction; + return ( + } + suffix={ + isActive && } - } } - > - { info.label } - - ) + onSelect={ ( event ) => { + event.preventDefault(); + if ( + isSorted && + view.sort.direction === + direction + ) { + onChangeView( { + ...view, + sort: undefined, + } ); + } else { + onChangeView( { + ...view, + sort: { + field: field.id, + direction, + }, + } ); + } + } } + > + { info.label } + + ); + } ) } ) } { isHidable && ( } onSelect={ ( event ) => { event.preventDefault(); - header.column.getToggleVisibilityHandler()( event ); + onChangeView( { + ...view, + hiddenFields: view.hiddenFields.concat( + field.id + ), + } ); } } > { __( 'Hide' ) } @@ -216,17 +203,20 @@ function HeaderMenu( { dataView, header } ) { ) } onSelect={ () => { - dataView.setColumnFilters( [ - ...otherFilters, - { - [ filter.field + - ':' + - filterInView?.operator ]: - isActive + onChangeView( { + ...view, + filters: [ + ...otherFilters, + { + field: filter.field, + operator: + filterInView?.operator, + value: isActive ? undefined : element.value, - }, - ] ); + }, + ], + } ); } } > { element.label } @@ -270,15 +260,18 @@ function HeaderMenu( { dataView, header } ) { ) } onSelect={ () => - dataView.setColumnFilters( [ - ...otherFilters, - { - [ filter.field + - ':' + - OPERATOR_IN ]: - filterInView?.value, - }, - ] ) + onChangeView( { + ...view, + filters: [ + ...otherFilters, + { + field: filter.field, + operator: + OPERATOR_IN, + value: filterInView?.value, + }, + ], + } ) } > { __( 'Is' ) } @@ -297,15 +290,18 @@ function HeaderMenu( { dataView, header } ) { ) } onSelect={ () => - dataView.setColumnFilters( [ - ...otherFilters, - { - [ filter.field + - ':' + - OPERATOR_NOT_IN ]: - filterInView?.value, - }, - ] ) + onChangeView( { + ...view, + filters: [ + ...otherFilters, + { + field: filter.field, + operator: + OPERATOR_NOT_IN, + value: filterInView?.value, + }, + ], + } ) } > { __( 'Is not' ) } @@ -340,208 +336,18 @@ function ViewTable( { data, getItemId, isLoading = false, - paginationInfo, deferredRendering, } ) { - const columns = useMemo( () => { - const _columns = fields.map( ( field ) => { - const { render, getValue, ...column } = field; - column.cell = ( props ) => render( { item: props.row.original } ); - if ( getValue ) { - column.accessorFn = ( item ) => getValue( { item } ); - } - return column; - } ); - if ( actions?.length ) { - _columns.push( { - header: __( 'Actions' ), - id: 'actions', - cell: ( props ) => { - return ( - - ); - }, - enableHiding: false, - } ); - } - - return _columns; - }, [ fields, actions ] ); - - const columnVisibility = useMemo( () => { - if ( ! view.hiddenFields?.length ) { - return; - } - return view.hiddenFields.reduce( - ( accumulator, fieldId ) => ( { - ...accumulator, - [ fieldId ]: false, - } ), - {} - ); - }, [ view.hiddenFields ] ); - - /** - * Transform the filters from the view format into the tanstack columns filter format. - * - * Input: - * - * view.filters = [ - * { field: 'date', operator: 'before', value: '2020-01-01' }, - * { field: 'date', operator: 'after', value: '2020-01-01' }, - * ] - * - * Output: - * - * columnFilters = [ - * { "date:before": '2020-01-01' }, - * { "date:after": '2020-01-01' } - * ] - * - * @param {Array} filters The view filters to transform. - * @return {Array} The transformed TanStack column filters. - */ - const toTanStackColumnFilters = ( filters ) => - filters?.map( ( filter ) => ( { - [ filter.field + ':' + filter.operator ]: filter.value, - } ) ); - - /** - * Transform the filters from the view format into the tanstack columns filter format. - * - * Input: - * - * columnFilters = [ - * { "date:before": '2020-01-01'}, - * { "date:after": '2020-01-01' } - * ] - * - * Output: - * - * view.filters = [ - * { field: 'date', operator: 'before', value: '2020-01-01' }, - * { field: 'date', operator: 'after', value: '2020-01-01' }, - * ] - * - * @param {Array} filters The TanStack column filters to transform. - * @return {Array} The transformed view filters. - */ - const fromTanStackColumnFilters = ( filters ) => - filters.map( ( filter ) => { - const [ key, value ] = Object.entries( filter )[ 0 ]; - const [ field, operator ] = key.split( ':' ); - return { field, operator, value }; - } ); - + const visibleFields = fields.filter( + ( field ) => + ! view.hiddenFields.includes( field.id ) && + ! [ view.layout.mediaField, view.layout.primaryField ].includes( + field.id + ) + ); const shownData = useAsyncList( data ); const usedData = deferredRendering ? shownData : data; - const dataView = useReactTable( { - data: usedData, - columns, - manualSorting: true, - manualFiltering: true, - manualPagination: true, - enableRowSelection: true, - state: { - sorting: view.sort - ? [ - { - id: view.sort.field, - desc: view.sort.direction === 'desc', - }, - ] - : [], - globalFilter: view.search, - columnFilters: toTanStackColumnFilters( view.filters ), - pagination: { - pageIndex: view.page, - pageSize: view.perPage, - }, - columnVisibility: columnVisibility ?? EMPTY_OBJECT, - }, - getRowId: getItemId, - onSortingChange: ( sortingUpdater ) => { - onChangeView( ( currentView ) => { - const sort = - typeof sortingUpdater === 'function' - ? sortingUpdater( - currentView.sort - ? [ - { - id: currentView.sort.field, - desc: - currentView.sort - .direction === 'desc', - }, - ] - : [] - ) - : sortingUpdater; - if ( ! sort.length ) { - return { - ...currentView, - sort: {}, - }; - } - const [ { id, desc } ] = sort; - return { - ...currentView, - sort: { field: id, direction: desc ? 'desc' : 'asc' }, - }; - } ); - }, - onColumnVisibilityChange: ( columnVisibilityUpdater ) => { - onChangeView( ( currentView ) => { - const hiddenFields = Object.entries( - columnVisibilityUpdater() - ).reduce( - ( accumulator, [ fieldId, value ] ) => { - if ( value ) { - return accumulator.filter( - ( id ) => id !== fieldId - ); - } - return [ ...accumulator, fieldId ]; - }, - [ ...( currentView.hiddenFields || [] ) ] - ); - return { - ...currentView, - hiddenFields, - }; - } ); - }, - onGlobalFilterChange: ( value ) => { - onChangeView( { ...view, search: value, page: 1 } ); - }, - onColumnFiltersChange: ( columnFiltersUpdater ) => { - onChangeView( { - ...view, - filters: fromTanStackColumnFilters( columnFiltersUpdater() ), - page: 1, - } ); - }, - onPaginationChange: ( paginationUpdater ) => { - onChangeView( ( currentView ) => { - const { pageIndex, pageSize } = paginationUpdater( { - pageIndex: currentView.page, - pageSize: currentView.perPage, - } ); - return { ...view, page: pageIndex, perPage: pageSize }; - } ); - }, - getCoreRowModel: getCoreRowModel(), - getFilteredRowModel: getFilteredRowModel(), - getSortedRowModel: getSortedRowModel(), - getPaginationRowModel: getPaginationRowModel(), - pageCount: paginationInfo.totalPages, - } ); - - const { rows } = dataView.getRowModel(); - const hasRows = !! rows?.length; + const hasData = !! usedData?.length; if ( isLoading ) { // TODO:Add spinner or progress bar.. return ( @@ -550,77 +356,75 @@ function ViewTable( { ); } - const sortValues = { asc: 'ascending', desc: 'descending' }; - return (
- { hasRows && ( + { hasData && ( - { dataView.getHeaderGroups().map( ( headerGroup ) => ( - - { headerGroup.headers.map( ( header ) => ( - - ) ) } - - ) ) } + + { visibleFields.map( ( field ) => ( + + ) ) } + { !! actions?.length && ( + + ) } + - { rows.map( ( row ) => ( - - { row.getVisibleCells().map( ( cell ) => ( + { usedData.map( ( item, index ) => ( + + { visibleFields.map( ( field ) => ( ) ) } + { !! actions?.length && ( + + ) } ) ) }
- -
+ + + { __( 'Actions' ) } +
- { flexRender( - cell.column.columnDef.cell, - cell.getContext() - ) } + { field.render( { + item, + } ) } + +
) } - { ! hasRows && ( + { ! hasData && (

{ __( 'No results' ) }