dashboard-shell-shell 1.0.1000000117 → 1.0.1000000118

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/assets/styles/base/_functions.scss +0 -0
  2. package/assets/styles/base/_mixins.scss +1 -1
  3. package/assets/styles/global/_button.scss +10 -17
  4. package/assets/styles/global/_form.scss +2 -2
  5. package/assets/styles/global/_labeled-input.scss +2 -6
  6. package/assets/styles/global/_select.scss +7 -6
  7. package/assets/styles/global/_table.scss +2 -3
  8. package/assets/styles/global/_tooltip.scss +1 -8
  9. package/assets/styles/themes/_dark.scss +0 -2
  10. package/assets/styles/themes/_light.scss +2 -5
  11. package/assets/styles/vendor/vue-select.scss +1 -2
  12. package/assets/translations/en-us.yaml +3 -1
  13. package/assets/translations/zh-hans.yaml +28 -51
  14. package/components/ActionDropdown.vue +0 -1
  15. package/components/ActionMenuShell.vue +3 -6
  16. package/components/BrandImage.vue +0 -22
  17. package/components/ClusterIconMenu.vue +1 -1
  18. package/components/CodeMirror.vue +0 -1
  19. package/components/CruResource.vue +1 -1
  20. package/components/CruResourceFooter.vue +1 -1
  21. package/components/ExplorerProjectsNamespaces.vue +24 -4
  22. package/components/GlobalRoleBindings.vue +48 -112
  23. package/components/IndentedPanel.vue +10 -4
  24. package/components/PromptRemove.vue +3 -3
  25. package/components/ResourceDetail/Masthead.vue +242 -190
  26. package/components/ResourceDetail/index.vue +5 -20
  27. package/components/ResourceList/Masthead.vue +84 -146
  28. package/components/ResourceList/ResourceLoadingIndicator.vue +2 -5
  29. package/components/ResourceTable.vue +1 -76
  30. package/components/SideNav.vue +29 -66
  31. package/components/SortableTable/THead.vue +0 -6
  32. package/components/SortableTable/index.vue +388 -481
  33. package/components/Tabbed/index.vue +5 -4
  34. package/components/auth/Principal.vue +2 -3
  35. package/components/auth/RoleDetailEdit.vue +5 -58
  36. package/components/auth/SelectPrincipal.vue +0 -1
  37. package/components/form/BannerSettings.vue +16 -18
  38. package/components/form/ChangePassword.vue +4 -4
  39. package/components/form/ColorInput.vue +8 -32
  40. package/components/form/Footer.vue +1 -1
  41. package/components/form/InputWithSelect.vue +0 -2
  42. package/components/form/KeyValue.vue +7 -31
  43. package/components/form/LabeledSelect.vue +178 -178
  44. package/components/form/Members/ClusterPermissionsEditor.vue +2 -1
  45. package/components/form/Members/MembershipEditor.vue +1 -1
  46. package/components/form/NameNsDescription.vue +11 -24
  47. package/components/form/Password.vue +2 -6
  48. package/components/form/ResourceQuota/Namespace.vue +1 -1
  49. package/components/form/ResourceQuota/NamespaceRow.vue +10 -13
  50. package/components/form/ResourceQuota/ProjectRow.vue +1 -0
  51. package/components/form/Select.vue +2 -2
  52. package/components/nav/Favorite.vue +1 -5
  53. package/components/nav/Group.vue +23 -69
  54. package/components/nav/Header.vue +17 -82
  55. package/components/nav/HeaderPageActionMenu.vue +0 -1
  56. package/components/nav/NamespaceFilter.vue +3 -0
  57. package/components/nav/TopLevelMenu.vue +119 -182
  58. package/components/nav/Type.vue +11 -48
  59. package/components/rancherResourceDetail/Masthead.vue +769 -0
  60. package/components/rancherResourceDetail/__tests__/Masthead.test.ts +65 -0
  61. package/components/rancherResourceDetail/index.vue +591 -0
  62. package/components/rancherResourceList/Masthead.vue +375 -0
  63. package/components/rancherResourceList/ResourceLoadingIndicator.vue +140 -0
  64. package/components/rancherResourceList/index.vue +307 -0
  65. package/components/rancherResourceList/resource-list.config.js +7 -0
  66. package/components/rancherResourceTable.vue +783 -0
  67. package/components/rancherSortableTable/THead.vue +561 -0
  68. package/components/rancherSortableTable/actions.js +153 -0
  69. package/components/rancherSortableTable/advanced-filtering.js +272 -0
  70. package/components/rancherSortableTable/debug.js +117 -0
  71. package/components/rancherSortableTable/filtering.js +290 -0
  72. package/components/rancherSortableTable/grouping.js +48 -0
  73. package/components/rancherSortableTable/index.vue +2712 -0
  74. package/components/rancherSortableTable/paging.js +155 -0
  75. package/components/rancherSortableTable/selection.js +629 -0
  76. package/components/rancherSortableTable/sortable-config.ts +4 -0
  77. package/components/rancherSortableTable/sorting.js +129 -0
  78. package/composables/useClickOutside.ts +1 -1
  79. package/config/product/auth.js +7 -16
  80. package/config/product/explorer.js +1 -1
  81. package/config/product/settings.js +8 -17
  82. package/config/settings.ts +0 -28
  83. package/edit/management.cattle.io.user.vue +4 -17
  84. package/edit/networking.k8s.io.ingress/RulePath.vue +1 -1
  85. package/edit/token.vue +1 -1
  86. package/list/harvesterhci.io.management.cluster.vue +0 -17
  87. package/list/management.cattle.io.setting.vue +13 -22
  88. package/list/management.cattle.io.user.vue +14 -25
  89. package/list/provisioning.cattle.io.cluster.vue +7 -6
  90. package/mixins/brand.js +0 -17
  91. package/package.json +1 -1
  92. package/pages/auth/login.vue +29 -84
  93. package/pages/c/_cluster/auth/roles/index.vue +14 -61
  94. package/pages/c/_cluster/settings/banners.vue +101 -174
  95. package/pages/c/_cluster/settings/brand.vue +301 -348
  96. package/pages/c/_cluster/settings/performance.vue +38 -61
  97. package/pages/home.vue +21 -70
  98. package/pages/prefs.vue +23 -25
  99. package/pkg/tsconfig.json +9 -9
  100. package/pkg/vue.config.js +1 -1
  101. package/promptRemove/mixin/roleDeletionCheck.js +2 -2
  102. package/scripts/clean +0 -0
  103. package/scripts/extension/bundle +0 -0
  104. package/scripts/extension/helm/scripts/package +0 -0
  105. package/scripts/extension/helm/scripts/patch +0 -0
  106. package/scripts/extension/helm/scripts/version +0 -0
  107. package/scripts/extension/helmpatch +0 -0
  108. package/scripts/extension/parse-tag-name +0 -0
  109. package/scripts/extension/publish +0 -0
  110. package/scripts/publish-shell.sh +60 -86
  111. package/scripts/serve-pkgs +0 -0
  112. package/scripts/sync-shell-deps +0 -0
  113. package/scripts/typegen.sh +28 -44
  114. package/store/i18n.js +5 -5
  115. package/store/prefs.js +5 -17
  116. package/store/type-map.js +1 -2
  117. package/types/cloud-shell/index.d.ts +11014 -0
  118. package/types/shell/index.d.ts +1 -1
  119. package/utils/error.js +0 -4
  120. package/utils/router.js +3 -3
  121. package/vue.config.js +6 -1
  122. package/assets/images/action.svg +0 -6
  123. package/assets/images/pl/logo.png +0 -0
  124. /package/components/{ResourceList → rancherResourceList}/Masthead-btn.vue +0 -0
