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,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
+ };
@@ -0,0 +1,117 @@
1
+ /* eslint-disable no-console */
2
+
3
+ // =========================================================================================
4
+ // Debug helper
5
+ // Adds a bunch of watches to help dignose computed properties bring re-evaluated
6
+ // =========================================================================================
7
+
8
+ export default {
9
+ watch: {
10
+ sortFields(neu, old) {
11
+ console.log('sortFields changed ------------------------------------------------');
12
+ console.log(neu);
13
+ console.log(old);
14
+ },
15
+
16
+ descending(neu, old) {
17
+ console.log('descending changed ------------------------------------------------');
18
+ console.log(neu);
19
+ console.log(old);
20
+ },
21
+
22
+ rows(neu, old) {
23
+ console.log('rows changed ------------------------------------------------');
24
+ console.log(neu.length);
25
+ console.log(old.length);
26
+
27
+ // console.log('Checking rows');
28
+
29
+ // let diff = 0;
30
+
31
+ // for (let i=0;i<neu.length;i++) {
32
+ // const a = JSON.stringify(neu[i]);
33
+ // const b = JSON.stringify(old[i]);
34
+
35
+ // if (a !== b) {
36
+ // console.log('rows differ ' + i);
37
+ // diff++;
38
+ // }
39
+ // }
40
+
41
+ // console.log(diff + ' rows changed');
42
+ },
43
+
44
+ pagingDisplay(neu, old) {
45
+ console.log('pagingDisplay changed ------------------------------------------------');
46
+ console.log(neu.length);
47
+ console.log(old.length);
48
+ },
49
+
50
+ totalPages(neu, old) {
51
+ console.log('totalPages changed ------------------------------------------------');
52
+ console.log(neu.length);
53
+ console.log(old.length);
54
+ },
55
+
56
+ pagedRows(neu, old) {
57
+ console.log('pagedRows changed ------------------------------------------------');
58
+ console.log(neu.length);
59
+ console.log(old.length);
60
+ },
61
+
62
+ arrangedRows(neu, old) {
63
+ console.log('arrangedRows changed ------------------------------------------------');
64
+ console.log(neu.length);
65
+ console.log(old.length);
66
+ },
67
+
68
+ searchFields(neu, old) {
69
+ console.log('searchFields changed ------------------------------------------------');
70
+ console.log(neu.length);
71
+ console.log(old.length);
72
+ },
73
+
74
+ filteredRows(neu, old) {
75
+ console.log('filteredRows changed ------------------------------------------------');
76
+ console.log(neu.length);
77
+ console.log(old.length);
78
+ },
79
+
80
+ groupedRows(neu, old) {
81
+ console.log('groupedRows changed ------------------------------------------------');
82
+ console.log(neu.length);
83
+ console.log(old.length);
84
+ },
85
+
86
+ headers(neu, old) {
87
+ console.log('headers changed ------------------------------------------------');
88
+ console.log(neu);
89
+ console.log(old);
90
+ },
91
+
92
+ displayRows(neu, old) {
93
+ console.log('displayRows changed ------------------------------------------------');
94
+ console.log(neu);
95
+ console.log(old);
96
+ },
97
+
98
+ groupBy(neu, old) {
99
+ console.log('groupBy changed ------------------------------------------------');
100
+ console.log(neu);
101
+ console.log(old);
102
+ },
103
+
104
+ groupSort(neu, old) {
105
+ console.log('groupSort changed ------------------------------------------------');
106
+ console.log(neu);
107
+ console.log(old);
108
+ },
109
+
110
+ columns(neu, old) {
111
+ console.log('columns changed ------------------------------------------------');
112
+ console.log(neu);
113
+ console.log(old);
114
+ },
115
+ }
116
+ };
117
+ /* eslint-enable no-console */
@@ -0,0 +1,290 @@
1
+ import { get } from '@shell/utils/object';
2
+ import { addObject, addObjects, isArray, removeAt } from '@shell/utils/array';
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
+
8
+ export default {
9
+ data() {
10
+ return {
11
+ searchQuery: null,
12
+ previousFilter: null,
13
+ previousResult: null,
14
+ };
15
+ },
16
+
17
+ computed: {
18
+ searchFields() {
19
+ const out = columnsToSearchField(this.columns);
20
+
21
+ if ( this.extraSearchFields ) {
22
+ addObjects(out, this.extraSearchFields);
23
+ }
24
+
25
+ return out;
26
+ },
27
+
28
+ /*
29
+ subFields: computed('subHeaders.@each.{searchField,name}', 'extraSearchSubFields.[]', function() {
30
+ let out = headersToSearchField(get(this, 'subHeaders'));
31
+
32
+ return out.addObjects(get(this, 'extraSearchSubFields') || []);
33
+ }),
34
+ */
35
+ filteredRows() {
36
+ if (this.externalPaginationEnabled) {
37
+ return;
38
+ }
39
+
40
+ // PROP hasAdvancedFiltering comes from Advanced Filtering mixin (careful changing data var there...)
41
+ if (!this.hasAdvancedFiltering) {
42
+ return this.handleFiltering();
43
+ } else {
44
+ return this.handleAdvancedFiltering();
45
+ }
46
+ },
47
+ },
48
+
49
+ methods: {
50
+ handleAdvancedFiltering() {
51
+ this.subMatches = null;
52
+
53
+ if (this.searchQuery.length) {
54
+ const out = (this.arrangedRows || []).slice();
55
+
56
+ const res = out.filter((row) => {
57
+ return this.searchQuery.every((f) => {
58
+ if (f.prop === ADV_FILTER_ALL_COLS_VALUE) {
59
+ // advFilterSelectOptions comes from Advanced Filtering mixin
60
+ // remove the All Columns option from the list so that we don't iterate over it
61
+ const allCols = this.advFilterSelectOptions.slice(1);
62
+ let searchFields = [];
63
+
64
+ allCols.forEach((col) => {
65
+ if (col.value.includes('[') && col.value.includes(']')) {
66
+ searchFields = searchFields.concat(JSON.parse(col.value));
67
+ } else {
68
+ // this means we are on the presence of a label, which should be dealt
69
+ // carefully because of object path such row.metadata.labels."app.kubernetes.io/managed-by
70
+ const value = col.isLabel ? `${ col.label }${ LABEL_IDENTIFIER }` : col.value;
71
+
72
+ searchFields.push(value);
73
+ }
74
+ });
75
+
76
+ return handleStringSearch(searchFields, [f.value], row);
77
+ } else {
78
+ if (f.prop.includes('[') && f.prop.includes(']')) {
79
+ return handleStringSearch(JSON.parse(f.prop), [f.value], row);
80
+ }
81
+
82
+ let prop = f.prop;
83
+
84
+ // this means we are on the presence of a label, which should be dealt
85
+ // carefully because of object path such row.metadata.labels."app.kubernetes.io/managed-by"
86
+ if (f.prop.includes('metadata.labels')) {
87
+ prop = `${ f.label }${ LABEL_IDENTIFIER }`;
88
+ }
89
+
90
+ return handleStringSearch([prop], [f.value], row);
91
+ }
92
+ });
93
+ });
94
+
95
+ return res;
96
+ }
97
+
98
+ // return arrangedRows array if we don't have anything to search for...
99
+ return this.arrangedRows;
100
+ },
101
+
102
+ handleFiltering() {
103
+ const searchText = (this.searchQuery || '').trim().toLowerCase();
104
+ let out;
105
+
106
+ if ( searchText && this.previousResult && searchText.startsWith(this.previousFilter) ) {
107
+ // If the new search is an addition to the last one, we can start with the same set of results as last time
108
+ // and filter those down, since adding more searchText can only reduce the number of results.
109
+ out = this.previousResult.slice();
110
+ } else {
111
+ this.previousResult = null;
112
+ out = (this.arrangedRows || []).slice();
113
+ }
114
+
115
+ this.previousFilter = searchText;
116
+
117
+ if ( !searchText.length ) {
118
+ this.subMatches = null;
119
+ this.previousResult = null;
120
+
121
+ return out;
122
+ }
123
+
124
+ const searchFields = this.searchFields;
125
+ const searchTokens = searchText.split(/\s*[, ]\s*/);
126
+ const subSearch = this.subSearch;
127
+ const subFields = this.subFields;
128
+ const subMatches = {};
129
+
130
+ for ( let i = out.length - 1 ; i >= 0 ; i-- ) {
131
+ const row = out[i];
132
+ let hits = 0;
133
+ let mainFound = true;
134
+
135
+ mainFound = handleStringSearch(searchFields, searchTokens, row);
136
+
137
+ if ( subFields && subSearch) {
138
+ const subRows = row[subSearch] || [];
139
+
140
+ for ( let k = subRows.length - 1 ; k >= 0 ; k-- ) {
141
+ let subFound = true;
142
+
143
+ subFound = handleStringSearch(subFields, searchTokens, row);
144
+
145
+ if ( subFound ) {
146
+ hits++;
147
+ }
148
+ }
149
+
150
+ subMatches[get(row, this.keyField)] = hits;
151
+ }
152
+
153
+ if ( !mainFound && hits === 0 ) {
154
+ removeAt(out, i);
155
+ }
156
+ }
157
+
158
+ this.subMatches = subMatches;
159
+ this.previousResult = out;
160
+
161
+ return out;
162
+ }
163
+ },
164
+
165
+ watch: {
166
+ arrangedRows(q) {
167
+ // The rows changed so the old filter result is no longer useful
168
+ this.previousResult = null;
169
+ },
170
+
171
+ searchQuery() {
172
+ this.debouncedPaginationChanged();
173
+ },
174
+ },
175
+ };
176
+
177
+ function columnsToSearchField(columns) {
178
+ const out = [];
179
+
180
+ (columns || []).forEach((column) => {
181
+ const field = column.search;
182
+
183
+ if ( field ) {
184
+ if ( typeof field === 'string' ) {
185
+ addObject(out, field);
186
+ } else if ( isArray(field) ) {
187
+ addObjects(out, field);
188
+ }
189
+ } else if ( field === false ) {
190
+ // Don't add the name
191
+ } else {
192
+ // Use value/name as the default
193
+ addObject(out, column.value || column.name);
194
+ }
195
+ });
196
+
197
+ return out.filter((x) => !!x);
198
+ }
199
+
200
+ const ipLike = /^[0-9a-f\.:]+$/i;
201
+
202
+ function handleStringSearch(searchFields, searchTokens, row) {
203
+ for ( let j = 0 ; j < searchTokens.length ; j++ ) {
204
+ let expect = true;
205
+ let token = searchTokens[j];
206
+
207
+ if ( token.substr(0, 1) === '!' ) {
208
+ expect = false;
209
+ token = token.substr(1);
210
+ }
211
+
212
+ if ( token && matches(searchFields, token, row) !== expect ) {
213
+ return false;
214
+ }
215
+
216
+ return true;
217
+ }
218
+ }
219
+
220
+ function matches(fields, token, item) {
221
+ for ( let field of fields ) {
222
+ if ( !field ) {
223
+ continue;
224
+ }
225
+
226
+ // some items might not even have metadata.labels or metadata.labels.something... ignore those items. Nothing to filter by
227
+ if (typeof field !== 'function' &&
228
+ field.includes(LABEL_IDENTIFIER) &&
229
+ (!item.metadata.labels || !item.metadata.labels[field.replace(LABEL_IDENTIFIER, '')])) {
230
+ continue;
231
+ }
232
+
233
+ let modifier;
234
+ let val;
235
+
236
+ if (typeof field === 'function') {
237
+ val = field(item);
238
+ } else if (field.includes(LABEL_IDENTIFIER)) {
239
+ val = item.metadata.labels[field.replace(LABEL_IDENTIFIER, '')];
240
+ } else {
241
+ const idx = field.indexOf(':');
242
+
243
+ if ( idx > 0 ) {
244
+ modifier = field.substr(idx + 1);
245
+ field = field.substr(0, idx);
246
+ }
247
+
248
+ if ( field.includes('.') ) {
249
+ val = get(item, field);
250
+ } else {
251
+ val = item[field];
252
+ }
253
+ }
254
+
255
+ if ( val === undefined ) {
256
+ continue;
257
+ }
258
+
259
+ val = (`${ val }`).toLowerCase();
260
+ if ( !val ) {
261
+ continue;
262
+ }
263
+
264
+ if ( !modifier ) {
265
+ if ( val.includes((`${ token }`).toLowerCase()) ) {
266
+ return true;
267
+ }
268
+ } else if ( modifier === 'exact' ) {
269
+ if ( val === token ) {
270
+ return true;
271
+ }
272
+ } else if ( modifier === 'ip' ) {
273
+ const tokenMayBeIp = ipLike.test(token);
274
+
275
+ if ( tokenMayBeIp ) {
276
+ const re = new RegExp(`(?:^|\\.)${ token }(?:\\.|$)`);
277
+
278
+ if ( re.test(val) ) {
279
+ return true;
280
+ }
281
+ }
282
+ } else if ( modifier === 'prefix' ) {
283
+ if ( val.indexOf(token) === 0) {
284
+ return true;
285
+ }
286
+ }
287
+ }
288
+
289
+ return false;
290
+ }