Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table data object data type #826

Draft
wants to merge 4 commits into
base: 1.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions assets/js/src/core/app/config/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ import { DynamicTypeObjectDataGeoPolygon } from '@Pimcore/modules/element/dynami
import { DynamicTypeObjectDataGeoPolyLine } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-geopolyline'
import { DynamicTypeObjectDataManyToManyRelation } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-many-to-many-relation'
import { DynamicTypeObjectDataStructuredTable } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-structured-table'
import { DynamicTypeObjectDataTable } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-table'
import { DynamicTypeObjectDataBlock } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-block'
import { DynamicTypeObjectDataLocalizedFields } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-localized-fields'
import { DynamicTypeGridCellAsset } from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/types/asset/dynamic-type-grid-cell-asset'
Expand Down Expand Up @@ -259,6 +260,7 @@ container.bind(serviceIds['DynamicTypes/ObjectData/GeoBounds']).to(DynamicTypeOb
container.bind(serviceIds['DynamicTypes/ObjectData/GeoPolygon']).to(DynamicTypeObjectDataGeoPolygon).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/GeoPolyLine']).to(DynamicTypeObjectDataGeoPolyLine).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/ManyToManyRelation']).to(DynamicTypeObjectDataManyToManyRelation).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/Table']).to(DynamicTypeObjectDataTable).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/StructuredTable']).to(DynamicTypeObjectDataStructuredTable).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/Block']).to(DynamicTypeObjectDataBlock).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/LocalizedFields']).to(DynamicTypeObjectDataLocalizedFields).inSingletonScope()
Expand Down
1 change: 1 addition & 0 deletions assets/js/src/core/app/config/services/service-ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export const serviceIds = {
'DynamicTypes/ObjectData/GeoPolygon': 'DynamicTypes/ObjectData/GeoPolygon',
'DynamicTypes/ObjectData/GeoPolyLine': 'DynamicTypes/ObjectData/GeoPolyLine',
'DynamicTypes/ObjectData/ManyToManyRelation': 'DynamicTypes/ObjectData/ManyToManyRelation',
'DynamicTypes/ObjectData/Table': 'DynamicTypes/ObjectData/Table',
'DynamicTypes/ObjectData/StructuredTable': 'DynamicTypes/ObjectData/StructuredTable',
'DynamicTypes/ObjectData/Block': 'DynamicTypes/ObjectData/Block',
'DynamicTypes/ObjectData/LocalizedFields': 'DynamicTypes/ObjectData/LocalizedFields',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ export const useStyle = createStyles(({ token, css }) => {
display: flex;
width: 100%;
height: 100%;


&.default-cell--active:not(:focus):not(.default-cell--edit-mode) {
background-color: ${token.controlItemBgActive};
}

&:focus {
outline: 1px solid ${token.colorPrimaryActive};
outline-offset: -1px;
Expand Down
11 changes: 10 additions & 1 deletion assets/js/src/core/components/grid/columns/default-cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ export const DefaultCell = ({ ...props }: DefaultCellProps): React.JSX.Element =
// @todo move to new dynamic type system
// onCopy={ onCopy }
onDoubleClick={ onDoubleClick }
onFocus={ () => props.onFocus?.({
rowIndex: row.index,
columnIndex: column.getIndex(),
columnId: column.id
}) }
onKeyDown={ onKeyDown }
// @todo move to new dynamic type system
// onPaste={ onPaste }
Expand All @@ -81,11 +86,15 @@ export const DefaultCell = ({ ...props }: DefaultCellProps): React.JSX.Element =
</EditableCellContextProvider>
</div>
)
}, [isInEditMode, props.getValue(), row, row.getIsSelected(), isEditable])
}, [isInEditMode, props.getValue(), row, row.getIsSelected(), isEditable, props.active])

