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.
- package/assets/styles/base/_functions.scss +0 -0
- package/assets/styles/base/_mixins.scss +1 -1
- package/assets/styles/global/_button.scss +10 -17
- package/assets/styles/global/_form.scss +2 -2
- package/assets/styles/global/_labeled-input.scss +2 -6
- package/assets/styles/global/_select.scss +7 -6
- package/assets/styles/global/_table.scss +2 -3
- package/assets/styles/global/_tooltip.scss +1 -8
- package/assets/styles/themes/_dark.scss +0 -2
- package/assets/styles/themes/_light.scss +2 -5
- package/assets/styles/vendor/vue-select.scss +1 -2
- package/assets/translations/en-us.yaml +3 -1
- package/assets/translations/zh-hans.yaml +28 -51
- package/components/ActionDropdown.vue +0 -1
- package/components/ActionMenuShell.vue +3 -6
- package/components/BrandImage.vue +0 -22
- package/components/ClusterIconMenu.vue +1 -1
- package/components/CodeMirror.vue +0 -1
- package/components/CruResource.vue +1 -1
- package/components/CruResourceFooter.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +24 -4
- package/components/GlobalRoleBindings.vue +48 -112
- package/components/IndentedPanel.vue +10 -4
- package/components/PromptRemove.vue +3 -3
- package/components/ResourceDetail/Masthead.vue +242 -190
- package/components/ResourceDetail/index.vue +5 -20
- package/components/ResourceList/Masthead.vue +84 -146
- package/components/ResourceList/ResourceLoadingIndicator.vue +2 -5
- package/components/ResourceTable.vue +1 -76
- package/components/SideNav.vue +29 -66
- package/components/SortableTable/THead.vue +0 -6
- package/components/SortableTable/index.vue +388 -481
- package/components/Tabbed/index.vue +5 -4
- package/components/auth/Principal.vue +2 -3
- package/components/auth/RoleDetailEdit.vue +5 -58
- package/components/auth/SelectPrincipal.vue +0 -1
- package/components/form/BannerSettings.vue +16 -18
- package/components/form/ChangePassword.vue +4 -4
- package/components/form/ColorInput.vue +8 -32
- package/components/form/Footer.vue +1 -1
- package/components/form/InputWithSelect.vue +0 -2
- package/components/form/KeyValue.vue +7 -31
- package/components/form/LabeledSelect.vue +178 -178
- package/components/form/Members/ClusterPermissionsEditor.vue +2 -1
- package/components/form/Members/MembershipEditor.vue +1 -1
- package/components/form/NameNsDescription.vue +11 -24
- package/components/form/Password.vue +2 -6
- package/components/form/ResourceQuota/Namespace.vue +1 -1
- package/components/form/ResourceQuota/NamespaceRow.vue +10 -13
- package/components/form/ResourceQuota/ProjectRow.vue +1 -0
- package/components/form/Select.vue +2 -2
- package/components/nav/Favorite.vue +1 -5
- package/components/nav/Group.vue +23 -69
- package/components/nav/Header.vue +17 -82
- package/components/nav/HeaderPageActionMenu.vue +0 -1
- package/components/nav/NamespaceFilter.vue +3 -0
- package/components/nav/TopLevelMenu.vue +119 -182
- package/components/nav/Type.vue +11 -48
- package/components/rancherResourceDetail/Masthead.vue +769 -0
- package/components/rancherResourceDetail/__tests__/Masthead.test.ts +65 -0
- package/components/rancherResourceDetail/index.vue +591 -0
- package/components/rancherResourceList/Masthead.vue +375 -0
- package/components/rancherResourceList/ResourceLoadingIndicator.vue +140 -0
- package/components/rancherResourceList/index.vue +307 -0
- package/components/rancherResourceList/resource-list.config.js +7 -0
- package/components/rancherResourceTable.vue +783 -0
- package/components/rancherSortableTable/THead.vue +561 -0
- package/components/rancherSortableTable/actions.js +153 -0
- package/components/rancherSortableTable/advanced-filtering.js +272 -0
- package/components/rancherSortableTable/debug.js +117 -0
- package/components/rancherSortableTable/filtering.js +290 -0
- package/components/rancherSortableTable/grouping.js +48 -0
- package/components/rancherSortableTable/index.vue +2712 -0
- package/components/rancherSortableTable/paging.js +155 -0
- package/components/rancherSortableTable/selection.js +629 -0
- package/components/rancherSortableTable/sortable-config.ts +4 -0
- package/components/rancherSortableTable/sorting.js +129 -0
- package/composables/useClickOutside.ts +1 -1
- package/config/product/auth.js +7 -16
- package/config/product/explorer.js +1 -1
- package/config/product/settings.js +8 -17
- package/config/settings.ts +0 -28
- package/edit/management.cattle.io.user.vue +4 -17
- package/edit/networking.k8s.io.ingress/RulePath.vue +1 -1
- package/edit/token.vue +1 -1
- package/list/harvesterhci.io.management.cluster.vue +0 -17
- package/list/management.cattle.io.setting.vue +13 -22
- package/list/management.cattle.io.user.vue +14 -25
- package/list/provisioning.cattle.io.cluster.vue +7 -6
- package/mixins/brand.js +0 -17
- package/package.json +1 -1
- package/pages/auth/login.vue +29 -84
- package/pages/c/_cluster/auth/roles/index.vue +14 -61
- package/pages/c/_cluster/settings/banners.vue +101 -174
- package/pages/c/_cluster/settings/brand.vue +301 -348
- package/pages/c/_cluster/settings/performance.vue +38 -61
- package/pages/home.vue +21 -70
- package/pages/prefs.vue +23 -25
- package/pkg/tsconfig.json +9 -9
- package/pkg/vue.config.js +1 -1
- package/promptRemove/mixin/roleDeletionCheck.js +2 -2
- package/scripts/clean +0 -0
- package/scripts/extension/bundle +0 -0
- package/scripts/extension/helm/scripts/package +0 -0
- package/scripts/extension/helm/scripts/patch +0 -0
- package/scripts/extension/helm/scripts/version +0 -0
- package/scripts/extension/helmpatch +0 -0
- package/scripts/extension/parse-tag-name +0 -0
- package/scripts/extension/publish +0 -0
- package/scripts/publish-shell.sh +60 -86
- package/scripts/serve-pkgs +0 -0
- package/scripts/sync-shell-deps +0 -0
- package/scripts/typegen.sh +28 -44
- package/store/i18n.js +5 -5
- package/store/prefs.js +5 -17
- package/store/type-map.js +1 -2
- package/types/cloud-shell/index.d.ts +11014 -0
- package/types/shell/index.d.ts +1 -1
- package/utils/error.js +0 -4
- package/utils/router.js +3 -3
- package/vue.config.js +6 -1
- package/assets/images/action.svg +0 -6
- package/assets/images/pl/logo.png +0 -0
- /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 = {};
|