import { AnyCommand, LimeObjectBulkCommand, LimeObjectCommand } from '../commandbus'; import { Condition } from '../conditionregistry'; import { Icon } from '../core'; import { Rule } from '../ruleregistry'; /** * Represents a user-triggerable action that can be displayed in various UI contexts. * * Actions are the primary way to expose functionality to users through the UI. * They can appear as: * - Toolbar buttons * - Dropdown menu options * - Quick action buttons * * Each action wraps a command that executes when the action is triggered. * Actions can be conditionally shown or hidden using {@link Condition}s, and they * support rich metadata like icons, labels, and descriptions for better UX. * * The generic type parameter `T` allows you to specify the exact command type, * enabling type-safe action definitions for specific contexts like single objects * or bulk operations. * * @example Basic action with icon and label * ```ts * import { Action, Command } from '@limetech/lime-web-components'; * * @Command({ id: 'save-record' }) * class SaveRecordCommand {} * * @Command({ id: 'delete-record' }) * class DeleteRecordCommand {} * * const actions: Action[] = [ * { * label: 'Save', * icon: 'save', * command: new SaveRecordCommand(), * }, * { * label: 'Delete', * icon: 'trash', * command: new DeleteRecordCommand(), * }, * ]; * ``` * * @see {@link LimeObjectAction} for single-object actions * @see {@link LimeObjectBulkAction} for bulk actions * @see {@link AnyCommand} for command types * @see {@link Condition} for conditional visibility * * @beta * @group Actions */ export interface Action { /** * The command to execute when the action is activated. * * This command will be dispatched to the {@link CommandBus} when the user * triggers the action. The command type determines what operation will be * performed (e.g., save, delete, export, send email). * * @see {@link AnyCommand} * @see {@link LimeObjectCommand} * @see {@link LimeObjectBulkCommand} */ command: T; /** * Optional condition that determines whether the action is available. * * If provided, the condition's evaluate function will be called to determine * if the action should be shown or enabled. If the condition evaluates to * `false`, the action is typically hidden from the UI. * * This is particularly useful for: * - Showing actions only for objects in specific states * - Permission-based action visibility * - Context-dependent action availability * * @example Hide a delete action while the record is locked * ```ts * import { * Command, * LimeObjectAction, * LimeObjectCommand, * LimeObjectCondition, * LimeObjectContext, * } from '@limetech/lime-web-components'; * * @Command({ id: 'delete-object' }) * class DeleteObjectCommand implements LimeObjectCommand { * public context: LimeObjectContext; * } * * const canDelete: LimeObjectCondition = { * id: 'record.can-delete', * type: 'limeobject', * evaluate: (object) => !object.getValue('locked'), * }; * * const deleteCommand = new DeleteObjectCommand(); * deleteCommand.context = { limetype: 'deal', id: 1234 }; * * const deleteAction: LimeObjectAction = { * label: 'Delete', * command: deleteCommand, * condition: canDelete, * }; * ``` * * @see {@link Condition} * * @deprecated Use {@link Action.rules | rules.visibility} instead. When * both are set, action-menu surfaces evaluate `rules.visibility` and * ignore `condition`. Migrating an action to `rules.visibility` for * visibility requires the `useRuleVisibility` feature switch to be * enabled. While it is off, surfaces keep evaluating `condition`. */ condition?: Condition; /** * Optional behavior rules. * * @see {@link Rule} * * @beta */ rules?: { /** * Rule that gates whether the action is visible. * * @remarks * `rules.visibility` only gates action visibility while the * `useRuleVisibility` feature switch is enabled. While it is off, * surfaces fall back to the legacy `condition`. * * If both `rules.visibility` and the legacy `condition` are set * on the same action, action-menu surfaces evaluate * `rules.visibility` and ignore `condition`. * * @example Show a delete action only to an admin, and only while the deal is open * ```ts * import { * Command, * LimeObjectAction, * LimeObjectCommand, * LimeObjectContext, * } from '@limetech/lime-web-components'; * * @Command({ id: 'delete-object' }) * class DeleteObjectCommand implements LimeObjectCommand { * public context: LimeObjectContext; * } * * const deleteCommand = new DeleteObjectCommand(); * deleteCommand.context = { limetype: 'deal', id: 1234 }; * * const deleteAction: LimeObjectAction = { * label: 'Delete', * command: deleteCommand, * rules: { * visibility: { * type: 'all', * rules: [ * { * type: 'ref', * id: 'limetech.user-has-role', * args: { roles: ['admin'] }, * }, * { type: 'ref', id: 'mycorp.deal-is-open' }, * ], * }, * }, * }; * ``` */ visibility?: Rule; }; /** * Icon to display with the action. * * Can be either a string referencing an icon name from the platform's icon * library, or an {@link Icon} object with additional properties like color. * * Icons help users quickly identify actions and improve visual scannability * of action menus and toolbars. * * @see {@link Icon} */ icon?: string | Icon; /** * Controls whether the action's label should be hidden in the UI. * * When `true`, only the icon is displayed, creating a more compact interface. * However, the label should still be provided for: * - Accessibility (screen readers) * - Tooltips on hover * - Alternative display contexts (e.g., dropdown menus) * * Icon-only actions work best in toolbars where space is limited and icons * are easily recognizable. * * @example Show only the icon in a toolbar * ```ts * import { Action, Command } from '@limetech/lime-web-components'; * * @Command({ id: 'send-email' }) * class SendEmailCommand { * public subject: string; * } * * // The label is still set, so screen readers and tooltips have text to read. * const sendEmailAction: Action = { * label: 'Send Email', * icon: 'envelope', * hideLabel: true, * command: new SendEmailCommand(), * }; * ``` */ hideLabel?: boolean; /** * Display label for the action. * * This text is shown to the user in menus, buttons, or tooltips. The label * is automatically translated using the platform's {@link Translator} service, * so you should provide translation keys rather than hardcoded strings. * * Even if `hideLabel` is true, you should still provide a label for * accessibility and alternative display contexts. * * @see {@link Translator} */ label?: string; /** * Additional descriptive text explaining what the action does. * * Descriptions provide more context than labels alone and are typically * displayed in: * - Tooltips on hover * - Help text in configuration dialogs * - Extended menu descriptions * * Use descriptions to clarify the action's purpose or any side effects. * * @example Describe what an export action does * ```ts * import { Action, Command } from '@limetech/lime-web-components'; * * @Command({ id: 'export-excel' }) * class ExportExcelCommand {} * * const exportAction: Action = { * label: 'Export to Excel', * description: 'Download all selected records as an Excel spreadsheet', * command: new ExportExcelCommand(), * }; * ``` */ description?: string; /** * Controls whether the action is disabled (grayed out but visible). * * When `true`, the action is shown in the UI but cannot be triggered. * This is different from hiding the action entirely (using `condition`). * * Use disabled state to: * - Indicate that an action exists but is temporarily unavailable * - Show what actions would be available in different contexts * - Provide better user feedback than simply hiding the action * * @example Disable a save action until there is something to save * ```ts * import { Action, Command } from '@limetech/lime-web-components'; * * @Command({ id: 'save' }) * class SaveCommand {} * * class MyComponent { * private hasUnsavedChanges = false; * * private getSaveAction(): Action { * return { * label: 'Save', * icon: 'save', * command: new SaveCommand(), * disabled: !this.hasUnsavedChanges, * }; * } * } * ``` */ disabled?: boolean; } /** * Specialized action type for operations on a single {@link LimeObject}. * * LimeObject actions are used in contexts where the user interacts with a single * business object instance (deal, contact, company, etc.). * * The command associated with this action type is {@link LimeObjectCommand}, * which provides type-safe access to object-specific payload properties. * * @example Actions for a contact detail view * ```ts * import { * Command, * LimeObjectAction, * LimeObjectCommand, * LimeObjectContext, * } from '@limetech/lime-web-components'; * * @Command({ id: 'send-email' }) * class SendEmailCommand implements LimeObjectCommand { * public context: LimeObjectContext; * } * * @Command({ id: 'schedule-meeting' }) * class ScheduleMeetingCommand implements LimeObjectCommand { * public context: LimeObjectContext; * } * * @Command({ id: 'add-to-campaign' }) * class AddToCampaignCommand implements LimeObjectCommand { * public context: LimeObjectContext; * } * * const contactContext: LimeObjectContext = { limetype: 'contact', id: 42 }; * * const sendEmailCommand = new SendEmailCommand(); * sendEmailCommand.context = contactContext; * * const scheduleMeetingCommand = new ScheduleMeetingCommand(); * scheduleMeetingCommand.context = contactContext; * * const addToCampaignCommand = new AddToCampaignCommand(); * addToCampaignCommand.context = contactContext; * * const actions: LimeObjectAction[] = [ * { * label: 'Send Email', * icon: 'envelope', * command: sendEmailCommand, * }, * { * label: 'Schedule Meeting', * icon: 'calendar', * command: scheduleMeetingCommand, * }, * { * label: 'Add to Campaign', * icon: 'bullhorn', * command: addToCampaignCommand, * }, * ]; * ``` * * @example Conditional action based on object state * ```ts * import { * Command, * LimeObjectAction, * LimeObjectCommand, * LimeObjectCondition, * LimeObjectContext, * } from '@limetech/lime-web-components'; * * @Command({ id: 'close-deal' }) * class CloseDealCommand implements LimeObjectCommand { * public context: LimeObjectContext; * } * * const isDealOpen: LimeObjectCondition = { * id: 'deal.is-open', * type: 'limeobject', * evaluate: (deal) => { * const status = deal.getValue('dealstatus'); * * return status !== 'won' && status !== 'lost'; * }, * }; * * const closeDealCommand = new CloseDealCommand(); * closeDealCommand.context = { limetype: 'deal', id: 1234 }; * * const closeDealAction: LimeObjectAction = { * label: 'Close Deal', * icon: 'check-circle', * description: 'Mark this deal as won or lost', * command: closeDealCommand, * condition: isDealOpen, * }; * ``` * * @see {@link Action} for the base action type * @see {@link LimeObjectCommand} for the command type * @see {@link LimeObject} for the object structure * * @beta * @group Actions */ export type LimeObjectAction = Action; /** * Specialized action type for bulk operations on multiple {@link LimeObject}s. * * Bulk actions are used when the user selects multiple objects and wants to * perform the same operation on all of them. These actions typically appear in: * - List view toolbars (when items are selected) * - Bulk action menus * - Selection context menus * * The command associated with this action type is {@link LimeObjectBulkCommand}, * which uses a filter expression to specify which objects to operate on. * * Common use cases include: * - Mass updates (change status, assign owner) * - Bulk exports (download selected records) * - Batch operations (send emails, delete records) * * @example Bulk actions for a list view * ```ts * import { * Command, * Expression, * LimeObjectBulkAction, * LimeObjectBulkCommand, * LimeTypeContext, * Operator, * } from '@limetech/lime-web-components'; * * @Command({ id: 'bulk-export' }) * class BulkExportCommand implements LimeObjectBulkCommand { * public context: LimeTypeContext; * public filter: Expression | null; * } * * @Command({ id: 'bulk-delete' }) * class BulkDeleteCommand implements LimeObjectBulkCommand { * public context: LimeTypeContext; * public filter: Expression | null; * } * * @Command({ id: 'bulk-send-email' }) * class BulkSendEmailCommand implements LimeObjectBulkCommand { * public context: LimeTypeContext; * public filter: Expression | null; * } * * const contacts: LimeTypeContext = { limetype: 'contact', id: null }; * const selectedObjects: Expression = { * key: 'id', * op: Operator.IN, * exp: [1, 2, 3], * }; * * const exportCommand = new BulkExportCommand(); * exportCommand.context = contacts; * exportCommand.filter = selectedObjects; * * const deleteCommand = new BulkDeleteCommand(); * deleteCommand.context = contacts; * deleteCommand.filter = selectedObjects; * * const sendEmailCommand = new BulkSendEmailCommand(); * sendEmailCommand.context = contacts; * sendEmailCommand.filter = selectedObjects; * * const bulkActions: LimeObjectBulkAction[] = [ * { * label: 'Export to Excel', * icon: 'file-excel', * description: 'Download selected records as Excel spreadsheet', * command: exportCommand, * }, * { * label: 'Delete Selected', * icon: 'trash', * description: 'Permanently delete all selected records', * command: deleteCommand, * }, * { * label: 'Send Email', * icon: 'envelope', * description: 'Send email to all selected contacts', * command: sendEmailCommand, * }, * ]; * ``` * * @example Bulk action that stays disabled until something is selected * ```ts * import { * Command, * Expression, * LimeObjectBulkAction, * LimeObjectBulkCommand, * LimeTypeContext, * } from '@limetech/lime-web-components'; * * @Command({ id: 'bulk-update-status' }) * class BulkUpdateStatusCommand implements LimeObjectBulkCommand { * public context: LimeTypeContext; * public filter: Expression | null; * public status: string; * } * * class DealListToolbar { * // `null` while nothing is selected, which is also what the bulk command * // reads as "every object of the limetype". * private selectedObjects: Expression | null = null; * * private getUpdateStatusAction(): LimeObjectBulkAction { * const command = new BulkUpdateStatusCommand(); * command.context = { limetype: 'deal', id: null }; * command.filter = this.selectedObjects; * * return { * label: 'Update Status', * icon: 'edit', * description: 'Change status for all selected records', * command: command, * disabled: !this.selectedObjects, * }; * } * } * ``` * * @see {@link Action} for the base action type * @see {@link LimeObjectBulkCommand} for the command type * @see {@link LimeObject} for the object structure * * @beta * @group Actions */ export type LimeObjectBulkAction = Action;