Skip to content

Commit

Permalink
Add initial setup for loading states in trees (#882)
Browse files Browse the repository at this point in the history
* Add initial setup for loading states in trees

* Apply eslint-fixer changes

* Automatic frontend build

---------

Co-authored-by: vin0401 <[email protected]>
  • Loading branch information
vin0401 and vin0401 authored Jan 17, 2025
1 parent a6ea758 commit dd15a65
Show file tree
Hide file tree
Showing 34 changed files with 1,766 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ import { type TreeNodeProps } from '../node/tree-node'
import { TreeContext } from '../element-tree'
import { Icon } from '@Pimcore/components/icon/icon'
import { useTranslation } from 'react-i18next'
import { Spin } from '@Pimcore/components/spin/spin'

export interface TreeExpanderProps {
node: TreeNodeProps
state: [boolean, React.Dispatch<React.SetStateAction<boolean>>]
}

export const TreeExpander = ({ node, state }: TreeExpanderProps): React.JSX.Element => {
const { hasChildren, children } = node
const { hasChildren, children, isLoading } = node
const { onLoad } = useContext(TreeContext)
const [isExpanded, setIsExpanded] = state
const { t } = useTranslation()
Expand All @@ -42,11 +43,17 @@ export const TreeExpander = ({ node, state }: TreeExpanderProps): React.JSX.Elem
}
}

console.log({ isLoading })

