Skip to content

Commit

Permalink
Add structured table data object data type (#761)
Browse files Browse the repository at this point in the history
  • Loading branch information
markus-moser authored Dec 2, 2024
1 parent 6c7cd2a commit f1748df
Show file tree
Hide file tree
Showing 34 changed files with 2,817 additions and 1 deletion.
4 changes: 4 additions & 0 deletions assets/js/src/core/app/config/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { DynamicTypeFieldFilterDatetime } from '@Pimcore/modules/element/dynamic
import { DynamicTypeGridCellText } from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/types/text/dynamic-type-grid-cell-text'
import { DynamicTypeGridCellRegistry } from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/dynamic-type-grid-cell-registry'
import { DynamicTypeGridCellTextarea } from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/types/textarea/dynamic-type-grid-cell-text'
import { DynamicTypeGridCellNumber } from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/types/number/dynamic-type-grid-cell-number'
import { DynamicTypeGridCellSelect } from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/types/select/dynamic-type-grid-cell-select'
import { DynamicTypeGridCellCheckbox } from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/types/checkbox/dynamic-type-grid-cell-checkbox'
import { DynamicTypeGridCellDate } from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/types/date/dynamic-type-grid-cell-date'
Expand Down Expand Up @@ -107,6 +108,7 @@ import { DynamicTypeObjectDataGeoBounds } from '@Pimcore/modules/element/dynamic
import { DynamicTypeObjectDataGeoPolygon } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-geopolygon'
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 { 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 @@ -164,6 +166,7 @@ container.bind(serviceIds['DynamicTypes/BatchEdit/TextArea']).to(DynamicTypeBatc
container.bind(serviceIds['DynamicTypes/GridCellRegistry']).to(DynamicTypeGridCellRegistry).inSingletonScope()
container.bind(serviceIds['DynamicTypes/GridCell/Text']).to(DynamicTypeGridCellText).inSingletonScope()
container.bind(serviceIds['DynamicTypes/GridCell/Textarea']).to(DynamicTypeGridCellTextarea).inSingletonScope()
container.bind(serviceIds['DynamicTypes/GridCell/Number']).to(DynamicTypeGridCellNumber).inSingletonScope()
container.bind(serviceIds['DynamicTypes/GridCell/Select']).to(DynamicTypeGridCellSelect).inSingletonScope()
container.bind(serviceIds['DynamicTypes/GridCell/Checkbox']).to(DynamicTypeGridCellCheckbox).inSingletonScope()
container.bind(serviceIds['DynamicTypes/GridCell/Date']).to(DynamicTypeGridCellDate).inSingletonScope()
Expand Down Expand Up @@ -248,6 +251,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/StructuredTable']).to(DynamicTypeObjectDataStructuredTable).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/Block']).to(DynamicTypeObjectDataBlock).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/LocalizedFields']).to(DynamicTypeObjectDataLocalizedFields).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/FieldCollection']).to(DynamicTypeObjectDataFieldCollection).inSingletonScope()
Expand Down
2 changes: 2 additions & 0 deletions assets/js/src/core/app/config/services/service-ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const serviceIds = {

'DynamicTypes/GridCell/Text': 'DynamicTypes/GridCell/Text',
'DynamicTypes/GridCell/Textarea': 'DynamicTypes/GridCell/Textarea',
'DynamicTypes/GridCell/Number': 'DynamicTypes/GridCell/Number',
'DynamicTypes/GridCell/Select': 'DynamicTypes/GridCell/Select',
'DynamicTypes/GridCell/Checkbox': 'DynamicTypes/GridCell/Checkbox',
'DynamicTypes/GridCell/Date': 'DynamicTypes/GridCell/Date',
Expand Down Expand Up @@ -142,6 +143,7 @@ export const serviceIds = {
'DynamicTypes/ObjectData/GeoPolygon': 'DynamicTypes/ObjectData/GeoPolygon',
'DynamicTypes/ObjectData/GeoPolyLine': 'DynamicTypes/ObjectData/GeoPolyLine',
'DynamicTypes/ObjectData/ManyToManyRelation': 'DynamicTypes/ObjectData/ManyToManyRelation',
'DynamicTypes/ObjectData/StructuredTable': 'DynamicTypes/ObjectData/StructuredTable',
'DynamicTypes/ObjectData/Block': 'DynamicTypes/ObjectData/Block',
'DynamicTypes/ObjectData/LocalizedFields': 'DynamicTypes/ObjectData/LocalizedFields',
'DynamicTypes/ObjectData/FieldCollection': 'DynamicTypes/ObjectData/FieldCollection',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Pimcore
*
* This source file is available under two different licenses:
* - Pimcore Open Core License (POCL)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import { createStyles } from 'antd-style'

export const useStyle = createStyles(({ token, css }) => {
return {
'number-cell': css`
padding: 4px;
`
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Pimcore
*
* This source file is available under two different licenses:
* - Pimcore Open Core License (POCL)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import React, { useEffect, useRef, useState } from 'react'
import { InputNumber } from 'antd'
import { useStyle } from './number-cell.styles'
import { type DefaultCellProps } from '@Pimcore/components/grid/columns/default-cell'
import { useEditMode } from '@Pimcore/components/grid/edit-mode/use-edit-mode'
import cn from 'classnames'
import type { InputNumberRef } from 'rc-input-number'

export interface NumberCellProps extends DefaultCellProps {}

export const NumberCell = (props: NumberCellProps): React.JSX.Element => {
const { isInEditMode, disableEditMode, fireOnUpdateCellDataEvent } = useEditMode(props)
const [value, setValue] = useState<number | null>(props.getValue() as number)
const { styles } = useStyle()
const element = useRef<InputNumberRef>(null)

useEffect(() => {
if (isInEditMode) {
element.current?.focus()
}
}, [isInEditMode])

function saveValue (): void {
fireOnUpdateCellDataEvent(value)
disableEditMode()
}

function onKeyDown (event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.key === 'Enter') {
saveValue()
}
}

function onBlur (): void {
saveValue()
}

function getCellContent (): React.JSX.Element {
if (!isInEditMode) {
return (
<>
{ props.getValue() }
</>
)
}

return (
<InputNumber
className="w-full"
defaultValue={ props.getValue() }
onBlur={ onBlur }
onChange={ setValue }
onKeyDown={ onKeyDown }
ref={ element }
value={ value }
/>
)
}

return (
<div className={ cn(styles['number-cell'], 'default-cell__content') }>
{ getCellContent() }
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Pimcore
*
* This source file is available under two different licenses:
* - Pimcore Open Core License (POCL)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import React, { type ReactElement } from 'react'
import { type AbstractGridCellDefinition, DynamicTypeGridCellAbstract } from '../../dynamic-type-grid-cell-abstract'
import { NumberCell } from '../../components/number/number-cell'
import { injectable } from 'inversify'

@injectable()
export class DynamicTypeGridCellNumber extends DynamicTypeGridCellAbstract {
readonly id = 'number'

getGridCellComponent (props: AbstractGridCellDefinition): ReactElement<AbstractGridCellDefinition> {
return <NumberCell { ...props } />
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* Pimcore
*
* This source file is available under two different licenses:
* - Pimcore Open Core License (POCL)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import React from 'react'
import { Grid } from '@Pimcore/components/grid/grid'
import { type ColumnDef, createColumnHelper } from '@tanstack/react-table'
import {
type StructuredTableCol,
type StructuredTableColType, type StructuredTableColumnValue,
type StructuredTableRow,
type StructuredTableValue
} from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/components/structured-table/structured-table'
import { useTranslation } from 'react-i18next'
import _ from 'lodash'

interface StructuredTableGridProps {
castColumnValue: (value: StructuredTableColumnValue, key: string) => StructuredTableColumnValue
cols: StructuredTableCol[]
rows: StructuredTableRow[]
labelWidth: number | null
labelFirstCell: string | null
value: StructuredTableValue | null
onChange?: (value: StructuredTableValue | null) => void
}

export const StructuredTableGrid = (props: StructuredTableGridProps): React.JSX.Element => {
const columnHelper = createColumnHelper()
const { t } = useTranslation()

const mapColType = (type: StructuredTableColType): string => {
switch (type) {
case 'text':
return 'text'
case 'bool':
return 'checkbox'
case 'number':
return 'number'
default:
return 'text'
}
}

const applyColWidth = (colWidth?: number | null): number => {
return !_.isEmpty(colWidth) && colWidth! > 0 ? colWidth! : 100
}

const columns: Array<ColumnDef<any>> = [
columnHelper.accessor('rowLabel', {
header: !_.isEmpty(props.labelFirstCell) ? t(props.labelFirstCell!) : '',
size: applyColWidth(props.labelWidth)
})
]
props.cols.forEach((col) => {
columns.push(
columnHelper.accessor(col.key, {
header: t(col.label),
size: applyColWidth(col.width),
meta: {
type: mapColType(col.type),
editable: true
}
})
)
})

const rows = props.rows.map((row) => {
const rowData = {
rowLabel: t(row.label)
}
props.cols.forEach((col) => {
rowData[col.key] = props.castColumnValue(props.value?.[row.key]?.[col.key] ?? null, col.key)
})
return rowData
})

return (
<Grid
columns={ columns }
data={ rows }
onUpdateCellData={ (data) => {
const newValue = {
...props.value,
[props.rows[data.rowIndex].key]: {
...props.value?.[props.rows[data.rowIndex].key],
[data.columnId]: props.castColumnValue(data.value as StructuredTableColumnValue, data.columnId)
}
}

props.onChange?.(newValue)
} }
resizable
/>
)
}
Loading

0 comments on commit f1748df

Please sign in to comment.