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

Wrapping Tag Shared Component #562

Open
wants to merge 11 commits into
base: 1.x
Choose a base branch
from
28 changes: 28 additions & 0 deletions assets/js/src/core/components/flex/flex.styles.ts
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 { createStyles } from 'antd-style'

interface StylesProps {
itemRowGap?: number
itemColGap?: number
}

export const useStyles = createStyles(({ css }, props: StylesProps) => {
return {
rowColGap: css`
row-gap: ${props.itemRowGap}px;
column-gap: ${props.itemColGap}px;
`
}
})
74 changes: 43 additions & 31 deletions assets/js/src/core/components/flex/flex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,50 +11,62 @@
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import { Flex as AntFlex, type FlexProps as AntFlexProps, theme } from 'antd'
import React from 'react'
import { Flex as AntFlex, type FlexProps as AntFlexProps, theme } from 'antd'
import cn from 'classnames'
import { mapGapToTokenValue } from '@Pimcore/components/flex/utils/mapGapToTokenValue'
import { isString, isNumber, isObject } from '@Pimcore/utils/type-utils'
import { useStyles } from '@Pimcore/components/flex/flex.styles'
import { type GapRowColGroupType, type GapType } from '@Pimcore/types/components/types'

export interface FlexProps extends Omit<AntFlexProps, 'gap'> {
gap?: number | 'mini' | 'extra-small' | 'small' | 'normal' | 'medium' | 'large' | 'extra-large' | 'maxi'
gap?: GapType
}

const { useToken } = theme

