summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts
blob: 0497f930193a726103a3d94d34057520b68d26f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';

import _ from 'lodash';

import { Icons } from '~/app/shared/enum/icons.enum';
import { CdTableAction } from '~/app/shared/models/cd-table-action';
import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
import { Permission } from '~/app/shared/models/permissions';

@Component({
  selector: 'cd-table-actions',
  templateUrl: './table-actions.component.html',
  styleUrls: ['./table-actions.component.scss']
})
export class TableActionsComponent implements OnChanges, OnInit {
  @Input()
  permission: Permission;
  @Input()
  selection: CdTableSelection;
  @Input()
  tableActions: CdTableAction[];
  @Input()
  btnColor = 'accent';

  // Use this if you just want to display a drop down button,
  // labeled with the given text, with all actions in it.
  // This disables the main action button.
  @Input()
  dropDownOnly?: string;

  currentAction?: CdTableAction;
  // Array with all visible actions
  dropDownActions: CdTableAction[] = [];

  icons = Icons;

  ngOnInit() {
    this.removeActionsWithNoPermissions();
    this.onSelectionChange();
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes.selection) {
      this.onSelectionChange();
    }
  }

  onSelectionChange(): void {
    this.updateDropDownActions();
    this.updateCurrentAction();
  }

  toClassName(action: CdTableAction): string {
    return action.name
      .replace(/ /g, '-')
      .replace(/[^a-z-]/gi, '')
      .toLowerCase();
  }

  /**
   * Removes all actions from 'tableActions' that need a permission the user doesn't have.
   */
  private removeActionsWithNoPermissions() {
    if (!this.permission) {
      this.tableActions = [];
      return;
    }
    const permissions = Object.keys(this.permission).filter((key) => this.permission[key]);
    this.tableActions = this.tableActions.filter((action) =>
      permissions.includes(action.permission)
    );
  }

  private updateDropDownActions(): void {
    this.dropDownActions = this.tableActions.filter((action) =>
      action.visible ? action.visible(this.selection) : action
    );
  }

  /**
   * Finds the next action that is used as main action for the button
   *
   * The order of the list is crucial to get the right main action.
   *
   * Default button conditions of actions:
   * - 'create' actions can be used with no or multiple selections
   * - 'update' and 'delete' actions can be used with one selection
   */
  private updateCurrentAction(): void {
    if (this.dropDownOnly) {
      this.currentAction = undefined;
      return;
    }
    let buttonAction = this.dropDownActions.find((tableAction) => this.showableAction(tableAction));
    if (!buttonAction && this.dropDownActions.length > 0) {
      buttonAction = this.dropDownActions[0];
    }
    this.currentAction = buttonAction;
  }

  /**
   * Determines if action can be used for the button
   *
   * @param {CdTableAction} action
   * @returns {boolean}
   */
  private showableAction(action: CdTableAction): boolean {
    const condition = action.canBePrimary;
    const singleSelection = this.selection.hasSingleSelection;
    const defaultCase = action.permission === 'create' ? !singleSelection : singleSelection;
    return (condition && condition(this.selection)) || (!condition && defaultCase);
  }

  useRouterLink(action: CdTableAction): string {
    if (!action.routerLink || this.disableSelectionAction(action)) {
      return undefined;
    }
    return _.isString(action.routerLink) ? action.routerLink : action.routerLink();
  }

  /**
   * Determines if an action should be disabled
   *
   * Default disable conditions of 'update' and 'delete' actions:
   * - If no or multiple selections are made
   * - If one selection is made, but a task is executed on that item
   *
   * @param {CdTableAction} action
   * @returns {Boolean}
   */
  disableSelectionAction(action: CdTableAction): Boolean {
    const disable = action.disable;
    if (disable) {
      return Boolean(disable(this.selection));
    }
    const permission = action.permission;
    const selected = this.selection.hasSingleSelection && this.selection.first();
    return Boolean(
      ['update', 'delete'].includes(permission) && (!selected || selected.cdExecuting)
    );
  }

  useClickAction(action: CdTableAction) {
    /**
     * In order to show tooltips for deactivated menu items, the class
     * 'pointer-events: auto;' has been added to the .scss file which also
     * re-activates the click-event.
     * To prevent calling the click-event on deactivated elements we also have
     * to check here if it's disabled.
     */
    return !this.disableSelectionAction(action) && action.click && action.click();
  }

  useDisableDesc(action: CdTableAction) {
    if (action.disable) {
      const result = action.disable(this.selection);
      return _.isString(result) ? result : undefined;
    }
    return undefined;
  }
}