UNPKG

react-antd-admin-panel

Version:

Modern TypeScript-first React admin panel builder with Ant Design 6

1 lines 18 kB
{"version":3,"file":"ActionButton-DWH3-bPK.cjs","sources":["../src/action/Action.ts","../src/action/ActionButton.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\r\nimport type { ButtonType } from 'antd/es/button';\r\n\r\n/**\r\n * Action types\r\n */\r\nexport type ActionType = 'submit' | 'callback' | 'link' | 'reset';\r\n\r\n/**\r\n * Confirmation dialog configuration\r\n */\r\nexport interface ActionConfirm {\r\n /** Confirmation dialog title */\r\n title?: string;\r\n /** Confirmation dialog content/message */\r\n content: string;\r\n /** OK button text */\r\n okText?: string;\r\n /** Cancel button text */\r\n cancelText?: string;\r\n /** Danger mode (red OK button) */\r\n danger?: boolean;\r\n}\r\n\r\n/**\r\n * Access requirement for the action\r\n */\r\nexport interface ActionAccess {\r\n /** Required role */\r\n role?: string;\r\n /** Required roles (any match) */\r\n roles?: string[];\r\n /** Required permission */\r\n permission?: string;\r\n /** Required feature */\r\n feature?: string;\r\n /** Required feature level */\r\n level?: number;\r\n}\r\n\r\n/**\r\n * Action - Clickable action builder with confirmation and callbacks\r\n * \r\n * Defines an action that can be triggered by user interaction.\r\n * Supports confirmation dialogs, loading states, and callbacks.\r\n * \r\n * @example\r\n * ```typescript\r\n * // Submit action with confirmation\r\n * const saveAction = new Action()\r\n * .key('save')\r\n * .label('Save Changes')\r\n * .icon(<SaveOutlined />)\r\n * .buttonType('primary')\r\n * .confirm('Are you sure you want to save?')\r\n * .onComplete(() => message.success('Saved!'))\r\n * .onError(() => message.error('Failed'));\r\n * \r\n * // Callback action\r\n * const deleteAction = new Action()\r\n * .key('delete')\r\n * .label('Delete')\r\n * .buttonType('primary')\r\n * .danger(true)\r\n * .confirm({ content: 'Delete this item?', danger: true })\r\n * .callback(async (action) => {\r\n * await api.delete(itemId);\r\n * });\r\n * \r\n * // Link action\r\n * const viewAction = new Action()\r\n * .key('view')\r\n * .label('View Details')\r\n * .type('link')\r\n * .route('/users/123');\r\n * ```\r\n */\r\nexport class Action {\r\n private _key?: string;\r\n private _label?: string;\r\n private _icon?: ReactNode;\r\n private _tooltip?: string;\r\n private _type: ActionType = 'submit';\r\n private _buttonType: ButtonType = 'default';\r\n private _danger = false;\r\n private _disabled = false;\r\n private _loading = false;\r\n private _confirm?: ActionConfirm;\r\n private _route?: string;\r\n private _access?: ActionAccess;\r\n \r\n // Callbacks\r\n private _callback?: (action: Action, args?: unknown) => void | Promise<void>;\r\n private _onComplete?: (result?: unknown) => void;\r\n private _onError?: (error: Error) => void;\r\n\r\n /**\r\n * Set action key/identifier\r\n */\r\n key(value: string): this {\r\n this._key = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get action key\r\n */\r\n getKey(): string | undefined {\r\n return this._key;\r\n }\r\n\r\n /**\r\n * Set action label\r\n */\r\n label(value: string): this {\r\n this._label = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get action label\r\n */\r\n getLabel(): string | undefined {\r\n return this._label;\r\n }\r\n\r\n /**\r\n * Set action icon\r\n */\r\n icon(value: ReactNode): this {\r\n this._icon = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get action icon\r\n */\r\n getIcon(): ReactNode {\r\n return this._icon;\r\n }\r\n\r\n /**\r\n * Set tooltip text (also used as aria-label for icon-only buttons)\r\n */\r\n tooltip(value: string): this {\r\n this._tooltip = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get tooltip text\r\n */\r\n getTooltip(): string | undefined {\r\n return this._tooltip;\r\n }\r\n\r\n /**\r\n * Set action type\r\n */\r\n type(value: ActionType): this {\r\n this._type = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get action type\r\n */\r\n getType(): ActionType {\r\n return this._type;\r\n }\r\n\r\n /**\r\n * Set button type (Ant Design ButtonType)\r\n */\r\n buttonType(value: ButtonType): this {\r\n this._buttonType = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get button type\r\n */\r\n getButtonType(): ButtonType {\r\n return this._buttonType;\r\n }\r\n\r\n /**\r\n * Set danger mode\r\n */\r\n danger(value = true): this {\r\n this._danger = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Check if danger mode\r\n */\r\n isDanger(): boolean {\r\n return this._danger;\r\n }\r\n\r\n /**\r\n * Set disabled state\r\n */\r\n disabled(value = true): this {\r\n this._disabled = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Check if disabled\r\n */\r\n isDisabled(): boolean {\r\n return this._disabled;\r\n }\r\n\r\n /**\r\n * Set loading state\r\n */\r\n loading(value = true): this {\r\n this._loading = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Check if loading\r\n */\r\n isLoading(): boolean {\r\n return this._loading;\r\n }\r\n\r\n /**\r\n * Set confirmation dialog (string shorthand or full config)\r\n */\r\n confirm(value: string | ActionConfirm): this {\r\n this._confirm = typeof value === 'string' \r\n ? { content: value }\r\n : value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get confirmation config\r\n */\r\n getConfirm(): ActionConfirm | undefined {\r\n return this._confirm;\r\n }\r\n\r\n /**\r\n * Set route for link type actions\r\n */\r\n route(value: string): this {\r\n this._route = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get route\r\n */\r\n getRoute(): string | undefined {\r\n return this._route;\r\n }\r\n\r\n /**\r\n * Set access requirements\r\n */\r\n access(value: ActionAccess): this {\r\n this._access = value;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get access requirements\r\n */\r\n getAccess(): ActionAccess | undefined {\r\n return this._access;\r\n }\r\n\r\n /**\r\n * Set callback function for 'callback' type actions\r\n */\r\n callback(fn: (action: Action, args?: unknown) => void | Promise<void>): this {\r\n this._type = 'callback';\r\n this._callback = fn;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get callback function\r\n */\r\n getCallback(): ((action: Action, args?: unknown) => void | Promise<void>) | undefined {\r\n return this._callback;\r\n }\r\n\r\n /**\r\n * Set completion callback\r\n */\r\n onComplete(fn: (result?: unknown) => void): this {\r\n this._onComplete = fn;\r\n return this;\r\n }\r\n\r\n /**\r\n * Call completion callback\r\n */\r\n callComplete(result?: unknown): void {\r\n this._onComplete?.(result);\r\n }\r\n\r\n /**\r\n * Set error callback\r\n */\r\n onError(fn: (error: Error) => void): this {\r\n this._onError = fn;\r\n return this;\r\n }\r\n\r\n /**\r\n * Call error callback\r\n */\r\n callError(error: Error): void {\r\n this._onError?.(error);\r\n }\r\n\r\n /**\r\n * Execute the action's callback\r\n * For 'callback' type actions\r\n */\r\n async execute(args?: unknown): Promise<void> {\r\n if (this._type !== 'callback' || !this._callback) {\r\n return;\r\n }\r\n\r\n this._loading = true;\r\n try {\r\n await this._callback(this, args);\r\n this._onComplete?.();\r\n } catch (error) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this._onError?.(err);\r\n throw err;\r\n } finally {\r\n this._loading = false;\r\n }\r\n }\r\n\r\n /**\r\n * Clone this action\r\n */\r\n clone(): Action {\r\n const cloned = new Action();\r\n cloned._key = this._key;\r\n cloned._label = this._label;\r\n cloned._icon = this._icon;\r\n cloned._type = this._type;\r\n cloned._buttonType = this._buttonType;\r\n cloned._danger = this._danger;\r\n cloned._disabled = this._disabled;\r\n cloned._confirm = this._confirm;\r\n cloned._route = this._route;\r\n cloned._access = this._access;\r\n cloned._callback = this._callback;\r\n cloned._onComplete = this._onComplete;\r\n cloned._onError = this._onError;\r\n return cloned;\r\n }\r\n}\r\n\r\nexport default Action;","import React, { useState, useCallback } from 'react';\r\nimport { Button, Modal } from 'antd';\r\nimport { ExclamationCircleOutlined } from '@ant-design/icons';\r\nimport { Action } from './Action';\r\nimport { useAccess } from '../hooks/useAccess';\r\nimport type { Formula } from '../formula/Formula';\r\n\r\n/**\r\n * Props for ActionButton component\r\n */\r\nexport interface ActionButtonProps {\r\n /** The Action model to render */\r\n action: Action;\r\n /** Formula to submit (for 'submit' type actions) */\r\n formula?: Formula;\r\n /** Additional arguments to pass to callback */\r\n args?: unknown;\r\n /** Navigate function for 'link' type actions */\r\n navigate?: (path: string) => void;\r\n /** Override disabled state */\r\n disabled?: boolean;\r\n /** Additional className */\r\n className?: string;\r\n /** Button size */\r\n size?: 'small' | 'middle' | 'large';\r\n}\r\n\r\n/**\r\n * ActionButton - Renders an Action as an Ant Design Button\r\n * \r\n * Handles confirmation dialogs, loading states, and access control.\r\n * \r\n * @example\r\n * ```tsx\r\n * const deleteAction = new Action()\r\n * .label('Delete')\r\n * .danger(true)\r\n * .confirm('Are you sure?')\r\n * .callback(async () => {\r\n * await api.deleteUser(id);\r\n * });\r\n * \r\n * <ActionButton action={deleteAction} />\r\n * \r\n * // With formula submission\r\n * const saveAction = new Action()\r\n * .label('Save')\r\n * .buttonType('primary');\r\n * \r\n * <ActionButton action={saveAction} formula={formula} />\r\n * ```\r\n */\r\nexport function ActionButton({\r\n action,\r\n formula,\r\n args,\r\n navigate,\r\n disabled: disabledProp,\r\n className,\r\n size,\r\n}: ActionButtonProps): React.ReactElement | null {\r\n const [loading, setLoading] = useState(false);\r\n const access = useAccess();\r\n\r\n // Check access requirements\r\n const accessConfig = action.getAccess();\r\n const hasAccess = useCallback(() => {\r\n if (!accessConfig) return true;\r\n \r\n if (accessConfig.role && !access.hasRole(accessConfig.role)) {\r\n return false;\r\n }\r\n if (accessConfig.roles && !access.hasAnyRole(accessConfig.roles)) {\r\n return false;\r\n }\r\n if (accessConfig.permission && !access.hasPermission(accessConfig.permission)) {\r\n return false;\r\n }\r\n if (accessConfig.feature && !access.hasFeature(accessConfig.feature, accessConfig.level)) {\r\n return false;\r\n }\r\n return true;\r\n }, [accessConfig, access]);\r\n\r\n // Don't render if no access\r\n if (!hasAccess()) {\r\n return null;\r\n }\r\n\r\n const isDisabled = disabledProp ?? action.isDisabled();\r\n const isLoading = loading || action.isLoading();\r\n const confirmConfig = action.getConfirm();\r\n\r\n const executeAction = async () => {\r\n const actionType = action.getType();\r\n\r\n try {\r\n setLoading(true);\r\n\r\n switch (actionType) {\r\n case 'submit':\r\n if (formula) {\r\n await formula.submit();\r\n }\r\n break;\r\n\r\n case 'callback':\r\n await action.execute(args);\r\n break;\r\n\r\n case 'link':\r\n const route = action.getRoute();\r\n if (route && navigate) {\r\n navigate(route);\r\n } else if (route) {\r\n window.location.href = route;\r\n }\r\n break;\r\n\r\n case 'reset':\r\n if (formula) {\r\n formula.reset();\r\n }\r\n break;\r\n }\r\n } catch {\r\n // Error already handled by callbacks in Action/Formula\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n const handleClick = () => {\r\n if (confirmConfig) {\r\n Modal.confirm({\r\n title: confirmConfig.title || 'Confirm',\r\n icon: <ExclamationCircleOutlined />,\r\n content: confirmConfig.content,\r\n okText: confirmConfig.okText || 'OK',\r\n cancelText: confirmConfig.cancelText || 'Cancel',\r\n okButtonProps: confirmConfig.danger ? { danger: true } : undefined,\r\n onOk: executeAction,\r\n });\r\n } else {\r\n executeAction();\r\n }\r\n };\r\n\r\n return (\r\n <Button\r\n type={action.getButtonType()}\r\n danger={action.isDanger()}\r\n disabled={isDisabled}\r\n loading={isLoading}\r\n icon={action.getIcon()}\r\n onClick={handleClick}\r\n className={className}\r\n size={size}\r\n aria-label={!action.getLabel() && action.getIcon() ? action.getTooltip() || 'Action button' : undefined}\r\n >\r\n {action.getLabel()}\r\n </Button>\r\n );\r\n}\r\n\r\nexport default ActionButton;"],"names":["useState","useAccess","useCallback","Modal","ExclamationCircleOutlined","jsx","Button"],"mappings":"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"}