return (
<div
className='tree-expander'
style={ { minWidth: 16, width: 16, height: 16 } }
>
{isLoading === true && (
<Spin type='classic' />
)}

{node.hasChildren === true && (
// keyboard navigation is already handled on parent level
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
Expand All @@ -56,19 +63,24 @@ export const TreeExpander = ({ node, state }: TreeExpanderProps): React.JSX.Elem
role='button'
tabIndex={ -1 }
>
{isExpanded
? (
<Icon
options={ { width: 16, height: 16 } }
value="chevron-up"
/>
)
: (
<Icon
options={ { width: 16, height: 16 } }
value="chevron-down"
/>
)}
{isLoading !== true && (
<>
{isExpanded
? (
<Icon
options={ { width: 16, height: 16 } }
value="chevron-up"
/>
)
: (
<Icon
options={ { width: 16, height: 16 } }
value="chevron-down"
/>
)
}
</>
)}
</span>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,18 @@ export const useStyles = createStyles(({ token, css }) => {
user-select: none;
&.tree-node--is-root {
.tree-node__content {
& > .tree-node__content {
padding-left: ${token.paddingSM}px;
}
}
&.tree-node--danger {
& > .tree-node__content .tree-node__content-wrapper {
color: ${token.colorError};
text-decoration: line-through;
}
}
.tree-node__content {
cursor: pointer;
width: 100%;
Expand Down
10 changes: 9 additions & 1 deletion assets/js/src/core/components/element-tree/node/tree-node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export interface TreeNodeProps {
type?: string
parentId?: string
isRoot?: boolean
isLoading?: boolean
danger?: boolean
}

const defaultProps: TreeNodeProps = {
Expand Down Expand Up @@ -71,6 +73,8 @@ const TreeNode = ({
label = defaultProps.label,
level = defaultProps.level,
isRoot = defaultProps.isRoot,
isLoading = false,
danger = false,
...props
}: TreeNodeProps): React.JSX.Element => {
const { token } = useToken()
Expand All @@ -86,7 +90,7 @@ const TreeNode = ({
} = useContext(TreeContext)
const [isExpanded, setIsExpanded] = React.useState(children.length !== 0)
const [selectedIds, setSelectedIds] = selectedIdsState!
const treeNodeProps = { id, icon, label, internalKey, level, ...props }
const treeNodeProps = { id, icon, label, internalKey, level, isLoading, isRoot, danger, ...props }
const { uploadFile: uploadFileProcessor } = UseFileUploader({ parentId: id })

useEffect(() => {
Expand All @@ -105,6 +109,10 @@ const TreeNode = ({
classes.push('tree-node--selected')
}

if (danger) {
classes.push('tree-node--danger')
}

if (isRoot === true) {
classes.push('tree-node--is-root')
}
Expand Down
20 changes: 15 additions & 5 deletions assets/js/src/core/components/spin/spin.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,22 @@ import { Spin } from './spin'

const config: Meta = {
title: 'Components/Feedback/Spin',
component: Spin,
args: {
asContainer: true
}
component: Spin
}

export default config

export const _default = {}
export const _default = {
}

export const Classic = {
args: {
type: 'classic'
}
}

export const AsContainer = {
args: {
asContainer: true
}
}
37 changes: 23 additions & 14 deletions assets/js/src/core/components/spin/spin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,45 @@

import React from 'react'
import { Spin as AntdSpin, type SpinProps as AntdSpinProps } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import { Icon } from '../icon/icon'
import { useStyles } from './spin.styles'

interface SpinProps extends AntdSpinProps {
interface SpinProps extends Omit<AntdSpinProps, 'indicator'> {
type?: 'dotted' | 'classic'
asContainer?: boolean
};

export const Spin = ({ asContainer = false, tip, ...props }: SpinProps): React.JSX.Element => {
export const Spin = ({ asContainer = false, type = 'dotted', tip, ...props }: SpinProps): React.JSX.Element => {
const { styles } = useStyles()

let icon = (
<Icon
className={ styles.spin }
value='spinner'
/>
)

if (type === 'classic') {
icon = (
<LoadingOutlined spin />
)
}

return (
<>
{ !asContainer && (
<AntdSpin
indicator={ <Icon
className={ styles.spin }
value='spinner'
/> }
{ ...props }
/>
<>
{icon}
</>
)}

{ asContainer && (
<div className={ styles.spinContainer }>
<AntdSpin
indicator={ <Icon
className={ styles.spin }
options={ { width: 20, height: 20 } }
value='spinner'
/> }
indicator={ <>
{icon}
</> }
{ ...props }
/>

Expand Down
34 changes: 34 additions & 0 deletions assets/js/src/core/modules/asset/tree/node/with-action-states.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* 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 TreeNodeProps } from '@Pimcore/components/element-tree/node/tree-node'
import React, { type ElementType, type ReactElement } from 'react'
import { useAssetPatchByIdMutation } from '../../asset-api-slice-enhanced'
import { useElementDeleteMutation } from '@Pimcore/modules/element/element-api-slice.gen'

export const withActionStates = (Component: ElementType<TreeNodeProps>): ElementType<TreeNodeProps> => {
const ActionStates = (props: TreeNodeProps): ReactElement => {
const [, { isLoading }] = useAssetPatchByIdMutation({ fixedCacheKey: `ASSET_ACTION_RENAME_ID_${props.id}` })
const [, { isLoading: isDeleteLoading }] = useElementDeleteMutation({ fixedCacheKey: `ASSET_ACTION_DELETE_ID_${props.id}` })

return (
<Component
{ ...props }
danger={ isDeleteLoading }
isLoading={ isLoading || isDeleteLoading }
/>
)
}

return ActionStates
}
3 changes: 2 additions & 1 deletion assets/js/src/core/modules/asset/tree/tree-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { transformApiDataToNodes } from './utils/transform-api-data-to-node'
import { Skeleton } from '@Pimcore/components/element-tree/skeleton/skeleton'
import { useTranslation } from 'react-i18next'
import { Box } from '@Pimcore/components/box/box'
import { withActionStates } from './node/with-action-states'

export interface TreeContainerProps {
id: number
Expand Down Expand Up @@ -95,7 +96,7 @@ const TreeContainer = ({ id = 1 }: TreeContainerProps): React.JSX.Element => {
nodeId={ id }
onSelect={ onSelect }
renderFilter={ SearchContainer }
renderNode={ withDraggable(TreeNode) }
renderNode={ withActionStates(withDraggable(TreeNode)) }
renderNodeContent={ defaultProps.renderNodeContent }
renderPager={ PagerContainer }
rootNode={ rootNode }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const useDelete = (elementType: ElementType): UseDeleteHookReturn => {
const { refreshTree } = useRefreshTree(elementType)
const { refreshGrid } = useRefreshGrid(elementType)
const { getElementById } = useElementApi(elementType)
const [elementDelete] = useElementDeleteMutation()
const [elementDelete] = useElementDeleteMutation({ fixedCacheKey: `${elementType.toUpperCase()}_ACTION_DELETE_ID_XY` })

const deleteElement = (id: number, label: string, parentId?: number, onFinish?: () => void): void => {
modal.confirm({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const AssignedTagsTable = ({ tags, isLoading }: { tags: Tag[], isLoading:
const checkedTags = useMemo(() => {
const tagEntries = Object.entries(tags)
return tagEntries
.map(([key, tag]) => ({ ...tag, key }))
.map(([key, tag]) => ({ ...tag }))
.filter((tag) => tag.id !== undefined)
}, [tags])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ interface UseElementApiReturn {

export const useElementApi = (elementType: ElementType): UseElementApiReturn => {
const dispatch = useAppDispatch()
const [assetPatch] = useAssetPatchByIdMutation()
const [assetPatch] = useAssetPatchByIdMutation({ fixedCacheKey: 'ASSET_ACTION_RENAME' })
const [dataObjectPatch] = useDataObjectPatchByIdMutation()
const { updateFieldValue: updateAssetFieldValue } = useCacheUpdate('asset', ['ASSET_TREE'])
const { updateFieldValue: updateDataObjectFieldValue } = useCacheUpdate('data-object', ['DATA_OBJECT_TREE'])
Expand Down
2 changes: 2 additions & 0 deletions public/build/398bdaff-dc7f-46da-a861-3f949842c411/105.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*!
*
* /**
* * 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
* * /
*
*/
16 changes: 16 additions & 0 deletions public/build/398bdaff-dc7f-46da-a861-3f949842c411/core-dll.css

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions public/build/398bdaff-dc7f-46da-a861-3f949842c411/core-dll.js

Large diffs are not rendered by default.

Loading

0 comments on commit dd15a65

Please sign in to comment.