@rancher/shell 0.1.21 → 0.2.1
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/global/_button.scss +1 -0
- package/assets/translations/en-us.yaml +48 -6
- package/assets/translations/zh-hans.yaml +18 -0
- package/components/HarvesterServiceAddOnConfig.vue +10 -10
- package/components/ResourceList/index.vue +34 -14
- package/components/ResourceTable.vue +19 -0
- package/components/SortableTable/THead.vue +311 -70
- package/components/SortableTable/advanced-filtering.js +272 -0
- package/components/SortableTable/filtering.js +90 -29
- package/components/SortableTable/index.vue +474 -271
- package/components/form/WorkloadPorts.vue +2 -2
- package/config/product/settings.js +1 -0
- package/config/product/uiplugins.js +1 -0
- package/config/uiplugins.js +13 -0
- package/creators/app/init +2 -2
- package/creators/app/package.json +1 -1
- package/creators/pkg/package.json +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/ACE.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/DrainOptions.vue +0 -1
- package/edit/provisioning.cattle.io.cluster/rke2.vue +25 -24
- package/edit/service.vue +1 -1
- package/models/management.cattle.io.cluster.js +9 -1
- package/package.json +1 -1
- package/pages/c/_cluster/apps/charts/install.vue +119 -31
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +1 -1
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +5 -2
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +1 -1
- package/pages/c/_cluster/uiplugins/index.vue +48 -11
- package/promptRemove/mixin/roleDeletionCheck.js +15 -1
- package/scripts/record-deps.js +3 -3
- package/store/type-map.js +2 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { ADV_FILTER_ALL_COLS_VALUE, ADV_FILTER_ALL_COLS_LABEL } from './filtering';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_ADV_FILTER_COLS_VALUE = ADV_FILTER_ALL_COLS_VALUE;
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
props: {
|
|
7
|
+
/**
|
|
8
|
+
* Group value
|
|
9
|
+
* To be used on the THead component when adv filtering is present
|
|
10
|
+
*/
|
|
11
|
+
group: {
|
|
12
|
+
type: String,
|
|
13
|
+
default: () => ''
|
|
14
|
+
},
|
|
15
|
+
/**
|
|
16
|
+
* Group options
|
|
17
|
+
* All of the grouping options available to be used on the THead component when adv filtering is present
|
|
18
|
+
*/
|
|
19
|
+
groupOptions: {
|
|
20
|
+
type: Array,
|
|
21
|
+
default: () => []
|
|
22
|
+
},
|
|
23
|
+
/**
|
|
24
|
+
* Flag that controls visibility of advanced filtering feature
|
|
25
|
+
*/
|
|
26
|
+
hasAdvancedFiltering: {
|
|
27
|
+
type: Boolean,
|
|
28
|
+
default: false
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* Flag that controls visibility of labels as possibe toggable cols to be displayed on the Sortable Table
|
|
32
|
+
*/
|
|
33
|
+
advFilterHideLabelsAsCols: {
|
|
34
|
+
type: Boolean,
|
|
35
|
+
default: false
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* Flag that prevents filtering by labels
|
|
39
|
+
*/
|
|
40
|
+
advFilterPreventFilteringLabels: {
|
|
41
|
+
type: Boolean,
|
|
42
|
+
default: false
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
data() {
|
|
46
|
+
return {
|
|
47
|
+
columnOptions: [],
|
|
48
|
+
colOptionsWatcher: null,
|
|
49
|
+
advancedFilteringVisibility: false,
|
|
50
|
+
advancedFilteringValues: [],
|
|
51
|
+
advFilterSearchTerm: null,
|
|
52
|
+
advFilterSelectedProp: DEFAULT_ADV_FILTER_COLS_VALUE,
|
|
53
|
+
advFilterSelectedLabel: ADV_FILTER_ALL_COLS_LABEL,
|
|
54
|
+
column: null,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
mounted() {
|
|
59
|
+
if (this.hasAdvancedFiltering) {
|
|
60
|
+
// trigger to first populate the cols options for filters
|
|
61
|
+
this.updateColsOptions();
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
watch: {
|
|
66
|
+
advancedFilteringValues() {
|
|
67
|
+
// passing different dummy args to make sure update is triggered
|
|
68
|
+
this.watcherUpdateLiveAndDelayed(true, false);
|
|
69
|
+
},
|
|
70
|
+
advancedFilteringVisibility(neu) {
|
|
71
|
+
if (neu) {
|
|
72
|
+
// check if user clicked outside the advanced filter box
|
|
73
|
+
window.addEventListener('click', this.onClickOutside);
|
|
74
|
+
|
|
75
|
+
// update filtering options and toggable cols every time dropdown is open
|
|
76
|
+
this.updateColsOptions();
|
|
77
|
+
} else {
|
|
78
|
+
// unregister click event
|
|
79
|
+
window.removeEventListener('click', this.onClickOutside);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
computed: {
|
|
85
|
+
advFilterSelectOptions() {
|
|
86
|
+
return this.columnOptions.filter(c => c.isFilter && !c.preventFiltering);
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
advGroupOptions() {
|
|
90
|
+
return this.groupOptions.map((item) => {
|
|
91
|
+
return {
|
|
92
|
+
label: this.t(item.tooltipKey),
|
|
93
|
+
value: item.value
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
methods: {
|
|
100
|
+
handleColsVisibilyAndFiltering(cols) {
|
|
101
|
+
const allCols = cols;
|
|
102
|
+
|
|
103
|
+
this.columnOptions.forEach((advCol) => {
|
|
104
|
+
if (advCol.isTableOption) {
|
|
105
|
+
const index = allCols.findIndex(col => col.name === advCol.name);
|
|
106
|
+
|
|
107
|
+
if (index !== -1) {
|
|
108
|
+
allCols[index].isColVisible = advCol.isColVisible;
|
|
109
|
+
allCols[index].isFilter = advCol.isFilter;
|
|
110
|
+
} else {
|
|
111
|
+
allCols.push(advCol);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return allCols;
|
|
117
|
+
},
|
|
118
|
+
// advanced filtering methods
|
|
119
|
+
setColsOptions() {
|
|
120
|
+
let opts = [];
|
|
121
|
+
const rowLabels = [];
|
|
122
|
+
const headerProps = [];
|
|
123
|
+
|
|
124
|
+
// Filter out any columns that are too heavy to show for large page sizes
|
|
125
|
+
const filteredHeaders = this.headers.slice().filter(c => (!c.maxPageSize || (c.maxPageSize && c.maxPageSize >= this.perPage)));
|
|
126
|
+
|
|
127
|
+
// add table cols from config (headers)
|
|
128
|
+
filteredHeaders.forEach((prop) => {
|
|
129
|
+
const name = prop.name;
|
|
130
|
+
const label = prop.labelKey ? this.t(`${ prop.labelKey }`) : prop.label;
|
|
131
|
+
const isFilter = !!((!Object.keys(prop).includes('search') || prop.search));
|
|
132
|
+
let sortVal = prop.sort;
|
|
133
|
+
const valueProp = prop.valueProp || prop.value;
|
|
134
|
+
let value = null;
|
|
135
|
+
let isColVisible = true;
|
|
136
|
+
|
|
137
|
+
if (prop.sort && valueProp) {
|
|
138
|
+
if (typeof prop.sort === 'string') {
|
|
139
|
+
sortVal = prop.sort.includes(':') ? [prop.sort.split(':')[0]] : [prop.sort];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!sortVal.includes(valueProp)) {
|
|
143
|
+
value = JSON.stringify(sortVal.concat([valueProp]));
|
|
144
|
+
} else {
|
|
145
|
+
value = JSON.stringify([valueProp]);
|
|
146
|
+
}
|
|
147
|
+
} else if (valueProp) {
|
|
148
|
+
value = JSON.stringify([valueProp]);
|
|
149
|
+
} else {
|
|
150
|
+
value = null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// maintain current visibility of cols if they exist already
|
|
154
|
+
if (this.columnOptions?.length) {
|
|
155
|
+
const opt = this.columnOptions.find(colOpt => colOpt.name === name && colOpt.label === label);
|
|
156
|
+
|
|
157
|
+
if (opt) {
|
|
158
|
+
isColVisible = opt.isColVisible;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
headerProps.push({
|
|
163
|
+
name,
|
|
164
|
+
label,
|
|
165
|
+
value,
|
|
166
|
+
isFilter,
|
|
167
|
+
isTableOption: true,
|
|
168
|
+
isColVisible
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// add labels as table cols
|
|
173
|
+
if (this.rows.length) {
|
|
174
|
+
this.rows.forEach((row) => {
|
|
175
|
+
if (row.metadata?.labels && Object.keys(row.metadata?.labels).length) {
|
|
176
|
+
Object.keys(row.metadata?.labels).forEach((label) => {
|
|
177
|
+
const res = {
|
|
178
|
+
name: label,
|
|
179
|
+
label,
|
|
180
|
+
value: `metadata.labels.${ label }`,
|
|
181
|
+
isFilter: true,
|
|
182
|
+
isTableOption: true,
|
|
183
|
+
isColVisible: false,
|
|
184
|
+
isLabel: true,
|
|
185
|
+
preventFiltering: this.advFilterPreventFilteringLabels,
|
|
186
|
+
preventColToggle: this.advFilterHideLabelsAsCols
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// maintain current visibility of cols if they exist already
|
|
190
|
+
if (this.columnOptions?.length) {
|
|
191
|
+
const opt = this.columnOptions.find(colOpt => colOpt.name === label && colOpt.label === label);
|
|
192
|
+
|
|
193
|
+
if (opt) {
|
|
194
|
+
res.isColVisible = opt.isColVisible;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!rowLabels.filter(row => row.label === label).length) {
|
|
199
|
+
rowLabels.push(res);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
opts = headerProps.concat(rowLabels);
|
|
207
|
+
|
|
208
|
+
// add find on all cols option...
|
|
209
|
+
if (opts.length) {
|
|
210
|
+
opts.unshift({
|
|
211
|
+
name: ADV_FILTER_ALL_COLS_LABEL,
|
|
212
|
+
label: ADV_FILTER_ALL_COLS_LABEL,
|
|
213
|
+
value: ADV_FILTER_ALL_COLS_VALUE,
|
|
214
|
+
isFilter: true,
|
|
215
|
+
isTableOption: false
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return opts;
|
|
220
|
+
},
|
|
221
|
+
addAdvancedFilter() {
|
|
222
|
+
// set new advanced filter
|
|
223
|
+
if (this.advFilterSelectedProp && this.advFilterSearchTerm) {
|
|
224
|
+
this.advancedFilteringValues.push({
|
|
225
|
+
prop: this.advFilterSelectedProp,
|
|
226
|
+
value: this.advFilterSearchTerm,
|
|
227
|
+
label: this.advFilterSelectedLabel
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
this.eventualSearchQuery = this.advancedFilteringValues;
|
|
231
|
+
|
|
232
|
+
this.advancedFilteringVisibility = false;
|
|
233
|
+
this.advFilterSelectedProp = DEFAULT_ADV_FILTER_COLS_VALUE;
|
|
234
|
+
this.advFilterSelectedLabel = ADV_FILTER_ALL_COLS_LABEL;
|
|
235
|
+
this.advFilterSearchTerm = null;
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
clearAllAdvancedFilters() {
|
|
239
|
+
this.advancedFilteringValues = [];
|
|
240
|
+
this.eventualSearchQuery = this.advancedFilteringValues;
|
|
241
|
+
|
|
242
|
+
this.advancedFilteringVisibility = false;
|
|
243
|
+
this.advFilterSelectedProp = DEFAULT_ADV_FILTER_COLS_VALUE;
|
|
244
|
+
this.advFilterSelectedLabel = ADV_FILTER_ALL_COLS_LABEL;
|
|
245
|
+
this.advFilterSearchTerm = null;
|
|
246
|
+
},
|
|
247
|
+
clearAdvancedFilter(index) {
|
|
248
|
+
this.advancedFilteringValues.splice(index, 1);
|
|
249
|
+
this.eventualSearchQuery = this.advancedFilteringValues;
|
|
250
|
+
},
|
|
251
|
+
onClickOutside(event) {
|
|
252
|
+
const advFilterBox = this.$refs['advanced-filter-group'];
|
|
253
|
+
|
|
254
|
+
if (!advFilterBox || advFilterBox.contains(event.target)) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
this.advancedFilteringVisibility = false;
|
|
258
|
+
},
|
|
259
|
+
updateColsOptions() {
|
|
260
|
+
this.columnOptions = this.setColsOptions();
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
// cols visibility
|
|
264
|
+
changeColVisibility(colData) {
|
|
265
|
+
const index = this.columnOptions.findIndex(col => col.label === colData.label);
|
|
266
|
+
|
|
267
|
+
if (index !== -1) {
|
|
268
|
+
this.columnOptions[index].isColVisible = colData.value;
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { get } from '@shell/utils/object';
|
|
2
2
|
import { addObject, addObjects, isArray, removeAt } from '@shell/utils/array';
|
|
3
3
|
|
|
4
|
+
export const ADV_FILTER_ALL_COLS_VALUE = 'allcols';
|
|
5
|
+
export const ADV_FILTER_ALL_COLS_LABEL = 'All Columns';
|
|
6
|
+
const LABEL_IDENTIFIER = ':::islabel';
|
|
7
|
+
|
|
4
8
|
export default {
|
|
5
9
|
data() {
|
|
6
10
|
return {
|
|
@@ -29,6 +33,69 @@ export default {
|
|
|
29
33
|
}),
|
|
30
34
|
*/
|
|
31
35
|
filteredRows() {
|
|
36
|
+
// PROP hasAdvancedFiltering comes from Advanced Filtering mixin (careful changing data var there...)
|
|
37
|
+
if (!this.hasAdvancedFiltering) {
|
|
38
|
+
return this.handleFiltering();
|
|
39
|
+
} else {
|
|
40
|
+
return this.handleAdvancedFiltering();
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
methods: {
|
|
46
|
+
handleAdvancedFiltering() {
|
|
47
|
+
this.subMatches = null;
|
|
48
|
+
|
|
49
|
+
if (this.searchQuery.length) {
|
|
50
|
+
const out = (this.arrangedRows || []).slice();
|
|
51
|
+
|
|
52
|
+
const res = out.filter((row) => {
|
|
53
|
+
return this.searchQuery.every((f) => {
|
|
54
|
+
if (f.prop === ADV_FILTER_ALL_COLS_VALUE) {
|
|
55
|
+
// advFilterSelectOptions comes from Advanced Filtering mixin
|
|
56
|
+
// remove the All Columns option from the list so that we don't iterate over it
|
|
57
|
+
const allCols = this.advFilterSelectOptions.slice(1);
|
|
58
|
+
let searchFields = [];
|
|
59
|
+
|
|
60
|
+
allCols.forEach((col) => {
|
|
61
|
+
if (col.value.includes('[') && col.value.includes(']')) {
|
|
62
|
+
searchFields = searchFields.concat(JSON.parse(col.value));
|
|
63
|
+
} else {
|
|
64
|
+
// this means we are on the presence of a label, which should be dealt
|
|
65
|
+
// carefully because of object path such row.metadata.labels."app.kubernetes.io/managed-by
|
|
66
|
+
const value = col.isLabel ? `${ col.label }${ LABEL_IDENTIFIER }` : col.value;
|
|
67
|
+
|
|
68
|
+
searchFields.push(value);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return handleStringSearch(searchFields, [f.value], row);
|
|
73
|
+
} else {
|
|
74
|
+
if (f.prop.includes('[') && f.prop.includes(']')) {
|
|
75
|
+
return handleStringSearch(JSON.parse(f.prop), [f.value], row);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let prop = f.prop;
|
|
79
|
+
|
|
80
|
+
// this means we are on the presence of a label, which should be dealt
|
|
81
|
+
// carefully because of object path such row.metadata.labels."app.kubernetes.io/managed-by"
|
|
82
|
+
if (f.prop.includes('metadata.labels')) {
|
|
83
|
+
prop = `${ f.label }${ LABEL_IDENTIFIER }`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return handleStringSearch([prop], [f.value], row);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return res;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// return arrangedRows array if we don't have anything to search for...
|
|
95
|
+
return this.arrangedRows;
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
handleFiltering() {
|
|
32
99
|
const searchText = (this.searchQuery || '').trim().toLowerCase();
|
|
33
100
|
let out;
|
|
34
101
|
|
|
@@ -61,20 +128,7 @@ export default {
|
|
|
61
128
|
let hits = 0;
|
|
62
129
|
let mainFound = true;
|
|
63
130
|
|
|
64
|
-
|
|
65
|
-
let expect = true;
|
|
66
|
-
let token = searchTokens[j];
|
|
67
|
-
|
|
68
|
-
if ( token.substr(0, 1) === '!' ) {
|
|
69
|
-
expect = false;
|
|
70
|
-
token = token.substr(1);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if ( token && matches(searchFields, token, row) !== expect ) {
|
|
74
|
-
mainFound = false;
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
131
|
+
mainFound = handleStringSearch(searchFields, searchTokens, row);
|
|
78
132
|
|
|
79
133
|
if ( subFields && subSearch) {
|
|
80
134
|
const subRows = row[subSearch] || [];
|
|
@@ -82,20 +136,7 @@ export default {
|
|
|
82
136
|
for ( let k = subRows.length - 1 ; k >= 0 ; k-- ) {
|
|
83
137
|
let subFound = true;
|
|
84
138
|
|
|
85
|
-
|
|
86
|
-
let expect = true;
|
|
87
|
-
let token = searchTokens[l];
|
|
88
|
-
|
|
89
|
-
if ( token.substr(0, 1) === '!' ) {
|
|
90
|
-
expect = false;
|
|
91
|
-
token = token.substr(1);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if ( matches(subFields, token, subRows[k]) !== expect ) {
|
|
95
|
-
subFound = false;
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
139
|
+
subFound = handleStringSearch(subFields, searchTokens, row);
|
|
99
140
|
|
|
100
141
|
if ( subFound ) {
|
|
101
142
|
hits++;
|
|
@@ -114,7 +155,7 @@ export default {
|
|
|
114
155
|
this.previousResult = out;
|
|
115
156
|
|
|
116
157
|
return out;
|
|
117
|
-
}
|
|
158
|
+
}
|
|
118
159
|
},
|
|
119
160
|
|
|
120
161
|
watch: {
|
|
@@ -150,6 +191,24 @@ function columnsToSearchField(columns) {
|
|
|
150
191
|
|
|
151
192
|
const ipLike = /^[0-9a-f\.:]+$/i;
|
|
152
193
|
|
|
194
|
+
function handleStringSearch(searchFields, searchTokens, row) {
|
|
195
|
+
for ( let j = 0 ; j < searchTokens.length ; j++ ) {
|
|
196
|
+
let expect = true;
|
|
197
|
+
let token = searchTokens[j];
|
|
198
|
+
|
|
199
|
+
if ( token.substr(0, 1) === '!' ) {
|
|
200
|
+
expect = false;
|
|
201
|
+
token = token.substr(1);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if ( token && matches(searchFields, token, row) !== expect ) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
153
212
|
function matches(fields, token, item) {
|
|
154
213
|
for ( let field of fields ) {
|
|
155
214
|
if ( !field ) {
|
|
@@ -161,6 +220,8 @@ function matches(fields, token, item) {
|
|
|
161
220
|
|
|
162
221
|
if (typeof field === 'function') {
|
|
163
222
|
val = field(item);
|
|
223
|
+
} else if (field.includes(LABEL_IDENTIFIER)) {
|
|
224
|
+
val = item.metadata.labels[field.replace(LABEL_IDENTIFIER, '')];
|
|
164
225
|
} else {
|
|
165
226
|
const idx = field.indexOf(':');
|
|
166
227
|
|