Skip to content

Commit

Permalink
Image data object data type (#789)
Browse files Browse the repository at this point in the history
  • Loading branch information
markus-moser authored Dec 4, 2024
1 parent 078a438 commit db21e92
Show file tree
Hide file tree
Showing 47 changed files with 2,310 additions and 1,929 deletions.
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 @@ -103,6 +103,7 @@ import { DynamicTypeObjectDataDatetime } from '@Pimcore/modules/element/dynamic-
import { DynamicTypeObjectDataDateRange } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-date-range'
import { DynamicTypeObjectDataTime } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-time'
import { DynamicTypeObjectDataExternalImage } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-external-image'
import { DynamicTypeObjectDataImage } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-image'
import { DynamicTypeObjectDataGeoPoint } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-geopoint'
import { DynamicTypeObjectDataGeoBounds } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-geobounds'
import { DynamicTypeObjectDataGeoPolygon } from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/types/dynamic-type-object-data-geopolygon'
Expand Down Expand Up @@ -246,6 +247,7 @@ container.bind(serviceIds['DynamicTypes/ObjectData/Datetime']).to(DynamicTypeObj
container.bind(serviceIds['DynamicTypes/ObjectData/DateRange']).to(DynamicTypeObjectDataDateRange).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/Time']).to(DynamicTypeObjectDataTime).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/ExternalImage']).to(DynamicTypeObjectDataExternalImage).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/Image']).to(DynamicTypeObjectDataImage).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/GeoPoint']).to(DynamicTypeObjectDataGeoPoint).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/GeoBounds']).to(DynamicTypeObjectDataGeoBounds).inSingletonScope()
container.bind(serviceIds['DynamicTypes/ObjectData/GeoPolygon']).to(DynamicTypeObjectDataGeoPolygon).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 @@ -138,6 +138,7 @@ export const serviceIds = {
'DynamicTypes/ObjectData/DateRange': 'DynamicTypes/ObjectData/DateRange',
'DynamicTypes/ObjectData/Time': 'DynamicTypes/ObjectData/Time',
'DynamicTypes/ObjectData/ExternalImage': 'DynamicTypes/ObjectData/ExternalImage',
'DynamicTypes/ObjectData/Image': 'DynamicTypes/ObjectData/Image',
'DynamicTypes/ObjectData/GeoPoint': 'DynamicTypes/ObjectData/GeoPoint',
'DynamicTypes/ObjectData/GeoBounds': 'DynamicTypes/ObjectData/GeoBounds',
'DynamicTypes/ObjectData/GeoPolygon': 'DynamicTypes/ObjectData/GeoPolygon',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const useStyle = createStyles(({ token, css }) => {
& .dnd--drag-valid {
background: ${token.colorBgTextActive};
border: 1px dashed ${token.colorInfoBorder};
border: 1px dashed ${token.colorInfoBorderHover};
}
& .dnd--drag-error {
Expand All @@ -32,9 +32,9 @@ export const useStyle = createStyles(({ token, css }) => {
}
`,
outline: css`
& .dnd--drag-valid {
outline: 1px dashed ${token.colorInfoBorder};
outline: 1px dashed ${token.colorInfoBorderHover};
}
& .dnd--drag-error {
Expand Down
3 changes: 2 additions & 1 deletion assets/js/src/core/components/flex/flex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import React from 'react'
import React, { type LegacyRef } from 'react'
import { Flex as AntFlex, type FlexProps as AntFlexProps, theme } from 'antd'
import cn from 'classnames'
import { isString, isNumber, isObject } from 'lodash'
Expand All @@ -21,6 +21,7 @@ import { type GapType } from '@Pimcore/types/components/types'

export interface FlexProps extends Omit<AntFlexProps, 'gap'> {
gap?: GapType
ref?: LegacyRef<HTMLDivElement>
}

const { useToken } = theme
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* 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 { type Meta } from '@storybook/react'
import { ImagePreview } from './image-preview'

const config: Meta = {
title: 'Components/Data Display/ImagePreview',
component: ImagePreview,
tags: ['autodocs'],
parameters: {
docs: {
description: {
component: 'This component is used to display an image preview with specified width and height. The component has a max-width of 100% and the image is displayed centered within the given box. This component can also be used within a droppable component to allow drag and drop of images.'
}
}
}
}

export default config

export const _default = {
args: {
width: 300,
height: 300,
style: {
border: '1px dashed #d9d9d9'
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* 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 {
imagePreviewContainer: css`
display: flex;
justify-content: center;
align-items: center;
max-width: 100%;
.ant-image {
height: 100%;
width: 100%;
}
.ant-image-img {
width: 100%;
height: 100%;
object-fit: contain;
}
`
}
})
74 changes: 74 additions & 0 deletions assets/js/src/core/components/image-preview/image-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* 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 CSSProperties, forwardRef, type MutableRefObject, useEffect } from 'react'
import { useStyle } from './image-preview.styles'
import cn from 'classnames'
import { toCssDimension } from '@Pimcore/utils/css'
import { Image } from 'antd'
import { getPrefix } from '@Pimcore/app/api/pimcore/route'
import { useDroppable } from '@Pimcore/components/drag-and-drop/hooks/use-droppable'
import { Spin } from '@Pimcore/components/spin/spin'
import { Flex } from '@Pimcore/components/flex/flex'

interface ImagePreviewProps {
src?: string
assetId?: number
className?: string
width: number | string
height: number | string
style?: CSSProperties
}

export const ImagePreview = forwardRef(function ImagePreview ({ src, assetId, width, height, className, style }: ImagePreviewProps, ref: MutableRefObject<HTMLDivElement>): React.JSX.Element {
const [key, setKey] = React.useState(0)
const { getStateClasses } = useDroppable()
const { styles } = useStyle()

const imageSrc = assetId !== undefined ? `${getPrefix()}/assets/${assetId}/image/stream/preview` : src

useEffect(() => {
setKey(key + 1)
}, [imageSrc])

const loadingSpinner = (
<Flex
align="center"
className="h-full"
justify="center"
>
<Spin size="small" />
</Flex>
)

return (
<div
className={ cn(className, styles.imagePreviewContainer, ...getStateClasses()) }
ref={ ref }
style={ {
...style,
height: toCssDimension(height),
width: toCssDimension(width)
} }
>
<Image
className="w-full"
fallback="/bundles/pimcorestudioui/img/fallback-image.svg"
key={ key }
placeholder={ loadingSpinner }
preview={ false }
src={ imageSrc }
/>
</div>
)
})
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ export const useStyle = createStyles(({ token, css }) => {
return {
imageTargetContainer: css`
border-radius: ${token.borderRadiusLG}px;
border: 1px dashed ${token.colorBorder};
outline: 1px dashed ${token.colorBorder};
background: ${token.controlItemBgHover};
padding: ${token.paddingSM}px;
max-width: 100%;
.image-target-title {
text-align: center;
Expand Down
51 changes: 39 additions & 12 deletions assets/js/src/core/components/image-target/image-target.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,57 @@
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import React from 'react'
import React, { forwardRef, type MutableRefObject } from 'react'
import { Flex } from '@Pimcore/components/flex/flex'
import { useStyle } from './image-target.styles'
import cn from 'classnames'
import { toCssDimension } from '@Pimcore/utils/css'
import { Icon } from '@Pimcore/components/icon/icon'
import { useDroppable } from '@Pimcore/components/drag-and-drop/hooks/use-droppable'

interface ImageTargetProps {
title: string
className?: string
width?: number
height?: number
width?: number | string
height?: number | string
dndIcon?: boolean
uploadIcon?: boolean
}

export const ImageTarget = ({ title, width, height, className }: ImageTargetProps): React.JSX.Element => {
export const ImageTarget = forwardRef(function ImageTarget ({ title, className, width = 200, height = 200, dndIcon, uploadIcon }: ImageTargetProps, ref: MutableRefObject<HTMLDivElement>): React.JSX.Element {
const { getStateClasses } = useDroppable()
const { styles } = useStyle()

return (
<Flex
align="center"
className={ cn(className, styles.imageTargetContainer) }
justify="center"
style={ { maxWidth: toCssDimension(width), height: toCssDimension(height) } }
<div
className={ cn(className, styles.imageTargetContainer, ...getStateClasses()) }
ref={ ref }
style={ {
height: toCssDimension(height),
width: toCssDimension(width)
} }
>
<div className="image-target-title">{ title }</div>
</Flex>
<Flex
align="center"
gap="extra-small"
justify="center"
style={ { height: '100%' } }
vertical
>
{ (dndIcon === true || uploadIcon === true) && (
<div className="icon-container">
<Icon
options={ { height: 30, width: 30 } }
value={ 'PlusOutlined' }
/>
<Icon
options={ { height: 30, width: 30 } }
value={ 'copy-07' }
/>
</div>
)}
<div className="image-target-title">{ title }</div>
</Flex>
</div>
)
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const defaultTheme = {
colorTextTreeElement: '#404655',
colorIconTree: '#404655',
colorIconTreeUnpublished: 'rgba(64, 70, 85, 0.4)',
colorInfoBorderHover: '#b37feb',
paddingTabs: 8,
colorTextSidebarTitle: '#531dab',
colorBgToolbar: '#f5f3fa',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ import { Card } from '@Pimcore/components/card/card'
import {
ExternalImageFooter
} from '@Pimcore/modules/element/dynamic-types/defintinitions/objects/data-related/components/external-image/footer'
import { Image } from 'antd'
import { ImageTarget } from '@Pimcore/components/image-target/image-target'
import { toCssDimension } from '@Pimcore/utils/css'
import { useTranslation } from 'react-i18next'
import { ImagePreview } from '@Pimcore/components/image-preview/image-preview'

export interface ExternalImageValue {
url: string
Expand Down Expand Up @@ -53,6 +52,7 @@ export const ExternalImage = (props: ExternalImageProps): React.JSX.Element => {
return (
<>
<Card
className="max-w-full"
fitContent={ Boolean(props.inputWidth) }
footer={ <ExternalImageFooter
disabled={ props.disabled }
Expand All @@ -64,19 +64,16 @@ export const ExternalImage = (props: ExternalImageProps): React.JSX.Element => {
>
{ value !== null && value.url !== ''
? (
<Image
fallback="/bundles/pimcorestudioui/img/fallback-image.svg"
<ImagePreview
height={ previewHeight }
preview={ false }
src={ value?.url }
style={ { maxHeight: toCssDimension(previewHeight), maxWidth: toCssDimension(previewWidth) } }
width={ '100%' }
width={ previewWidth }
/>
)
: (
<ImageTarget
height={ previewHeight }
title={ t('data-object.editor.external-image.preview-placeholder') }
title={ t('external-image.preview-placeholder') }
width={ previewWidth }
/>
)}
Expand Down
Loading

0 comments on commit db21e92

Please sign in to comment.