function getCssClasses (): string[] {
const classes: string[] = []

if (props.active === true) {
classes.push('default-cell--active')
}

if (props.modified === true) {
classes.push('default-cell--modified')
}
Expand Down
25 changes: 17 additions & 8 deletions assets/js/src/core/components/grid/grid-cell/grid-cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,37 @@

import { type Cell, type CellContext, flexRender } from '@tanstack/react-table'
import React from 'react'
import { type ExtendedCellContext } from '../grid'
import { type GridCellReference, type ExtendedCellContext } from '../grid'
import { type GridContextProviderProps, GridContextProvider } from '../grid-context'
import {
DynamicTypeRegistryProvider
} from '@Pimcore/modules/element/dynamic-types/registry/provider/dynamic-type-registry-provider'

export interface GridCellProps {
cell: Cell<any, unknown>
isActive?: boolean
isModified?: boolean
onFocusCell?: (cell: GridCellReference) => void
tableElement: GridContextProviderProps['table']
}

export const GridCell = ({ cell, isModified, tableElement }: GridCellProps): React.JSX.Element => {
export const GridCell = ({ cell, isModified, isActive, onFocusCell, tableElement }: GridCellProps): React.JSX.Element => {
return (
<GridContextProvider table={ tableElement }>
<div className='grid__cell-content'>
{flexRender(cell.column.columnDef.cell, getExtendedCellContext(cell.getContext()))}
</div>
</GridContextProvider>
<DynamicTypeRegistryProvider serviceIds={ ['DynamicTypes/GridCellRegistry'] }>
<GridContextProvider table={ tableElement }>
<div className='grid__cell-content'>
{flexRender(cell.column.columnDef.cell, getExtendedCellContext(cell.getContext()))}
</div>
</GridContextProvider>
</DynamicTypeRegistryProvider>
)

function getExtendedCellContext (context: CellContext<any, any>): ExtendedCellContext {
return {
...context,
modified: isModified
active: isActive,
modified: isModified,
onFocus: onFocusCell
}
}
}
7 changes: 6 additions & 1 deletion assets/js/src/core/components/grid/grid-cell/grid-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ import React, { useMemo } from 'react'
import { GridCell } from './grid-cell'
import { type GridContextProviderProps } from '../grid-context'
import { type GridProps } from '@Pimcore/types/components/types'
import { type GridCellReference } from '@Pimcore/components/grid/grid'

export interface GridRowProps {
row: Row<any>
modifiedCells: string
isSelected?: boolean
tableElement: GridContextProviderProps['table']
columns: GridProps['columns']
activeColumId?: string
onFocusCell?: (cell: GridCellReference) => void
}

const GridRow = ({ row, isSelected, modifiedCells, ...props }: GridRowProps): React.JSX.Element => {
Expand Down Expand Up @@ -51,15 +54,17 @@ const GridRow = ({ row, isSelected, modifiedCells, ...props }: GridRowProps): Re
>
<GridCell
cell={ cell }
isActive={ props.activeColumId === cell.column.id }
isModified={ isModifiedCell(cell.column.id) }
key={ cell.id }
onFocusCell={ props.onFocusCell }
tableElement={ props.tableElement }
/>
</td>
))}
</tr>
)
}, [JSON.stringify(row), memoModifiedCells, isSelected, props.columns])
}, [JSON.stringify(row), memoModifiedCells, isSelected, props.columns, props.activeColumId])

function isModifiedCell (cellId: string): boolean {
return memoModifiedCells.find((item) => item.columnId === cellId) !== undefined
Expand Down
132 changes: 77 additions & 55 deletions assets/js/src/core/components/grid/grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
type TableOptions,
useReactTable
} from '@tanstack/react-table'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { isEmpty } from 'lodash'
import { useStyles } from './grid.styles'
import { Resizer } from './resizer/resizer'
Expand All @@ -36,7 +36,6 @@ import { useTranslation } from 'react-i18next'
import { Checkbox, Skeleton } from 'antd'
import { GridRow } from './grid-cell/grid-row'
import { SortButton, type SortDirection, SortDirections } from '../sort-button/sort-button'
import { DynamicTypeRegistryProvider } from '@Pimcore/modules/element/dynamic-types/registry/provider/dynamic-type-registry-provider'
import { type GridProps } from '@Pimcore/types/components/types'
import trackError, { GeneralError } from '@Pimcore/modules/app/error-handler'

Expand All @@ -55,22 +54,35 @@ declare module '@tanstack/react-table' {
}
}

export interface GridCellReference {
rowIndex: number
columnIndex: number
columnId: string
}

export interface ExtendedCellContext extends CellContext<any, any> {
modified?: boolean
active?: boolean
onFocus?: (cell: GridCellReference) => void
}

