143 lines
3.9 KiB
JavaScript
143 lines
3.9 KiB
JavaScript
import { createElement } from 'react'
|
|
import { Dropdown, Button } from 'antd'
|
|
import { getModelByName } from '../../../database/ObjectModels'
|
|
import PropTypes from 'prop-types'
|
|
import { useNavigate, useLocation } from 'react-router-dom'
|
|
|
|
// Recursively filter actions based on visibleActions
|
|
function filterActionsByVisibility(actions, visibleActions) {
|
|
if (!visibleActions) return actions
|
|
|
|
return actions.filter((action) => {
|
|
if (action.type === 'divider') {
|
|
return true // Always show dividers
|
|
}
|
|
|
|
const actionKey = action.key || action.name
|
|
const isVisible = visibleActions[actionKey] !== false
|
|
|
|
// If this action has children, filter them recursively
|
|
if (action.children && Array.isArray(action.children)) {
|
|
const filteredChildren = filterActionsByVisibility(
|
|
action.children,
|
|
visibleActions
|
|
)
|
|
action.children = filteredChildren
|
|
// Show parent if it has visible children or if it's explicitly visible
|
|
return (
|
|
isVisible &&
|
|
(filteredChildren.length > 0 || visibleActions[actionKey] === true)
|
|
)
|
|
}
|
|
|
|
return isVisible
|
|
})
|
|
}
|
|
|
|
// Recursively map actions to AntD Dropdown items
|
|
function mapActionsToMenuItems(actions, currentUrlWithActions, id) {
|
|
return actions.map((action) => {
|
|
if (action.type === 'divider') {
|
|
return { type: 'divider' }
|
|
}
|
|
const actionUrl = action.url ? action.url(id) : undefined
|
|
const item = {
|
|
key: action.key || action.name,
|
|
label: action.label,
|
|
danger: action?.danger || false,
|
|
icon: action.icon ? createElement(action.icon) : undefined,
|
|
disabled: actionUrl && actionUrl === currentUrlWithActions
|
|
}
|
|
if (action.children && Array.isArray(action.children)) {
|
|
item.children = mapActionsToMenuItems(
|
|
action.children,
|
|
currentUrlWithActions,
|
|
id
|
|
)
|
|
}
|
|
return item
|
|
})
|
|
}
|
|
|
|
const stripActionParam = (pathname, search) => {
|
|
const params = new URLSearchParams(search)
|
|
params.delete('action')
|
|
const query = params.toString()
|
|
return pathname + (query ? `?${query}` : '')
|
|
}
|
|
|
|
const ObjectActions = ({
|
|
type,
|
|
id,
|
|
disabled = false,
|
|
buttonProps = {},
|
|
visibleActions = {},
|
|
...dropdownProps
|
|
}) => {
|
|
const model = getModelByName(type)
|
|
const actions = model.actions || []
|
|
const navigate = useNavigate()
|
|
const location = useLocation()
|
|
|
|
// Get current url without 'action' param
|
|
const currentUrlWithoutActions = stripActionParam(
|
|
location.pathname,
|
|
location.search
|
|
)
|
|
|
|
// First filter by visibility, then by current URL
|
|
const visibilityFilteredActions = filterActionsByVisibility(
|
|
actions,
|
|
visibleActions
|
|
)
|
|
|
|
const filteredActions = visibilityFilteredActions.filter(
|
|
(action) =>
|
|
typeof action.url !== 'function' ||
|
|
action.url(id) !== currentUrlWithoutActions
|
|
)
|
|
|
|
const currentUrlWithActions = location.pathname + location.search
|
|
|
|
// Compose AntD Dropdown menu items
|
|
const menu = {
|
|
items: mapActionsToMenuItems(filteredActions, currentUrlWithActions, id),
|
|
onClick: (info) => {
|
|
// Find the action by key
|
|
const findAction = (acts, key) => {
|
|
for (const act of acts) {
|
|
if ((act.key || act.name) === key) return act
|
|
if (act.children) {
|
|
const found = findAction(act.children, key)
|
|
if (found) return found
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
const action = findAction(filteredActions, info.key)
|
|
if (action && action.url) {
|
|
navigate(action.url(id))
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Dropdown menu={menu} {...dropdownProps}>
|
|
<Button {...buttonProps} disabled={disabled}>
|
|
Actions
|
|
</Button>
|
|
</Dropdown>
|
|
)
|
|
}
|
|
|
|
ObjectActions.propTypes = {
|
|
type: PropTypes.string.isRequired,
|
|
id: PropTypes.string.isRequired,
|
|
disabled: PropTypes.bool,
|
|
buttonProps: PropTypes.object,
|
|
buttonLabel: PropTypes.string,
|
|
visibleActions: PropTypes.object
|
|
}
|
|
|
|
export default ObjectActions
|