export const Flex = ({ gap = 0, ...props }: FlexProps): React.JSX.Element => {
export const Flex = ({ gap = 0, children, ...props }: FlexProps): React.JSX.Element => {
const { token } = useToken()
let internalGap = gap

if (typeof gap === 'string') {
internalGap = transferSizingToToken(gap)
const { rowGap, colGap } = calculateGap(gap)

const { styles } = useStyles({
itemRowGap: rowGap,
itemColGap: colGap
})

const flexClassNames = cn(styles.rowColGap)

/**
* Calculates the row and column gaps based on the provided gap value.
* * The function handles three possible cases for the gap:
* * - A string value (predefined gap sizes like 'small', 'normal', etc.).
* * - A numeric value (representing a direct gap size).
* * - An object containing specific row and column gap sizes.
*/
function calculateGap (gap: GapType): { rowGap: number, colGap: number } {
const getGapValue = (gap: GapType): number => mapGapToTokenValue({ token, gap })

if (isString(gap)) return { rowGap: getGapValue(gap), colGap: getGapValue(gap) }

if (isNumber(gap)) return { rowGap: gap as number, colGap: gap as number }

if (isObject(gap)) {
return {
rowGap: getGapValue((gap as GapRowColGroupType).rowGap),
colGap: getGapValue((gap as GapRowColGroupType).colGap)
}
}

return { rowGap: 0, colGap: 0 }
}

return (
<AntFlex
gap={ internalGap }
className={ flexClassNames }
{ ...props }
/>
>
{children}
</AntFlex>
)

function transferSizingToToken (sizing: string): number {
switch (sizing) {
case 'mini':
return token.sizeXXS
case 'extra-small':
return token.sizeXS
case 'small':
return token.sizeSM
case 'normal':
return token.size
case 'medium':
return token.sizeMD
case 'large':
return token.sizeLG
case 'extra-large':
return token.sizeXL
case 'maxi':
return token.sizeXXL
default:
return 0
}
}
}
39 changes: 39 additions & 0 deletions assets/js/src/core/components/flex/utils/mapGapToTokenValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* 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 GlobalToken } from 'antd'
import type { GapType } from '@Pimcore/types/components/types'

export function mapGapToTokenValue ({ token, gap }: { token: GlobalToken, gap: GapType }): number {
switch (gap) {
case 'mini':
return token.sizeXXS
case 'extra-small':
return token.sizeXS
case 'small':
return token.sizeSM
case 'normal':
return token.size
case 'medium':
return token.sizeMD
case 'large':
return token.sizeLG
case 'extra-large':
return token.sizeXL
case 'maxi':
return token.sizeXXL

default:
return 0
}
}
55 changes: 55 additions & 0 deletions assets/js/src/core/components/tag-list/tag-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* 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 cn from 'classnames'
import { Tag, type TagProps } from '@Pimcore/components/tag/tag'
import { Flex } from '@Pimcore/components/flex/flex'
import { type GapType } from '@Pimcore/types/components/types'

export interface TagListProps {
list: TagProps[][]
itemCharMaxLength?: number
itemGap?: GapType
tagListClassNames?: string
tagListItemClassNames?: string
}

export const TagList = ({ list, itemCharMaxLength, itemGap, tagListClassNames, tagListItemClassNames }: TagListProps): React.JSX.Element => {
return (
<Flex
gap="small"
rootClassName={ cn(tagListClassNames) }
vertical
>
{list.map((group, groupIndex) => (
<Flex
gap={ itemGap }
key={ groupIndex }
rootClassName={ cn(tagListItemClassNames) }
wrap
>
{group.map((item, itemIndex) => (
<Tag
key={ `${groupIndex}-${itemIndex}` }
maxLength={ itemCharMaxLength }
{ ...item }
>
{item.children}
</Tag>
))}
</Flex>
))}
</Flex>
)
}
16 changes: 16 additions & 0 deletions assets/js/src/core/components/tag/tag.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export const useStyles = createStyles(({ css, token }) => {
return {
tag: css`
&.ant-tag {
margin-inline-end: 0;

&.ant-tag-default {
background-color: ${token.colorFillTertiary};
color: ${token.colorTextLabel};
Expand All @@ -32,6 +34,20 @@ export const useStyles = createStyles(({ css, token }) => {
margin-inline-start: 4px;
}
}
`,

tooltip: css`
.ant-tooltip-inner {
color: ${token.colorTextLightSolid};
background-color: ${token.colorBgSpotlight};
border-radius: ${token.borderRadius}px;
}

.ant-tooltip-arrow {
&::before {
background-color: ${token.colorBgSpotlight};
}
}
`
}
})
56 changes: 44 additions & 12 deletions assets/js/src/core/components/tag/tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,37 @@
* @license https://github.com/pimcore/studio-ui-bundle/blob/1.x/LICENSE.md POCL and PCL
*/

import { Tag as AntTag, type TagProps as AntTagPropsProps } from 'antd'
import React, { type ReactNode } from 'react'
import { useStyles } from './tag.styles'
import { Tag as AntTag, type TagProps as AntTagPropsProps, Tooltip } from 'antd'
import cn from 'classnames'
import { Icon } from '@Pimcore/components/icon/icon'
import { isNumber, isString } from '@Pimcore/utils/type-utils'
import { useStyles } from './tag.styles'

export interface TagProps extends AntTagPropsProps {
children: ReactNode
iconName?: string
className?: string
theme?: TagTheme
maxLength?: number
}

export type TagTheme = 'transparent'

export const Tag = ({ children, icon, iconName, theme, className, ...props }: TagProps): React.JSX.Element => {
export const Tag = ({ children, icon, iconName, theme, maxLength, className, ...props }: TagProps): React.JSX.Element => {
const { styles } = useStyles()
const classes = [styles.tag, className].filter(Boolean)
theme !== undefined && classes.push(`theme-${theme}`)

const isLimitedCharNumber = maxLength != null && isNumber(maxLength)
const shouldTruncateText = isLimitedCharNumber && isString(children) && (children as string)?.length > maxLength
const shouldShowTooltip = isLimitedCharNumber && shouldTruncateText

const displayText = shouldTruncateText ? `${(children as string)?.substring(0, maxLength)}...` : children

const tagClassNames = cn(
styles.tag,
className,
{ [`theme-${theme}`]: theme }
)

const renderIcon = (name: string): React.JSX.Element => (
<Icon
Expand All @@ -38,19 +51,38 @@ export const Tag = ({ children, icon, iconName, theme, className, ...props }: Ta
/>
)

const getIcon = (): React.ReactElement<any, string | React.JSXElementConstructor<any>> | string | number | Iterable<React.ReactNode> | React.ReactPortal | null | boolean => {
if (iconName !== undefined) return renderIcon(iconName)
else if (icon !== undefined) return icon
else return null
const getIcon = (): React.ReactNode => {
if (iconName !== undefined) {
return renderIcon(iconName)
}

return icon ?? null
}

return (
const renderTag = (): JSX.Element => (
<AntTag
className={ classes.join(' ') }
className={ tagClassNames }
icon={ getIcon() }
{ ...props }
>
{children}
{displayText}
</AntTag>
)

return (
<>
{shouldShowTooltip
? (
<Tooltip
align={ { offset: [20, -4] } }
overlayClassName={ styles.tooltip }
title={ children }
>
{renderTag()}
</Tooltip>
)
: renderTag()
}
</>
)
}
8 changes: 4 additions & 4 deletions assets/js/src/core/components/workflow-card/workflow-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ export const WorkflowCard = ({ workflow }: IWorkflowCardProps): React.JSX.Elemen
) }
>
{workflow.graph !== undefined && (
<img
alt={ 'workflow' }
src={ `data:image/svg+xml;utf8,${encodeURIComponent(workflow.graph)}` }
/>
<img
alt={ 'workflow' }
src={ `data:image/svg+xml;utf8,${encodeURIComponent(workflow.graph)}` }
/>
)}
</Card>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const defaultTheme = {
colorFill: 'rgba(215, 199, 236, 0.6)',
colorFillQuaternary: 'rgba(215, 199, 236, 0.4)',
colorBgLayout: '#fcfcfc',
colorBgSpotlight: 'rgba(0, 0, 0, 0.85)',
colorPrimary: '#722ed1',
fontSize: 12,
fontSizeHeading1: 35,
Expand Down Expand Up @@ -182,6 +183,8 @@ const defaultTheme = {
lineWidth: 1,
marginXS: 8,
paddingXXS: 4,
colGapXXS: 4,
rowGapXXS: 4,
fontSize: 12,
fontSizeIcon: 12,
fontSizeSM: 12,
Expand Down
17 changes: 17 additions & 0 deletions assets/js/src/core/types/components/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* 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
*/

type GapStringType = 'mini' | 'extra-small' | 'small' | 'normal' | 'medium' | 'large' | 'extra-large' | 'maxi'
export interface GapRowColGroupType { colGap: GapStringType, rowGap: GapStringType }

export type GapType = number | GapStringType | GapRowColGroupType
Loading