export const Grid = ({ enableMultipleRowSelection = false, modifiedCells = [], sorting, manualSorting = false, enableSorting = false, enableRowSelection = false, selectedRows = {}, ...props }: GridProps): React.JSX.Element => {
export const Grid = ({ enableMultipleRowSelection = false, modifiedCells = [], sorting, manualSorting = false, enableSorting = false, hideColumnHeaders = false, highlightActiveCell = false, onActiveCellChange, enableRowSelection = false, selectedRows = {}, ...props }: GridProps): React.JSX.Element => {
const { t } = useTranslation()
const hashId = useCssComponentHash('table')
const { styles } = useStyles()
const [columnResizeMode] = useState<ColumnResizeMode>('onEnd')
const [activeCell, setActiveCell] = useState<GridCellReference | undefined>()
const [tableAutoWidth, setTableAutoWidth] = useState<boolean>(props.autoWidth ?? false)
const tableElement = useRef<HTMLTableElement>(null)
const isRowSelectionEnabled = useMemo(() => enableMultipleRowSelection || enableRowSelection, [enableMultipleRowSelection, enableRowSelection])
const [internalSorting, setInternalSorting] = useState<SortingState>(sorting ?? [])
const memoModifiedCells = useMemo(() => { return modifiedCells ?? [] }, [JSON.stringify(modifiedCells)])
const autoColumnRef = useRef<HTMLTableCellElement>(null)

useEffect(() => {
onActiveCellChange?.(activeCell)
}, [activeCell])

useEffect(() => {
if (sorting !== undefined) {
setInternalSorting(sorting)
Expand Down Expand Up @@ -185,16 +197,22 @@ export const Grid = ({ enableMultipleRowSelection = false, modifiedCells = [], s

const table = useReactTable(tableProps)

const onFocusCell = useCallback((cell: GridCellReference) => {
setActiveCell(cell)
}, [])

return useMemo(() => (
<DynamicTypeRegistryProvider serviceIds={ ['DynamicTypes/GridCellRegistry'] }>
<div className={ ['ant-table-wrapper', hashId, styles.grid].join(' ') }>
<div className="ant-table ant-table-small">
<div className='ant-table-container'>
<div className='ant-table-content'>
<table
ref={ tableElement }
style={ { width: tableAutoWidth ? '100%' : table.getCenterTotalSize(), minWidth: table.getCenterTotalSize() } }
>
<div
className={ ['ant-table-wrapper', hashId, styles.grid].join(' ') }
>
<div className="ant-table ant-table-small">
<div className='ant-table-container'>
<div className='ant-table-content'>
<table
ref={ tableElement }
style={ { width: tableAutoWidth ? '100%' : table.getCenterTotalSize(), minWidth: table.getCenterTotalSize() } }
>
{ !hideColumnHeaders && (
<thead className='ant-table-thead'>
{table.getHeaderGroups().map(headerGroup => (
<tr key={ headerGroup.id }>
Expand All @@ -204,16 +222,16 @@ export const Grid = ({ enableMultipleRowSelection = false, modifiedCells = [], s
key={ header.id }
ref={ header.column.columnDef.meta?.autoWidth === true ? autoColumnRef : null }
style={
header.column.columnDef.meta?.autoWidth === true && !header.column.getIsResizing()
? {
width: 'auto',
minWidth: header.column.getSize()
}
: {
width: header.column.getSize(),
maxWidth: header.column.getSize()
}
}
header.column.columnDef.meta?.autoWidth === true && !header.column.getIsResizing()
? {
width: 'auto',
minWidth: header.column.getSize()
}
: {
width: header.column.getSize(),
maxWidth: header.column.getSize()
}
}
>
<div className='grid__cell-content'>
<span>
Expand All @@ -224,13 +242,13 @@ export const Grid = ({ enableMultipleRowSelection = false, modifiedCells = [], s
</span>

{header.column.getCanSort() && (
<div className='grid__sorter'>
<SortButton
allowUnsorted={ sorting === undefined }
onSortingChange={ (value) => { updateSortDirection(header.column, value) } }
value={ getSortDirection(header.column) }
/>
</div>
<div className='grid__sorter'>
<SortButton
allowUnsorted={ sorting === undefined }
onSortingChange={ (value) => { updateSortDirection(header.column, value) } }
value={ getSortDirection(header.column) }
/>
</div>
)}
</div>

Expand All @@ -246,35 +264,39 @@ export const Grid = ({ enableMultipleRowSelection = false, modifiedCells = [], s
</tr>
))}
</thead>
<tbody className="ant-table-tbody">
{table.getRowModel().rows.length === 0 && (
<tr className={ 'ant-table-row' }>
<td
className='ant-table-cell ant-table-cell__no-data'
colSpan={ table.getAllColumns().length }
>
{t('no-data-available-yet')}
</td>
</tr>
)}
{table.getRowModel().rows.map(row => (
<GridRow
columns={ columns }
isSelected={ row.getIsSelected() }
key={ row.id }
modifiedCells={ JSON.stringify(getModifiedRow(row.id)) }
row={ row }
tableElement={ tableElement }
/>
))}
</tbody>
</table>
</div>
)}
<tbody
className="ant-table-tbody"
>
{table.getRowModel().rows.length === 0 && (
<tr className={ 'ant-table-row' }>
<td
className='ant-table-cell ant-table-cell__no-data'
colSpan={ table.getAllColumns().length }
>
{t('no-data-available-yet')}
</td>
</tr>
)}
{table.getRowModel().rows.map(row => (
<GridRow
activeColumId={ highlightActiveCell === true && row.index === activeCell?.rowIndex ? activeCell.columnId : undefined }
columns={ columns }
isSelected={ row.getIsSelected() }
key={ row.id }
modifiedCells={ JSON.stringify(getModifiedRow(row.id)) }
onFocusCell={ onFocusCell }
row={ row }
tableElement={ tableElement }
/>
))}
</tbody>
</table>
</div>
</div>
</div>
</DynamicTypeRegistryProvider>
), [table, modifiedCells, data, columns, rowSelection, internalSorting])
</div>
), [table, modifiedCells, data, columns, rowSelection, internalSorting, (highlightActiveCell === true ? activeCell : undefined)])

function getModifiedRow (rowIndex: string): GridProps['modifiedCells'] {
return memoModifiedCells.filter(({ rowIndex: rIndex }) => String(rIndex) === String(rowIndex)) ?? []
Expand Down
Loading
Loading