185 lines
4.8 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'
import { useActionsModal } from '../context/ActionsModalContext'
import KeyboardShortcut from './KeyboardShortcut'
// 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, objectData) {
return actions.map((action) => {
if (action.type === 'divider') {
return { type: 'divider' }
}
const actionUrl = action.url ? action.url(id) : undefined
var disabled = actionUrl && actionUrl === currentUrlWithActions
var visible = true
if (action.disabled) {
if (typeof action.disabled === 'function') {
disabled = action.disabled(objectData)
} else {
disabled = action.disabled
}
}
if (action.visible) {
if (typeof action.visible === 'function') {
visible = action.visible(objectData)
} else {
visible = action.visible
}
}
const item = {
key: action.key || action.name,
label: action.label,
danger: action?.danger || false,
icon: action.icon ? createElement(action.icon) : undefined,
disabled
}
if (action.children && Array.isArray(action.children)) {
item.children = mapActionsToMenuItems(
action.children,
currentUrlWithActions,
id,
objectData
)
}
if (visible == true) {
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,
objectData,
disabled = false,
buttonProps = {},
visibleActions = {},
...dropdownProps
}) => {
const model = getModelByName(type)
const actions = model.actions || []
const navigate = useNavigate()
const location = useLocation()
const { showActionsModal } = useActionsModal()
// 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,
objectData
),
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 (
<KeyboardShortcut
shortcut='alt+a'
onTrigger={() => showActionsModal(id, type, objectData)}
>
<Dropdown menu={menu} {...dropdownProps}>
<Button
{...buttonProps}
disabled={disabled}
onClick={() => showActionsModal(id, type, objectData)}
>
Actions
</Button>
</Dropdown>
</KeyboardShortcut>
)
}
ObjectActions.propTypes = {
type: PropTypes.string.isRequired,
objectData: PropTypes.object.isRequired,
id: PropTypes.string.isRequired,
disabled: PropTypes.bool,
buttonProps: PropTypes.object,
buttonLabel: PropTypes.string,
visibleActions: PropTypes.object
}
export default ObjectActions