Skip to content

Commit

Permalink
Add advanced many to many object relation data object type (#910)
Browse files Browse the repository at this point in the history
  • Loading branch information
markus-moser authored Jan 22, 2025
1 parent 0f50592 commit f3d035d
Show file tree
Hide file tree
Showing 41 changed files with 2,231 additions and 14 deletions.
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 @@ -38,6 +38,7 @@ import { DynamicTypeGridCellRegistry } from '@Pimcore/modules/element/dynamic-ty
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 { DynamicTypeGridCellMultiSelect } from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/types/multi-select/dynamic-type-grid-cell-multi-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'
import { DynamicTypeGridCellTime } from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/types/time/dynamic-type-grid-cell-time'
Expand Down Expand Up @@ -118,6 +119,7 @@ import { DynamicTypeObjectDataGeoPolyLine } from '@Pimcore/modules/element/dynam
import { DynamicTypeObjectDataManyToOneRelation } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-many-to-one-relation'
import { DynamicTypeObjectDataManyToManyRelation } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-many-to-many-relation'
import { DynamicTypeObjectDataManyToManyObjectRelation } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-many-to-many-object-relation'
import { DynamicTypeObjectDataAdvancedManyToManyObjectRelation } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-advanced-many-to-many-object-relation'
import { DynamicTypeObjectDataReverseObjectRelation } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-reverse-object-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'
Expand Down Expand Up @@ -181,6 +183,7 @@ container.bind(serviceIds['DynamicTypes/GridCell/Text']).to(DynamicTypeGridCellT
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/MultiSelect']).to(DynamicTypeGridCellMultiSelect).inSingletonScope()
container.bind(serviceIds['DynamicTypes/GridCell/Checkbox']).to(DynamicTypeGridCellCheckbox).inSingletonScope()
container.bind(serviceIds['DynamicTypes/GridCell/Date']).to(DynamicTypeGridCellDate).inSingletonScope()
container.bind(serviceIds['DynamicTypes/GridCell/Time']).to(DynamicTypeGridCellTime).inSingletonScope()
Expand Down Expand Up @@ -274,6 +277,7 @@ container.bind(serviceIds['DynamicTypes/ObjectData/GeoPolyLine']).to(DynamicType
container.bind(serviceIds['DynamicTypes/ObjectData/ManyToOneRelation']).to(DynamicTypeObjectDataManyToOneRelation).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/ManyToManyRelation']).to(DynamicTypeObjectDataManyToManyRelation).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/ManyToManyObjectRelation']).to(DynamicTypeObjectDataManyToManyObjectRelation).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/AdvancedManyToManyObjectRelation']).to(DynamicTypeObjectDataAdvancedManyToManyObjectRelation).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/ReverseObjectRelation']).to(DynamicTypeObjectDataReverseObjectRelation).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/Table']).to(DynamicTypeObjectDataTable).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/StructuredTable']).to(DynamicTypeObjectDataStructuredTable).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 @@ -63,6 +63,7 @@ export const serviceIds = {
'DynamicTypes/GridCell/Textarea': 'DynamicTypes/GridCell/Textarea',
'DynamicTypes/GridCell/Number': 'DynamicTypes/GridCell/Number',
'DynamicTypes/GridCell/Select': 'DynamicTypes/GridCell/Select',
'DynamicTypes/GridCell/MultiSelect': 'DynamicTypes/GridCell/MultiSelect',
'DynamicTypes/GridCell/Checkbox': 'DynamicTypes/GridCell/Checkbox',
'DynamicTypes/GridCell/Date': 'DynamicTypes/GridCell/Date',
'DynamicTypes/GridCell/Time': 'DynamicTypes/GridCell/Time',
Expand Down Expand Up @@ -153,6 +154,7 @@ export const serviceIds = {
'DynamicTypes/ObjectData/ManyToOneRelation': 'DynamicTypes/ObjectData/ManyToOneRelation',
'DynamicTypes/ObjectData/ManyToManyRelation': 'DynamicTypes/ObjectData/ManyToManyRelation',
'DynamicTypes/ObjectData/ManyToManyObjectRelation': 'DynamicTypes/ObjectData/ManyToManyObjectRelation',
'DynamicTypes/ObjectData/AdvancedManyToManyObjectRelation': 'DynamicTypes/ObjectData/AdvancedManyToManyObjectRelation',
'DynamicTypes/ObjectData/ReverseObjectRelation': 'DynamicTypes/ObjectData/ReverseObjectRelation',
'DynamicTypes/ObjectData/Table': 'DynamicTypes/ObjectData/Table',
'DynamicTypes/ObjectData/StructuredTable': 'DynamicTypes/ObjectData/StructuredTable',
Expand Down
2 changes: 1 addition & 1 deletion assets/js/src/core/components/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const Select = forwardRef<RefSelectProps, SelectProps>(({ customIcon, cus
const isStatusWarning = status === 'warning'
const isStatusError = status === 'error'

const selectContainerClassNames = cn(styles.selectContainer, {
const selectContainerClassNames = cn('studio-select', styles.selectContainer, {
[styles.selectContainerWarning]: isStatusWarning,
[styles.selectContainerError]: isStatusError,
[styles.selectContainerWithClear]: allowClear === true && isSelected
Expand Down
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 { createStyles } from 'antd-style'

export const useStyles = createStyles(({ css }) => {
return {
'multi-select-cell': css`
padding: 4px;
.ant-select, .studio-select {
width: 100%;
}
`
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* 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 { type RefSelectProps } from 'antd/es/select'
import cn from 'classnames'
import { useEditMode } from '@Pimcore/components/grid/edit-mode/use-edit-mode'
import { Select } from '@Pimcore/components/select/select'
import { useStyles } from './multi-select-cell.styles'
import { type DefaultCellProps } from '@Pimcore/components/grid/columns/default-cell'
import {
type SelectOptionType
} from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/components/select/select-cell'

export interface MultiSelectCellConfig {
options: string[] | SelectOptionType[]
}
export const MultiSelectCell = (props: DefaultCellProps): React.JSX.Element => {
const { styles } = useStyles()
const { column, getValue } = props
const { isInEditMode, disableEditMode, fireOnUpdateCellDataEvent } = useEditMode(props)
const [open, setOpen] = useState<boolean>(false)
const config = column.columnDef.meta?.config as MultiSelectCellConfig | undefined
const element = useRef<RefSelectProps>(null)

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

const value: [] = Array.isArray(getValue()) ? getValue() : []

if (config === undefined) {
return <>{ value.join(', ') }</>
}

const options: SelectOptionType[] = config.options.map((value: string | object) => (
typeof value === 'object' ? value : { label: value, value }
))

const displayOptions = value.map((value: string) => {
const option = options.find((option: SelectOptionType) => option.value === value)
return option?.displayValue ?? option?.label ?? value
})
const displayValue = displayOptions.join(', ')

if (!isInEditMode) {
return (
<div className={ [styles['multi-select-cell'], 'default-cell__content'].join(' ') }>
{ displayValue }
</div>
)
}

return (
<div className={ cn(styles['multi-select-cell'], 'default-cell__content') }>
<Select
mode="multiple"
onBlur={ disableEditMode }
onChange={ onChange }
open={ open }
options={ options }
popupMatchSelectWidth={ false }
ref={ element }
value={ value }
/>
</div>
)

function onChange (value: string): void {
fireOnUpdateCellDataEvent(value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const useStyles = createStyles(({ css }) => {
'select-cell': css`
padding: 4px;
.ant-select {
.ant-select, .studio-select {
width: 100%;
}
`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* 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 { injectable } from 'inversify'
import {
MultiSelectCell
} from '@Pimcore/modules/element/dynamic-types/defintinitions/grid-cell/components/multi-select/multi-select-cell'

@injectable()
export class DynamicTypeGridCellMultiSelect extends DynamicTypeGridCellAbstract {
readonly id = 'multi-select'

getGridCellComponent (props: AbstractGridCellDefinition): ReactElement<AbstractGridCellDefinition> {
return <MultiSelectCell { ...props } />
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* 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 } from 'react'
import {
ManyToManyObjectRelation,
type VisibleFieldDefinition
} from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/components/many-to-many-object-relation/many-to-many-object-relation'
import type {
ManyToManyRelationValue,
ManyToManyRelationValueItem
} from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/components/many-to-many-relation/hooks/use-value'
import _ from 'lodash'
import { Alert } from '@Pimcore/components/alert/alert'
import {
type AdvancedManyToManyRelationValue
} from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/helpers/relations/types/advanced-many-to-many-relation'
import {
useConvertRelationEditableColumns
} from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/helpers/relations/hooks/use-convert-relation-editable-columns'

export interface AdvancedManyToManyObjectRelationClassDefinitionProps {
allowToClearRelation: boolean
allowMultipleAssignments: boolean
maxItems: number | null
pathFormatterClass: string | null
width: number | string | null
height: number | string | null
visibleFieldDefinitions?: Record<string, VisibleFieldDefinition> | null
allowedClassId: string | null
columns?: RelationColumnDefinition[] | null
name: string[]
}

export interface RelationColumnDefinition {
type?: string
position: number
key: string
label?: string
width?: number
value?: string
}

export interface AdvancedManyToManyObjectRelationProps extends AdvancedManyToManyObjectRelationClassDefinitionProps {
disabled?: boolean
value?: AdvancedManyToManyRelationValue | null
onChange?: (value?: AdvancedManyToManyRelationValue | null) => void
enrichRowData?: (row: ManyToManyRelationValueItem) => ManyToManyRelationValueItem & Record<string, any>
}

export const AdvancedManyToManyObjectRelation = (props: AdvancedManyToManyObjectRelationProps): React.JSX.Element => {
const fieldName = props.name[props.name.length - 1]
const { columnDefinition, onUpdateCellData, convertToManyToManyRelationValue, convertToAdvancedManyToManyRelationValue } = useConvertRelationEditableColumns(props.columns ?? [], fieldName, props.value, props.onChange)

useEffect(() => {
}, [props.value])

if (_.isEmpty(props.allowedClassId)) {
return (
<Alert
message="Allowed class definition is missing in field configuration."
type="warning"
/>
)
}

const onChange = (value?: ManyToManyRelationValue | null): void => {
props.onChange?.(convertToAdvancedManyToManyRelationValue(value))
}

return (
<ManyToManyObjectRelation
{ ...props }
allowedClasses={ [String(props.allowedClassId)] }
columnDefinition={ columnDefinition }
dataObjectsAllowed
onChange={ onChange }
onUpdateCellData={ onUpdateCellData }
value={ convertToManyToManyRelationValue(props.value) }
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
enrichRowData,
visibleFieldsToColumnDefinitions
} from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/components/many-to-many-object-relation/utils/column-definition'
import { type OnUpdateCellDataEvent } from '@Pimcore/types/components/types'

export interface ManyToManyObjectRelationClassDefinitionProps {
allowToClearRelation: boolean
Expand All @@ -51,6 +52,7 @@ export interface ManyToManyObjectRelationProps extends IRelationAllowedTypesData
columnDefinition?: Array<ColumnDef<any>>
enrichRowData?: (row: ManyToManyRelationValueItem) => ManyToManyRelationValueItem & Record<string, any>
hint?: React.ReactNode | null
onUpdateCellData?: (event: OnUpdateCellDataEvent) => void
}

export const ManyToManyObjectRelation = (props: ManyToManyObjectRelationProps): React.JSX.Element => {
Expand Down Expand Up @@ -95,7 +97,7 @@ export const ManyToManyObjectRelation = (props: ManyToManyObjectRelationProps):
return (
<ManyToManyRelation
{ ...props }
columnDefinition={ columnDefinition }
columnDefinition={ [...columnDefinition, ...(props.columnDefinition ?? [])] }
dataObjectsAllowed={ !_.isEmpty(props.allowedClasses) }
enrichRowData={ (row: ManyToManyRelationValueItem) => enrichRowData(visibleFieldDefinitions, row) }
// isLoading // todo: set this prop while loading the column definition data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,19 @@ import cn from 'classnames'
import { useDownload } from '@Pimcore/modules/asset/actions/download/use-download'
import { toCssDimension } from '@Pimcore/utils/css'
import { Content } from '@Pimcore/components/content/content'
import { type OnUpdateCellDataEvent } from '@Pimcore/types/components/types'

interface ManyToManyRelationGridProps {
value?: ManyToManyRelationValue | null
deleteItem: (id: number, type: string) => void
deleteItem: (rowIndex: number) => void
assetInlineDownloadAllowed: boolean
disabled?: boolean
width: number | string | null
height: number | string | null
enrichRowData?: (row: ManyToManyRelationValueItem) => ManyToManyRelationValueItem & Record<string, any>
columnDefinition?: Array<ColumnDef<any>>
hint?: React.ReactNode | null
onUpdateCellData?: (event: OnUpdateCellDataEvent) => void
}

export const ManyToManyRelationGrid = forwardRef(function ManyToManyRelationGrid (props: ManyToManyRelationGridProps, ref: MutableRefObject<HTMLDivElement>): React.JSX.Element {
Expand Down Expand Up @@ -89,6 +91,7 @@ export const ManyToManyRelationGrid = forwardRef(function ManyToManyRelationGrid
header: t('actions'),
size: 110,
cell: (info) => {
const rowIndex = info.row.index
const rowValue = info.row.original as ManyToManyRelationValueItem

const buttons: ReactElement[] = []
Expand Down Expand Up @@ -152,7 +155,7 @@ export const ManyToManyRelationGrid = forwardRef(function ManyToManyRelationGrid
} }
/>,
onOk: () => {
props.deleteItem(rowValue.id, rowValue.type)
props.deleteItem(rowIndex)
}
})
} }
Expand Down Expand Up @@ -208,6 +211,7 @@ export const ManyToManyRelationGrid = forwardRef(function ManyToManyRelationGrid
autoWidth
columns={ columns }
data={ getDataArray() }
onUpdateCellData={ props.onUpdateCellData }
resizable
/>
{ props.hint }
Expand Down
Loading

0 comments on commit f3d035d

Please sign in to comment.