@@ -0,0 +1,629 @@
1
+ import { mapGetters } from 'vuex';
2
+ import { isMore, isRange, suppressContextMenu, isAlternate } from '@shell/utils/platform';
3
+ import { get } from '@shell/utils/object';
4
+ import { filterBy } from '@shell/utils/array';
5
+ import { getParent } from '@shell/utils/dom';
6
+
7
+ export const ALL = 'all';
8
+ export const SOME = 'some';
9
+ export const NONE = 'none';
10
+
11
+ export default {
12
+ mounted() {
13
+ const table = this.$el.querySelector('TABLE');
14
+
15
+ this._onRowClickBound = this.onRowClick.bind(this);
16
+ this._onRowMousedownBound = this.onRowMousedown.bind(this);
17
+ this._onRowContextBound = this.onRowContext.bind(this);
18
+
19
+ table.addEventListener('click', this._onRowClickBound);
20
+ table.addEventListener('mousedown', this._onRowMousedownBound);
21
+ table.addEventListener('contextmenu', this._onRowContextBound);
22
+ },
23
+
24
+ beforeUnmount() {
25
+ const table = this.$el.querySelector('TABLE');
26
+
27
+ table.removeEventListener('click', this._onRowClickBound);
28
+ table.removeEventListener('mousedown', this._onRowMousedownBound);
29
+ table.removeEventListener('contextmenu', this._onRowContextBound);
30
+ },
31
+
32
+ computed: {
33
+ ...mapGetters({
34
+ // Use either these Vuex getters
35
+ // OR the props to set the action menu state,
36
+ // but don't use both.
37
+ targetElem: 'action-menu/elem',
38
+ shouldShow: 'action-menu/showing',
39
+ }),
40
+ // Used for the table-level selection check-box to show checked (all selected)/intermediate (some selected)/unchecked (none selected)
41
+ howMuchSelected() {
42
+ const total = this.pagedRows.length;
43
+ const selected = this.selectedRows.length;
44
+
45
+ if ( selected >= total && total > 0 ) {
46
+ return ALL;
47
+ } else if ( selected > 0 ) {
48
+ return SOME;
49
+ }
50
+
51
+ return NONE;
52
+ },
53
+
54
+ // NOTE: The logic here could be simplified and made more performant
55
+ bulkActionsForSelection() {
56
+ let disableAll = false;
57
+
58
+ // pagedRows is all rows in the current page
59
+ const all = this.pagedRows;
60
+ const allRows = this.arrangedRows || all;
61
+ let selected = this.selectedRows;
62
+
63
+ // Nothing is selected
64
+ if ( !this.selectedRows.length ) {
65
+ // and there are no rows
66
+ if ( !allRows ) {
67
+ return [];
68
+ }
69
+
70
+ const firstNode = allRows[0];
71
+
72
+ selected = firstNode ? [firstNode] : [];
73
+ disableAll = true;
74
+ }
75
+
76
+ const map = {};
77
+
78
+ // Find and add all the actions for all the nodes so that we know
79
+ // what all the possible actions are
80
+ for ( const node of all ) {
81
+ if (node.availableActions) {
82
+ for ( const act of node.availableActions ) {
83
+ if ( act.bulkable ) {
84
+ _add(map, act, false);
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ // Go through all the selected items and add the actions (which were already identified above)
91
+ // as available for some (or all) of the selected nodes
92
+ for ( const node of selected ) {
93
+ if (node.availableActions) {
94
+ for ( const act of node.availableActions ) {
95
+ if ( act.bulkable && act.enabled ) {
96
+ _add(map, act, false);
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ // If there's no items actually selected, we want to see all the actions
103
+ // so you know what exists, but have them all be disabled since there's nothing to do them on.
104
+ const out = _filter(map, disableAll);
105
+
106
+ // Enable a bulkaction if some of the selected items can perform the action
107
+ out.forEach((bulkAction) => {
108
+ const actionEnabledForSomeSelected = this.selectedRows.some((node) => {
109
+ const availableActions = node.availableActions || [];
110
+
111
+ return availableActions.some((action) => action.action === bulkAction.action && action.enabled);
112
+ });
113
+
114
+ bulkAction.enabled = this.selectedRows.length > 0 && actionEnabledForSomeSelected;
115
+ });
116
+
117
+ return out.sort((a, b) => (b.weight || 0) - (a.weight || 0));
118
+ }
119
+ },
120
+
121
+ data() {
122
+ return {
123
+ // List of selected items in the table
124
+ selectedRows: [],
125
+ prevNode: null,
126
+ };
127
+ },
128
+
129
+ watch: {
130
+ // On page change
131
+ pagedRows() {
132
+ // When the table contents changes:
133
+ // - Remove items that are in the selection but no longer in the table.
134
+
135
+ const content = this.pagedRows;
136
+ const toRemove = [];
137
+
138
+ for (const node of this.selectedRows) {
139
+ if (!content.includes(node) ) {
140
+ toRemove.push(node);
141
+ }
142
+ }
143
+
144
+ this.update([], toRemove);
145
+ }
146
+ },
147
+
148
+ methods: {
149
+ onToggleAll(value) {
150
+ if ( value ) {
151
+ this.update(this.pagedRows, []);
152
+
153
+ return true;
154
+ } else {
155
+ this.update([], this.pagedRows);
156
+
157
+ return false;
158
+ }
159
+ },
160
+
161
+ onRowMousedown(e) {
162
+ if ( isRange(e) || this.isSelectionCheckbox(e.target) ) {
163
+ e.preventDefault();
164
+ }
165
+ },
166
+
167
+ onRowMouseEnter(e) {
168
+ const tr = e.target.closest('TR');
169
+
170
+ if (tr.classList.contains('sub-row')) {
171
+ const trMainRow = tr.previousElementSibling;
172
+
173
+ trMainRow.classList.add('sub-row-hovered');
174
+ }
175
+ },
176
+
177
+ onRowMouseLeave(e) {
178
+ const tr = e.target.closest('TR');
179
+
180
+ if (tr.classList.contains('sub-row')) {
181
+ const trMainRow = tr.previousElementSibling;
182
+
183
+ trMainRow.classList.remove('sub-row-hovered');
184
+ }
185
+ },
186
+
187
+ nodeForEvent(e) {
188
+ const tagName = e.target.tagName;
189
+ const tgt = e.target;
190
+ const actionElement = tgt.closest('.actions');
191
+
192
+ if ( tgt.classList.contains('select-all-check') ) {
193
+ return;
194
+ }
195
+
196
+ if ( !actionElement ) {
197
+ if (
198
+ tagName === 'A' ||
199
+ tagName === 'BUTTON' ||
200
+ getParent(tgt, '.btn')
201
+ ) {
202
+ return;
203
+ }
204
+ }
205
+
206
+ const tgtRow = e.target.closest('TR');
207
+
208
+ return this.nodeForRow(tgtRow);
209
+ },
210
+
211
+ nodeForRow(tgtRow) {
212
+ if ( tgtRow?.classList.contains('separator-row') ) {
213
+ return;
214
+ }
215
+
216
+ while ( tgtRow && !tgtRow.classList.contains('main-row') ) {
217
+ tgtRow = tgtRow.previousElementSibling;
218
+ }
219
+
220
+ if ( !tgtRow ) {
221
+ return;
222
+ }
223
+
224
+ const nodeId = tgtRow.dataset.nodeId;
225
+
226
+ if ( !nodeId ) {
227
+ return;
228
+ }
229
+
230
+ const node = this.pagedRows.find( (x) => get(x, this.keyField) === nodeId );
231
+
232
+ return node;
233
+ },
234
+
235
+ async onRowClick(e) {
236
+ const node = this.nodeForEvent(e);
237
+ const td = e.target.closest('TD');
238
+ const skipSelect = td?.classList.contains('skip-select');
239
+
240
+ if (skipSelect) {
241
+ return;
242
+ }
243
+ const selection = this.selectedRows;
244
+ const isCheckbox = this.isSelectionCheckbox(e.target) || td?.classList.contains('row-check');
245
+ const isExpand = td?.classList.contains('row-expand');
246
+ const content = this.pagedRows;
247
+
248
+ this.$emit('rowClick', e);
249
+
250
+ if ( !node ) {
251
+ return;
252
+ }
253
+
254
+ if ( isExpand ) {
255
+ this.toggleExpand(node);
256
+
257
+ return;
258
+ }
259
+
260
+ const actionElement = e.target.closest('.actions');
261
+
262
+ if ( actionElement ) {
263
+ let resources = [node];
264
+
265
+ if ( this.mangleActionResources ) {
266
+ const i = actionElement.querySelector('i');
267
+
268
+ i.classList.remove('icon-actions');
269
+ i.classList.add('icon-spinner');
270
+ i.classList.add('icon-spin');
271
+
272
+ try {
273
+ resources = await this.mangleActionResources(resources);
274
+ } finally {
275
+ i.classList.remove('icon-spinner');
276
+ i.classList.remove('icon-spin');
277
+ i.classList.add('icon-actions');
278
+ }
279
+ }
280
+
281
+ if (!this.targetElem && !this.shouldShow) {
282
+ this.$store.commit(`action-menu/show`, {
283
+ resources,
284
+ event: e,
285
+ elem: actionElement
286
+ });
287
+ } else if (this.targetElem === actionElement && this.shouldShow) {
288
+ // this condition is needed so that we can "toggle" the action menu with
289
+ // the keyboard for accessibility (row action menu)
290
+ this.$store.commit('action-menu/hide');
291
+ }
292
+
293
+ return;
294
+ }
295
+
296
+ const isSelected = selection.includes(node);
297
+ let prevNode = this.prevNode;
298
+
299
+ // PrevNode is only valid if it's in the current content
300
+ if ( !prevNode || !content.includes(prevNode) ) {
301
+ prevNode = node;
302
+ }
303
+
304
+ if ( isMore(e) ) {
305
+ this.toggle(node);
306
+ } else if ( isRange(e) ) {
307
+ const toToggle = this.nodesBetween(prevNode, node);
308
+
309
+ if ( isSelected ) {
310
+ this.update([], toToggle);
311
+ } else {
312
+ this.update(toToggle, []);
313
+ }
314
+ } else if ( isCheckbox ) {
315
+ this.toggle(node);
316
+ } else {
317
+ this.update([node], content);
318
+ }
319
+
320
+ this.prevNode = node;
321
+ },
322
+
323
+ async onRowContext(e) {
324
+ const node = this.nodeForEvent(e);
325
+
326
+ if ( suppressContextMenu(e) ) {
327
+ return;
328
+ }
329
+
330
+ if ( !node ) {
331
+ return;
332
+ }
333
+
334
+ e.preventDefault();
335
+ e.stopPropagation();
336
+
337
+ this.prevNode = node;
338
+ const isSelected = this.selectedRows.includes(node);
339
+
340
+ if ( !isSelected ) {
341
+ this.update([node], this.selectedRows.slice());
342
+ }
343
+ },
344
+
345
+ keySelectRow(row, more = false) {
346
+ const node = this.nodeForRow(row);
347
+ const content = this.pagedRows;
348
+
349
+ if ( !node ) {
350
+ return;
351
+ }
352
+
353
+ if ( more ) {
354
+ this.update([node], []);
355
+ } else {
356
+ this.update([node], content);
357
+ }
358
+
359
+ this.prevNode = node;
360
+ },
361
+
362
+ isSelectionCheckbox(element) {
363
+ return element.tagName === 'INPUT' &&
364
+ element.type === 'checkbox' &&
365
+ element.closest('.selection-checkbox') !== null;
366
+ },
367
+
368
+ nodesBetween(a, b) {
369
+ let toToggle = [];
370
+ const key = this.groupBy;
371
+
372
+ if ( key ) {
373
+ // Grouped has 2 levels to look through
374
+ const grouped = this.groupedRows;
375
+
376
+ let from = this.groupIdx(a);
377
+ let to = this.groupIdx(b);
378
+
379
+ if ( !from || !to ) {
380
+ return [];
381
+ }
382
+
383
+ // From has to come before To
384
+ if ( (from.group > to.group) || ((from.group === to.group) && (from.item > to.item)) ) {
385
+ [from, to] = [to, from];
386
+ }
387
+
388
+ for ( let i = from.group ; i <= to.group ; i++ ) {
389
+ const items = grouped[i].rows;
390
+ let j = (from.group === i ? from.item : 0);
391
+
392
+ while ( items[j] && ( i < to.group || j <= to.item )) {
393
+ toToggle.push(items[j]);
394
+ j++;
395
+ }
396
+ }
397
+ } else {
398
+ // Ungrouped is much simpler
399
+ const content = this.pagedRows;
400
+ let from = content.indexOf(a);
401
+ let to = content.indexOf(b);
402
+
403
+ [from, to] = [Math.min(from, to), Math.max(from, to)];
404
+ toToggle = content.slice(from, to + 1);
405
+ }
406
+
407
+ // check if there is already duplicate content selected (selectedRows) on the list to toggle...
408
+ toToggle = toToggle.filter((item) => !this.selectedRows.includes(item));
409
+
410
+ return toToggle;
411
+ },
412
+
413
+ groupIdx(node) {
414
+ const grouped = this.groupedRows;
415
+
416
+ for ( let i = 0 ; i < grouped.length ; i++ ) {
417
+ const rows = grouped[i].rows;
418
+
419
+ for ( let j = 0 ; j < rows.length ; j++ ) {
420
+ if ( rows[j] === node ) {
421
+ return {
422
+ group: i,
423
+ item: j
424
+ };
425
+ }
426
+ }
427
+ }
428
+
429
+ return null;
430
+ },
431
+
432
+ toggle(node) {
433
+ const add = [];
434
+ const remove = [];
435
+
436
+ if (this.selectedRows.includes(node)) {
437
+ remove.push(node);
438
+ } else {
439
+ add.push(node);
440
+ }
441
+
442
+ this.update(add, remove);
443
+ },
444
+
445
+ update(toAdd, toRemove) {
446
+ toRemove.forEach((row) => {
447
+ const index = this.selectedRows.findIndex((r) => r === row);
448
+
449
+ if (index !== -1) {
450
+ this.selectedRows.splice(index, 1);
451
+ }
452
+ });
453
+
454
+ if ( toAdd ) {
455
+ this.selectedRows.push(...toAdd);
456
+ }
457
+
458
+ // Uncheck and check the checkboxes of nodes that have been added/removed
459
+ if (toRemove.length) {
460
+ this.$nextTick(() => {
461
+ for ( let i = 0 ; i < toRemove.length ; i++ ) {
462
+ this.updateInput(toRemove[i], false, this.keyField);
463
+ }
464
+ });
465
+ }
466
+
467
+ if (toAdd.length) {
468
+ this.$nextTick(() => {
469
+ for ( let i = 0 ; i < toAdd.length ; i++ ) {
470
+ this.updateInput(toAdd[i], true, this.keyField);
471
+ }
472
+ });
473
+ }
474
+
475
+ this.$nextTick(() => {
476
+ this.$emit('selection', this.selectedRows);
477
+ });
478
+ },
479
+
480
+ updateInput(node, on, keyField) {
481
+ const id = get(node, keyField);
482
+
483
+ if ( id ) {
484
+ // Note: This is looking for the checkbox control for the row
485
+ const input = this.$el.querySelector(`div[data-checkbox-ctrl][data-node-id="${ id }"]`);
486
+
487
+ if ( input && !input.disabled ) {
488
+ const label = input.querySelector('label');
489
+
490
+ if (label) {
491
+ label.value = on;
492
+ }
493
+ let tr = input.closest('tr');
494
+ let first = true;
495
+
496
+ while ( tr && (first || tr.classList.contains('sub-row') ) ) {
497
+ if (on) {
498
+ tr.classList.add('row-selected');
499
+ } else {
500
+ tr.classList.remove('row-selected');
501
+ }
502
+ tr = tr.nextElementSibling;
503
+ first = false;
504
+ }
505
+ }
506
+ }
507
+ },
508
+
509
+ select(nodes) {
510
+ nodes.forEach((node) => {
511
+ const id = get(node, this.keyField);
512
+ const input = this.$el.querySelector(`label[data-node-id="${ id }"]`);
513
+
514
+ input.dispatchEvent(new Event('click'));
515
+ });
516
+ },
517
+
518
+ applyTableAction(action, args, event) {
519
+ const opts = { alt: event && isAlternate(event), event };
520
+
521
+ // Go through the table selection and filter out those actions that can't run the chosen action
522
+ const executableSelection = this.selectedRows.filter((row) => {
523
+ const matchingResourceAction = row.availableActions.find((a) => a.action === action.action);
524
+
525
+ return matchingResourceAction?.enabled;
526
+ });
527
+
528
+ _execute(executableSelection, action, args, opts, this);
529
+
530
+ this.actionOfInterest = null;
531
+ },
532
+
533
+ clearSelection() {
534
+ this.update([], this.selectedRows);
535
+ },
536
+
537
+ }
538
+ };
539
+
540
+ // ---------------------------------------------------------------------
541
+ // --- Helpers that were in selectionStore.js --------------------------
542
+ // ---------------------------------------------------------------------
543
+
544
+ let anon = 0;
545
+
546
+ function _add(map, act, incrementCounts = true) {
547
+ let id = act.action;
548
+
549
+ if ( !id ) {
550
+ id = `anon${ anon }`;
551
+ anon++;
552
+ }
553
+
554
+ let obj = map[id];
555
+
556
+ if ( !obj ) {
557
+ obj = Object.assign({}, act);
558
+ map[id] = obj;
559
+ obj.allEnabled = false;
560
+ }
561
+
562
+ if ( !act.enabled ) {
563
+ obj.allEnabled = false;
564
+ } else {
565
+ obj.anyEnabled = true;
566
+ }
567
+
568
+ if ( incrementCounts ) {
569
+ obj.available = (obj.available || 0) + (!act.enabled ? 0 : 1 );
570
+ obj.total = (obj.total || 0) + 1;
571
+ }
572
+
573
+ return obj;
574
+ }
575
+
576
+ function _filter(map, disableAll = false) {
577
+ const out = filterBy(Object.values(map), 'anyEnabled', true);
578
+
579
+ for ( const act of out ) {
580
+ if ( disableAll ) {
581
+ act.enabled = false;
582
+ } else {
583
+ act.enabled = ( act.available >= act.total );
584
+ }
585
+ }
586
+
587
+ return out;
588
+ }
589
+
590
+ function _execute(resources, action, args, opts = {}, ctx) {
591
+ args = args || [];
592
+
593
+ // New pattern for extensions - always call invoke
594
+ if (action.invoke) {
595
+ const actionOpts = {
596
+ action,
597
+ event: opts.event,
598
+ isAlt: !!opts.alt,
599
+ };
600
+
601
+ return action.invoke.apply(ctx, [actionOpts, resources || [], args]);
602
+ }
603
+
604
+ if ( resources.length > 1 && action.bulkAction && !opts.alt ) {
605
+ const fn = resources[0][action.bulkAction];
606
+
607
+ if ( fn ) {
608
+ return fn.call(resources[0], resources, ...args);
609
+ }
610
+ }
611
+
612
+ const promises = [];
613
+
614
+ for ( const resource of resources ) {
615
+ let fn;
616
+
617
+ if (opts.alt && action.altAction) {
618
+ fn = resource[action.altAction];
619
+ } else {
620
+ fn = resource[action.action];
621
+ }
622
+
623
+ if ( fn ) {
624
+ promises.push(fn.apply(resource, args));
625
+ }
626
+ }
627
+
628
+ return Promise.all(promises);
629
+ }
@@ -0,0 +1,4 @@
1
+ // Its quicker to render if we directly supply the components for the formatters
2
+ // rather than just the name of a global component - so create a map of the formatter comoponents
3
+ // NOTE: This is populated by a plugin (formatters.js) to avoid issues with plugins
4
+ export const FORMATTERS = {};