@rancher/shell 0.3.24 → 0.3.25
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/themes/_light.scss +1 -1
- package/assets/translations/en-us.yaml +29 -7
- package/assets/translations/zh-hans.yaml +1 -1
- package/components/ClusterIconMenu.vue +143 -0
- package/components/CruResource.vue +7 -1
- package/components/ExplorerProjectsNamespaces.vue +11 -1
- package/components/FixedBanner.vue +17 -1
- package/components/Markdown.vue +1 -1
- package/components/Questions/__tests__/Yaml.test.ts +3 -2
- package/components/SortableTable/index.vue +3 -2
- package/components/auth/RoleDetailEdit.vue +15 -2
- package/components/auth/login/saml.vue +12 -1
- package/components/form/LabeledSelect.vue +12 -5
- package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
- package/components/form/Members/MembershipEditor.vue +6 -1
- package/components/form/__tests__/KeyValue.test.ts +6 -3
- package/components/form/__tests__/LabeledSelect.test.ts +18 -0
- package/components/formatter/PodsUsage.vue +11 -36
- package/components/formatter/PrincipalGroupBindings.vue +8 -5
- package/components/formatter/__tests__/PodsUsage.test.ts +36 -19
- package/components/nav/Group.vue +25 -27
- package/components/nav/Header.vue +12 -5
- package/components/nav/Pinned.vue +47 -0
- package/components/nav/TopLevelMenu.vue +233 -60
- package/components/nav/Type.vue +57 -3
- package/config/home-links.js +1 -1
- package/config/product/istio.js +15 -5
- package/config/router.js +3 -9
- package/config/table-headers.js +5 -6
- package/config/uiplugins.js +1 -0
- package/core/plugin-helpers.js +3 -0
- package/core/types.ts +6 -1
- package/creators/app/files/.vscode/settings.json +0 -1
- package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +118 -0
- package/detail/autoscaling.horizontalpodautoscaler/index.vue +4 -4
- package/detail/provisioning.cattle.io.cluster.vue +7 -5
- package/edit/__tests__/management.cattle.io.clusterroletemplatebinding.test.ts +58 -0
- package/edit/__tests__/namespace.test.ts +5 -3
- package/edit/management.cattle.io.clusterroletemplatebinding.vue +3 -11
- package/edit/namespace.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/Basics.vue +662 -0
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +6 -0
- package/edit/provisioning.cattle.io.cluster/DrainOptions.vue +13 -8
- package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -2
- package/edit/provisioning.cattle.io.cluster/MemberRoles.vue +40 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +237 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.tests.ts +71 -23
- package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +52 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -142
- package/edit/provisioning.cattle.io.cluster/rke2.vue +194 -598
- package/edit/workload/storage/__tests__/Storage.test.ts +2 -2
- package/edit/workload/storage/persistentVolumeClaim/__tests__/persistentvolumeclaim.test.ts +36 -0
- package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +15 -7
- package/initialize/index.js +5 -5
- package/layouts/default.vue +6 -6
- package/layouts/home.vue +6 -2
- package/layouts/plain.vue +9 -2
- package/list/fleet.cattle.io.cluster.vue +2 -2
- package/list/management.cattle.io.feature.vue +1 -1
- package/machine-config/vmwarevsphere.vue +48 -7
- package/mixins/brand.js +0 -8
- package/mixins/child-hook.js +2 -2
- package/mixins/create-edit-view/impl.js +3 -3
- package/models/__tests__/management.cattle.io.node.ts +96 -0
- package/models/__tests__/node.ts +74 -0
- package/models/cluster/node.js +6 -5
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -2
- package/models/management.cattle.io.cluster.js +22 -1
- package/models/management.cattle.io.clusterroletemplatebinding.js +3 -3
- package/models/management.cattle.io.globalrole.js +17 -2
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.projectroletemplatebinding.js +3 -3
- package/models/management.cattle.io.roletemplate.js +17 -2
- package/package.json +2 -6
- package/pages/about.vue +2 -0
- package/pages/auth/setup.vue +5 -4
- package/pages/c/_cluster/monitoring/index.vue +8 -3
- package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +9 -66
- package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +182 -0
- package/pages/c/_cluster/uiplugins/CatalogList/index.vue +15 -32
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +8 -46
- package/pages/c/_cluster/uiplugins/index.vue +64 -64
- package/pages/diagnostic.vue +0 -39
- package/pages/home.vue +1 -1
- package/plugins/dashboard-store/normalize.js +4 -4
- package/plugins/int-number.js +5 -2
- package/plugins/positive-int-number.js +19 -0
- package/plugins/steve/__tests__/getters.spec.ts +15 -0
- package/plugins/steve/getters.js +22 -10
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +0 -8
- package/rancher-components/Form/Radio/RadioButton.test.ts +3 -7
- package/store/index.js +4 -0
- package/store/prefs.js +1 -0
- package/types/shell/index.d.ts +13 -4
- package/utils/__tests__/cluster.test.ts +55 -0
- package/utils/__tests__/object.test.ts +21 -2
- package/utils/cluster.js +47 -1
- package/utils/object.js +12 -5
- package/utils/validators/formRules/__tests__/index.test.ts +13 -1
- package/utils/validators/formRules/index.ts +4 -0
- package/utils/validators/role-template.js +9 -1
- package/utils/version.js +1 -1
- package/yarn-error.log +16 -16
- package/components/ClusterProviderIconMenu.vue +0 -161
- package/content/docs/en-us/getting-started.md +0 -224
- package/content/docs/en-us/whats-new.md +0 -29
- package/content/docs/zh-hans/getting-started.md +0 -224
- package/content/docs/zh-hans/whats-new.md +0 -28
- package/pages/docs/_doc.vue +0 -345
- package/pages/docs/toc.js +0 -27
- package/plugins/console.js +0 -34
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import BrandImage from '@shell/components/BrandImage';
|
|
3
|
-
import
|
|
3
|
+
import ClusterIconMenu from '@shell/components/ClusterIconMenu';
|
|
4
4
|
import IconOrSvg from '../IconOrSvg';
|
|
5
5
|
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
6
6
|
import { mapGetters } from 'vuex';
|
|
@@ -14,13 +14,14 @@ import { LEGACY } from '@shell/store/features';
|
|
|
14
14
|
import { SETTING } from '@shell/config/settings';
|
|
15
15
|
import { filterOnlyKubernetesClusters, filterHiddenLocalCluster } from '@shell/utils/cluster';
|
|
16
16
|
import { isRancherPrime } from '@shell/config/version';
|
|
17
|
+
import Pinned from '@shell/components/nav/Pinned';
|
|
17
18
|
|
|
18
19
|
export default {
|
|
19
|
-
|
|
20
20
|
components: {
|
|
21
21
|
BrandImage,
|
|
22
|
-
|
|
23
|
-
IconOrSvg
|
|
22
|
+
ClusterIconMenu,
|
|
23
|
+
IconOrSvg,
|
|
24
|
+
Pinned
|
|
24
25
|
},
|
|
25
26
|
|
|
26
27
|
data() {
|
|
@@ -34,7 +35,9 @@ export default {
|
|
|
34
35
|
clusterFilter: '',
|
|
35
36
|
hasProvCluster,
|
|
36
37
|
maxClustersToShow: MENU_MAX_CLUSTERS,
|
|
37
|
-
emptyCluster: BLANK_CLUSTER
|
|
38
|
+
emptyCluster: BLANK_CLUSTER,
|
|
39
|
+
showPinClusters: false,
|
|
40
|
+
searchActive: false,
|
|
38
41
|
};
|
|
39
42
|
},
|
|
40
43
|
|
|
@@ -56,6 +59,24 @@ export default {
|
|
|
56
59
|
},
|
|
57
60
|
},
|
|
58
61
|
|
|
62
|
+
globalBannerSettings() {
|
|
63
|
+
const settings = this.$store.getters['management/all'](MANAGEMENT.SETTING);
|
|
64
|
+
const bannerSettings = settings.find((s) => s.id === SETTING.BANNERS);
|
|
65
|
+
|
|
66
|
+
if (bannerSettings) {
|
|
67
|
+
const parsed = JSON.parse(bannerSettings.value);
|
|
68
|
+
const {
|
|
69
|
+
showFooter, showHeader, bannerFooter, bannerHeader
|
|
70
|
+
} = parsed;
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
headerFont: showHeader === 'true' ? this.pxToEm(bannerHeader.fontSize) : '0px',
|
|
74
|
+
footerFont: showFooter === 'true' ? this.pxToEm(bannerFooter.fontSize) : '0px'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return undefined;
|
|
79
|
+
},
|
|
59
80
|
legacyEnabled() {
|
|
60
81
|
return this.features(LEGACY);
|
|
61
82
|
},
|
|
@@ -95,7 +116,9 @@ export default {
|
|
|
95
116
|
badge: x.badge,
|
|
96
117
|
isLocal: x.isLocal,
|
|
97
118
|
isHarvester: x.isHarvester,
|
|
98
|
-
pinned:
|
|
119
|
+
pinned: x.pinned,
|
|
120
|
+
pin: () => x.pin(),
|
|
121
|
+
unpin: () => x.unpin()
|
|
99
122
|
};
|
|
100
123
|
});
|
|
101
124
|
},
|
|
@@ -107,13 +130,38 @@ export default {
|
|
|
107
130
|
|
|
108
131
|
const sorted = sortBy(out, ['ready:desc', 'label']);
|
|
109
132
|
|
|
133
|
+
if (search) {
|
|
134
|
+
this.showPinClusters = false;
|
|
135
|
+
this.searchActive = !sorted.length > 0;
|
|
136
|
+
|
|
137
|
+
return sorted;
|
|
138
|
+
}
|
|
139
|
+
this.showPinClusters = true;
|
|
140
|
+
this.searchActive = false;
|
|
141
|
+
|
|
110
142
|
if (sorted.length >= this.maxClustersToShow) {
|
|
111
|
-
|
|
143
|
+
const sortedPinOut = sorted.filter((item) => !item.pinned).slice(0, this.maxClustersToShow);
|
|
144
|
+
|
|
145
|
+
return sortedPinOut;
|
|
112
146
|
} else {
|
|
113
|
-
return sorted;
|
|
147
|
+
return sorted.filter((item) => !item.pinned);
|
|
114
148
|
}
|
|
115
149
|
},
|
|
116
150
|
|
|
151
|
+
pinFiltered() {
|
|
152
|
+
const out = this.clusters.filter((item) => item.pinned);
|
|
153
|
+
const sorted = sortBy(out, ['ready:desc', 'label']);
|
|
154
|
+
|
|
155
|
+
return sorted;
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
pinnedClustersHeight() {
|
|
159
|
+
const pinCount = this.clusters.filter((item) => item.pinned).length;
|
|
160
|
+
const height = pinCount > 2 ? (pinCount * 43) : 90;
|
|
161
|
+
|
|
162
|
+
return `min-height: ${ height }px`;
|
|
163
|
+
},
|
|
164
|
+
|
|
117
165
|
clusterFilterCount() {
|
|
118
166
|
return this.clusterFilter ? this.clustersFiltered.length : this.clusters.length;
|
|
119
167
|
},
|
|
@@ -208,6 +256,19 @@ export default {
|
|
|
208
256
|
},
|
|
209
257
|
|
|
210
258
|
methods: {
|
|
259
|
+
/**
|
|
260
|
+
* Converts a pixel value to an em value based on the default font size.
|
|
261
|
+
* @param {number} elementFontSize - The font size of the element in pixels.
|
|
262
|
+
* @param {number} [defaultFontSize=14] - The default font size in pixels.
|
|
263
|
+
* @returns {string} The converted value in em units.
|
|
264
|
+
*/
|
|
265
|
+
pxToEm(elementFontSize, defaultFontSize = 14) {
|
|
266
|
+
const lineHeightInPx = 2 * parseInt(elementFontSize);
|
|
267
|
+
const lineHeightInEm = lineHeightInPx / defaultFontSize;
|
|
268
|
+
|
|
269
|
+
return `${ lineHeightInEm }em`;
|
|
270
|
+
},
|
|
271
|
+
|
|
211
272
|
handler(e) {
|
|
212
273
|
if (e.keyCode === KEY.ESCAPE ) {
|
|
213
274
|
this.hide();
|
|
@@ -242,7 +303,7 @@ export default {
|
|
|
242
303
|
return {
|
|
243
304
|
content: this.shown ? null : item,
|
|
244
305
|
placement: 'right',
|
|
245
|
-
popperOptions: { modifiers: { preventOverflow: { enabled: false } } }
|
|
306
|
+
popperOptions: { modifiers: { preventOverflow: { enabled: false }, hide: { enabled: false } } }
|
|
246
307
|
};
|
|
247
308
|
} else {
|
|
248
309
|
return { content: undefined };
|
|
@@ -263,6 +324,10 @@ export default {
|
|
|
263
324
|
data-testid="side-menu"
|
|
264
325
|
class="side-menu"
|
|
265
326
|
:class="{'menu-open': shown, 'menu-close':!shown}"
|
|
327
|
+
:style="{'marginBottom':
|
|
328
|
+
globalBannerSettings?.footerFont,
|
|
329
|
+
'marginTop':
|
|
330
|
+
globalBannerSettings?.headerFont}"
|
|
266
331
|
tabindex="-1"
|
|
267
332
|
>
|
|
268
333
|
<div class="title">
|
|
@@ -306,7 +371,6 @@ export default {
|
|
|
306
371
|
{{ t('nav.home') }}
|
|
307
372
|
</div>
|
|
308
373
|
</nuxt-link>
|
|
309
|
-
|
|
310
374
|
<div
|
|
311
375
|
v-if="showClusterSearch"
|
|
312
376
|
class="clusters-search"
|
|
@@ -375,41 +439,101 @@ export default {
|
|
|
375
439
|
<div
|
|
376
440
|
ref="clusterList"
|
|
377
441
|
class="clusters"
|
|
442
|
+
:style="pinnedClustersHeight"
|
|
378
443
|
>
|
|
379
444
|
<div
|
|
380
|
-
v-
|
|
381
|
-
|
|
382
|
-
@click="hide()"
|
|
445
|
+
v-if="showPinClusters && pinFiltered.length"
|
|
446
|
+
class="clustersPinned"
|
|
383
447
|
>
|
|
384
|
-
<
|
|
385
|
-
v-
|
|
386
|
-
:
|
|
387
|
-
|
|
388
|
-
:to="{ name: 'c-cluster-explorer', params: { cluster: c.id } }"
|
|
448
|
+
<div
|
|
449
|
+
v-for="c in pinFiltered"
|
|
450
|
+
:key="c.id"
|
|
451
|
+
@click="hide()"
|
|
389
452
|
>
|
|
390
|
-
<
|
|
391
|
-
v-
|
|
392
|
-
:
|
|
393
|
-
class="
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
453
|
+
<nuxt-link
|
|
454
|
+
v-if="c.ready"
|
|
455
|
+
:data-testid="`menu-cluster-${ c.id }`"
|
|
456
|
+
class="cluster selector option"
|
|
457
|
+
:to="{ name: 'c-cluster-explorer', params: { cluster: c.id } }"
|
|
458
|
+
>
|
|
459
|
+
<ClusterIconMenu
|
|
460
|
+
v-tooltip="getTooltipConfig(c.label)"
|
|
461
|
+
:cluster="c"
|
|
462
|
+
class="rancher-provider-icon"
|
|
463
|
+
/>
|
|
464
|
+
<div class="cluster-name">
|
|
465
|
+
{{ c.label }}
|
|
466
|
+
</div>
|
|
467
|
+
<Pinned
|
|
468
|
+
:cluster="c"
|
|
469
|
+
/>
|
|
470
|
+
</nuxt-link>
|
|
471
|
+
<span
|
|
472
|
+
v-else
|
|
473
|
+
class="option cluster selector disabled"
|
|
474
|
+
>
|
|
475
|
+
<ClusterIconMenu
|
|
476
|
+
v-tooltip="getTooltipConfig(c.label)"
|
|
477
|
+
:cluster="c"
|
|
478
|
+
class="rancher-provider-icon"
|
|
479
|
+
/>
|
|
480
|
+
<div class="cluster-name">{{ c.label }}</div>
|
|
481
|
+
<Pinned
|
|
482
|
+
:cluster="c"
|
|
483
|
+
/>
|
|
484
|
+
</span>
|
|
485
|
+
</div>
|
|
486
|
+
<div
|
|
487
|
+
v-if="clustersFiltered.length > 0"
|
|
488
|
+
class="category-title"
|
|
402
489
|
>
|
|
403
|
-
<
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
490
|
+
<hr>
|
|
491
|
+
</div>
|
|
492
|
+
</div>
|
|
493
|
+
<div class="clustersList">
|
|
494
|
+
<div
|
|
495
|
+
v-for="c in clustersFiltered"
|
|
496
|
+
:key="c.id"
|
|
497
|
+
@click="hide()"
|
|
498
|
+
>
|
|
499
|
+
<nuxt-link
|
|
500
|
+
v-if="c.ready"
|
|
501
|
+
:data-testid="`menu-cluster-${ c.id }`"
|
|
502
|
+
class="cluster selector option"
|
|
503
|
+
:to="{ name: 'c-cluster-explorer', params: { cluster: c.id } }"
|
|
504
|
+
>
|
|
505
|
+
<ClusterIconMenu
|
|
506
|
+
v-tooltip="getTooltipConfig(c.label)"
|
|
507
|
+
:cluster="c"
|
|
508
|
+
class="rancher-provider-icon"
|
|
509
|
+
/>
|
|
510
|
+
<div class="cluster-name">
|
|
511
|
+
{{ c.label }}
|
|
512
|
+
</div>
|
|
513
|
+
<Pinned
|
|
514
|
+
:class="{'showPin': c.pinned}"
|
|
515
|
+
:cluster="c"
|
|
516
|
+
/>
|
|
517
|
+
</nuxt-link>
|
|
518
|
+
<span
|
|
519
|
+
v-else
|
|
520
|
+
class="option cluster selector disabled"
|
|
521
|
+
>
|
|
522
|
+
<ClusterIconMenu
|
|
523
|
+
v-tooltip="getTooltipConfig(c.label)"
|
|
524
|
+
:cluster="c"
|
|
525
|
+
class="rancher-provider-icon"
|
|
526
|
+
/>
|
|
527
|
+
<div class="cluster-name">{{ c.label }}</div>
|
|
528
|
+
<Pinned
|
|
529
|
+
:class="{'showPin': c.pinned}"
|
|
530
|
+
:cluster="c"
|
|
531
|
+
/>
|
|
532
|
+
</span>
|
|
533
|
+
</div>
|
|
410
534
|
</div>
|
|
411
535
|
<div
|
|
412
|
-
v-if="clustersFiltered.length === 0 &&
|
|
536
|
+
v-if="(clustersFiltered.length === 0 || pinFiltered.length === 0) && searchActive"
|
|
413
537
|
class="none-matching"
|
|
414
538
|
>
|
|
415
539
|
{{ t('nav.search.noResults') }}
|
|
@@ -573,25 +697,25 @@ export default {
|
|
|
573
697
|
$option-padding-left: 14px;
|
|
574
698
|
$option-height: $icon-size + $option-padding + $option-padding;
|
|
575
699
|
|
|
576
|
-
.menu {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
700
|
+
.side-menu {
|
|
701
|
+
.menu {
|
|
702
|
+
position: absolute;
|
|
703
|
+
width: $app-bar-collapsed-width;
|
|
704
|
+
height: 54px;
|
|
705
|
+
top: 0;
|
|
706
|
+
grid-area: menu;
|
|
707
|
+
cursor: pointer;
|
|
708
|
+
display: flex;
|
|
709
|
+
align-items: center;
|
|
710
|
+
justify-content: center;
|
|
586
711
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
712
|
+
.menu-icon {
|
|
713
|
+
width: 25px;
|
|
714
|
+
height: 25px;
|
|
715
|
+
fill: var(--header-btn-text);
|
|
716
|
+
}
|
|
591
717
|
}
|
|
592
|
-
}
|
|
593
718
|
|
|
594
|
-
.side-menu {
|
|
595
719
|
position: fixed;
|
|
596
720
|
top: 0;
|
|
597
721
|
left: 0px;
|
|
@@ -636,7 +760,7 @@ export default {
|
|
|
636
760
|
svg {
|
|
637
761
|
width: 25px;
|
|
638
762
|
height: 25px;
|
|
639
|
-
margin-left:
|
|
763
|
+
margin-left: 9px;
|
|
640
764
|
}
|
|
641
765
|
}
|
|
642
766
|
.home-text {
|
|
@@ -664,8 +788,23 @@ export default {
|
|
|
664
788
|
font-weight: 500;
|
|
665
789
|
}
|
|
666
790
|
|
|
791
|
+
.pin {
|
|
792
|
+
font-size: 16px;
|
|
793
|
+
margin-left: auto;
|
|
794
|
+
display: none;
|
|
795
|
+
color: var(--body-text);
|
|
796
|
+
&.showPin {
|
|
797
|
+
display: block;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
667
801
|
&:hover {
|
|
668
802
|
text-decoration: none;
|
|
803
|
+
|
|
804
|
+
.pin {
|
|
805
|
+
display: block;
|
|
806
|
+
color: var(--darker-text);
|
|
807
|
+
}
|
|
669
808
|
}
|
|
670
809
|
&.disabled {
|
|
671
810
|
background: transparent;
|
|
@@ -676,6 +815,10 @@ export default {
|
|
|
676
815
|
filter: grayscale(1);
|
|
677
816
|
color: var(--muted);
|
|
678
817
|
}
|
|
818
|
+
|
|
819
|
+
.pin {
|
|
820
|
+
cursor: pointer;
|
|
821
|
+
}
|
|
679
822
|
}
|
|
680
823
|
|
|
681
824
|
&:focus {
|
|
@@ -729,6 +872,11 @@ export default {
|
|
|
729
872
|
&.disabled {
|
|
730
873
|
background: transparent;
|
|
731
874
|
color: var(--muted);
|
|
875
|
+
|
|
876
|
+
> .pin {
|
|
877
|
+
color:var(--default-text);
|
|
878
|
+
display: block;
|
|
879
|
+
}
|
|
732
880
|
}
|
|
733
881
|
}
|
|
734
882
|
|
|
@@ -749,7 +897,7 @@ export default {
|
|
|
749
897
|
> input {
|
|
750
898
|
background-color: transparent;
|
|
751
899
|
margin-bottom: 8px;
|
|
752
|
-
padding-right:
|
|
900
|
+
padding-right: 35px;
|
|
753
901
|
}
|
|
754
902
|
> i {
|
|
755
903
|
position: absolute;
|
|
@@ -779,10 +927,6 @@ export default {
|
|
|
779
927
|
.clusters {
|
|
780
928
|
overflow-y: auto;
|
|
781
929
|
|
|
782
|
-
@media screen and (max-height: 720px) {
|
|
783
|
-
min-height: 80px;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
930
|
a, span {
|
|
787
931
|
margin: 0;
|
|
788
932
|
}
|
|
@@ -846,6 +990,24 @@ export default {
|
|
|
846
990
|
padding: 8px
|
|
847
991
|
}
|
|
848
992
|
|
|
993
|
+
.clustersPinned {
|
|
994
|
+
.category {
|
|
995
|
+
&-title {
|
|
996
|
+
margin: 8px 0;
|
|
997
|
+
margin-left: 16px;
|
|
998
|
+
hr {
|
|
999
|
+
margin: 0;
|
|
1000
|
+
width: 94%;
|
|
1001
|
+
transition: all 0.5s ease-in-out;
|
|
1002
|
+
max-width: 100%;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
.pin {
|
|
1007
|
+
display: block;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
849
1011
|
.category {
|
|
850
1012
|
display: flex;
|
|
851
1013
|
flex-direction: column;
|
|
@@ -907,6 +1069,17 @@ export default {
|
|
|
907
1069
|
}
|
|
908
1070
|
}
|
|
909
1071
|
}
|
|
1072
|
+
|
|
1073
|
+
.clustersPinned {
|
|
1074
|
+
.category {
|
|
1075
|
+
&-title {
|
|
1076
|
+
hr {
|
|
1077
|
+
width: 40px;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
910
1083
|
.footer {
|
|
911
1084
|
margin: 20px 15px;
|
|
912
1085
|
|
package/components/nav/Type.vue
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import Favorite from '@shell/components/nav/Favorite';
|
|
3
3
|
import { FAVORITE, USED } from '@shell/store/type-map';
|
|
4
|
+
import { linkActiveClass } from '@shell/config/router';
|
|
4
5
|
|
|
5
6
|
const showFavoritesFor = [FAVORITE, USED];
|
|
6
7
|
|
|
@@ -27,12 +28,65 @@ export default {
|
|
|
27
28
|
|
|
28
29
|
data() {
|
|
29
30
|
return {
|
|
30
|
-
near:
|
|
31
|
-
over:
|
|
31
|
+
near: false,
|
|
32
|
+
over: false,
|
|
33
|
+
menuPath: this.type.route ? this.$router.resolve(this.type.route)?.route?.path : undefined,
|
|
34
|
+
linkActiveClass
|
|
32
35
|
};
|
|
33
36
|
},
|
|
34
37
|
|
|
35
38
|
computed: {
|
|
39
|
+
isCurrent() {
|
|
40
|
+
// This is required to avoid scenarios where fragments break vue routers location matching
|
|
41
|
+
// For example, the following fails
|
|
42
|
+
// Curruent Path /c/c-m-hzqf4tqt/explorer/members#project-membership
|
|
43
|
+
// Menu Path /c/c-m-hzqf4tqt/explorer/members
|
|
44
|
+
// vue-router exact-path="true" fixes this (https://v3.router.vuejs.org/api/#exact-path),
|
|
45
|
+
// but fails when the the current path is a child (for instance a resource detail page)
|
|
46
|
+
|
|
47
|
+
// Scenarios to consider
|
|
48
|
+
// - Fragement world
|
|
49
|
+
// Curruent Path /c/c-m-hzqf4tqt/explorer/members#project-membership
|
|
50
|
+
// Menu Path /c/c-m-hzqf4tqt/explorer/members
|
|
51
|
+
// - Similar current paths
|
|
52
|
+
// /c/c-m-hzqf4tqt/fleet/fleet.cattle.io.bundlenamespacemapping
|
|
53
|
+
// /c/c-m-hzqf4tqt/fleet/fleet.cattle.io.bundle
|
|
54
|
+
// - Other menu items that appear in current menu item
|
|
55
|
+
// /c/c-m-hzqf4tqt/fleet
|
|
56
|
+
// /c/c-m-hzqf4tqt/fleet/management.cattle.io.fleetworkspace
|
|
57
|
+
|
|
58
|
+
// If there's no hash the n-link will determine it's linkActiveClass correctly, so avoid this faff
|
|
59
|
+
const invalidHash = !this.$route.hash;
|
|
60
|
+
// Lets be super safe
|
|
61
|
+
const invalidProps = !this.menuPath || !this.$route.path;
|
|
62
|
+
|
|
63
|
+
if (invalidHash || invalidProps) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// We're kind of, but in a fixing way, copying n-link --> vue-router link see vue-router/src/components/link.js & vue-router/src/util/route.js
|
|
68
|
+
// We're only going to compare the path and ignore query and fragment
|
|
69
|
+
|
|
70
|
+
if (this.type.exact) {
|
|
71
|
+
return this.$route.path === this.menuPath;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const currentPath = this.$route.path.split('/');
|
|
75
|
+
const menuPath = this.menuPath.split('/');
|
|
76
|
+
|
|
77
|
+
if (menuPath.length > currentPath.length) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < menuPath.length; i++) {
|
|
82
|
+
if (menuPath[i] !== currentPath[i]) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return true;
|
|
88
|
+
},
|
|
89
|
+
|
|
36
90
|
showFavorite() {
|
|
37
91
|
return ( this.type.mode && this.near && showFavoritesFor.includes(this.type.mode) );
|
|
38
92
|
},
|
|
@@ -80,7 +134,7 @@ export default {
|
|
|
80
134
|
:to="type.route"
|
|
81
135
|
tag="li"
|
|
82
136
|
class="child nav-type"
|
|
83
|
-
:class="{'root': isRoot, [`depth-${depth}`]: true}"
|
|
137
|
+
:class="{'root': isRoot, [`depth-${depth}`]: true, [linkActiveClass]: isCurrent}"
|
|
84
138
|
:exact="type.exact"
|
|
85
139
|
>
|
|
86
140
|
<a
|
package/config/home-links.js
CHANGED
package/config/product/istio.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AGE, NAME as NAME_HEADER, STATE } from '@shell/config/table-headers';
|
|
1
|
+
import { AGE, NAME as NAME_HEADER, NAMESPACE as NAMESPACE_HEADER, STATE } from '@shell/config/table-headers';
|
|
2
2
|
import { ISTIO } from '@shell/config/types';
|
|
3
3
|
import { DSL, IF_HAVE } from '@shell/store/type-map';
|
|
4
4
|
|
|
@@ -14,9 +14,10 @@ export function init(store) {
|
|
|
14
14
|
} = DSL(store, NAME);
|
|
15
15
|
|
|
16
16
|
product({
|
|
17
|
-
ifHaveGroup:
|
|
18
|
-
ifHave:
|
|
19
|
-
icon:
|
|
17
|
+
ifHaveGroup: /^(.*\.)*istio\.io$/,
|
|
18
|
+
ifHave: IF_HAVE.NOT_V1_ISTIO,
|
|
19
|
+
icon: 'istio',
|
|
20
|
+
showNamespaceFilter: true,
|
|
20
21
|
});
|
|
21
22
|
|
|
22
23
|
virtualType({
|
|
@@ -42,7 +43,9 @@ export function init(store) {
|
|
|
42
43
|
'networking.istio.io.envoyfilter',
|
|
43
44
|
'networking.istio.io.serviceentry',
|
|
44
45
|
'networking.istio.io.sidecar',
|
|
45
|
-
'networking.istio.io.
|
|
46
|
+
'networking.istio.io.proxyconfig',
|
|
47
|
+
'networking.istio.io.workloadentry',
|
|
48
|
+
'networking.istio.io.workloadgroup',
|
|
46
49
|
], 'Networking');
|
|
47
50
|
|
|
48
51
|
basicType([
|
|
@@ -58,9 +61,16 @@ export function init(store) {
|
|
|
58
61
|
'security.istio.io.requestauthentication',
|
|
59
62
|
], 'Security');
|
|
60
63
|
|
|
64
|
+
basicType([
|
|
65
|
+
'install.istio.io.istiooperator',
|
|
66
|
+
'telemetry.istio.io.telemetry',
|
|
67
|
+
'extensions.istio.io.wasmplugin',
|
|
68
|
+
], 'Advanced');
|
|
69
|
+
|
|
61
70
|
headers(ISTIO.VIRTUAL_SERVICE, [
|
|
62
71
|
STATE,
|
|
63
72
|
NAME_HEADER,
|
|
73
|
+
NAMESPACE_HEADER,
|
|
64
74
|
{
|
|
65
75
|
name: 'gateways',
|
|
66
76
|
label: 'Gateways',
|
package/config/router.js
CHANGED
|
@@ -6,13 +6,15 @@ import scrollBehavior from '../utils/router.scrollBehavior.js';
|
|
|
6
6
|
|
|
7
7
|
const emptyFn = () => {};
|
|
8
8
|
|
|
9
|
+
export const linkActiveClass = 'nuxt-link-active';
|
|
10
|
+
|
|
9
11
|
Vue.use(Router);
|
|
10
12
|
|
|
11
13
|
export const routerOptions = {
|
|
12
14
|
mode: 'history',
|
|
13
15
|
// Note: router base comes from the ROUTER_BASE env var
|
|
14
16
|
base: process.env.routerBase || '/',
|
|
15
|
-
linkActiveClass
|
|
17
|
+
linkActiveClass,
|
|
16
18
|
linkExactActiveClass: 'nuxt-link-exact-active',
|
|
17
19
|
scrollBehavior,
|
|
18
20
|
|
|
@@ -72,10 +74,6 @@ export const routerOptions = {
|
|
|
72
74
|
path: '/auth/verify',
|
|
73
75
|
component: () => interopDefault(import('../pages/auth/verify.vue' /* webpackChunkName: "pages/auth/verify" */)),
|
|
74
76
|
name: 'auth-verify'
|
|
75
|
-
}, {
|
|
76
|
-
path: '/docs/toc',
|
|
77
|
-
component: () => interopDefault(import('../pages/docs/toc.js' /* webpackChunkName: "pages/docs/toc" */)),
|
|
78
|
-
name: 'docs-toc'
|
|
79
77
|
}, {
|
|
80
78
|
path: '/rio/mesh',
|
|
81
79
|
component: () => interopDefault(import('../pages/rio/mesh.vue' /* webpackChunkName: "pages/rio/mesh" */)),
|
|
@@ -84,10 +82,6 @@ export const routerOptions = {
|
|
|
84
82
|
path: '/c/:cluster',
|
|
85
83
|
component: () => interopDefault(import('../pages/c/_cluster/index.vue' /* webpackChunkName: "pages/c/_cluster/index" */)),
|
|
86
84
|
name: 'c-cluster'
|
|
87
|
-
}, {
|
|
88
|
-
path: '/docs/:doc?',
|
|
89
|
-
component: () => interopDefault(import('../pages/docs/_doc.vue' /* webpackChunkName: "pages/docs/_doc" */)),
|
|
90
|
-
name: 'docs-doc'
|
|
91
85
|
}, {
|
|
92
86
|
path: '/c/:cluster/apps',
|
|
93
87
|
component: () => interopDefault(import('../pages/c/_cluster/apps/index.vue' /* webpackChunkName: "pages/c/_cluster/apps/index" */)),
|
package/config/table-headers.js
CHANGED
|
@@ -993,13 +993,12 @@ export const UI_PLUGIN_CATALOG = [
|
|
|
993
993
|
name: 'image',
|
|
994
994
|
sort: ['image'],
|
|
995
995
|
labelKey: 'plugins.manageCatalog.headers.image.label',
|
|
996
|
-
value: '
|
|
996
|
+
value: 'image'
|
|
997
997
|
},
|
|
998
998
|
{
|
|
999
|
-
name:
|
|
1000
|
-
sort:
|
|
1001
|
-
labelKey:
|
|
1002
|
-
value:
|
|
1003
|
-
formatter: 'ExtensionCache'
|
|
999
|
+
name: 'repository',
|
|
1000
|
+
sort: ['repository'],
|
|
1001
|
+
labelKey: 'plugins.manageCatalog.headers.repository.label',
|
|
1002
|
+
value: 'repo.metadata.name'
|
|
1004
1003
|
}
|
|
1005
1004
|
];
|
package/config/uiplugins.js
CHANGED
|
@@ -49,6 +49,7 @@ export const UI_PLUGIN_CHART_ANNOTATIONS = {
|
|
|
49
49
|
UI_VERSION: 'catalog.cattle.io/ui-version',
|
|
50
50
|
EXTENSIONS_HOST: 'catalog.cattle.io/ui-extensions-host',
|
|
51
51
|
DISPLAY_NAME: 'catalog.cattle.io/display-name',
|
|
52
|
+
HIDDEN_BUILTIN: 'catalog.cattle.io/ui-hidden-builtin',
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
// Extension catalog labels
|
package/core/plugin-helpers.js
CHANGED
|
@@ -85,6 +85,9 @@ function checkExtensionRouteBinding($route, locationConfig, context) {
|
|
|
85
85
|
// also handle "mode" in a separate way because it mainly depends on query params
|
|
86
86
|
} else if (param === 'mode') {
|
|
87
87
|
res = checkRouteMode($route, locationConfigParam);
|
|
88
|
+
} else if (param === 'resource') {
|
|
89
|
+
// Match exact resource but also allow resource of '*' to match any resource
|
|
90
|
+
res = (params[param] && locationConfigParam === '*') || locationConfigParam === params[param];
|
|
88
91
|
} else if (param === 'context') {
|
|
89
92
|
// Need all keys and values to match
|
|
90
93
|
res = isEqual(locationConfigParam, context);
|
package/core/types.ts
CHANGED
|
@@ -102,7 +102,7 @@ export type Action = {
|
|
|
102
102
|
icon?: string;
|
|
103
103
|
multiple?: boolean;
|
|
104
104
|
enabled?: Function | boolean;
|
|
105
|
-
invoke: (opts: ActionOpts, resources: any[]) => void | boolean | Promise<boolean>;
|
|
105
|
+
invoke: (opts: ActionOpts, resources: any[], globals?: any) => void | boolean | Promise<boolean>;
|
|
106
106
|
};
|
|
107
107
|
|
|
108
108
|
/** Definition of a panel (options that can be passed when defining an extension panel enhancement) */
|
|
@@ -567,3 +567,8 @@ export interface IPlugin {
|
|
|
567
567
|
*/
|
|
568
568
|
DSL(store: any, productName: string): DSLReturnType;
|
|
569
569
|
}
|
|
570
|
+
|
|
571
|
+
// Internal interface
|
|
572
|
+
// Built-in extensions may use this, but external extensions should not, as this is subject to change
|
|
573
|
+
// Defined as any for now
|
|
574
|
+
export type IInternal = any;
|